diff options
Diffstat (limited to 'servers/rendering')
-rw-r--r-- | servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp | 45 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/renderer_canvas_render_rd.h | 3 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/renderer_storage_rd.cpp | 85 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/renderer_storage_rd.h | 29 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/shaders/canvas.glsl | 26 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/shaders/canvas_sdf.glsl | 60 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/shaders/particles.glsl | 238 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/shaders/particles_copy.glsl | 47 | ||||
-rw-r--r-- | servers/rendering/renderer_storage.h | 3 | ||||
-rw-r--r-- | servers/rendering/renderer_viewport.cpp | 3 | ||||
-rw-r--r-- | servers/rendering/shader_language.cpp | 150 | ||||
-rw-r--r-- | servers/rendering/shader_language.h | 52 | ||||
-rw-r--r-- | servers/rendering/shader_warnings.cpp | 131 | ||||
-rw-r--r-- | servers/rendering/shader_warnings.h | 83 |
14 files changed, 817 insertions, 138 deletions
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index f448698976..1f27dd54ec 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -33,6 +33,7 @@ #include "core/math/geometry_2d.h" #include "core/math/math_funcs.h" #include "renderer_compositor_rd.h" +#include "servers/rendering/rendering_server_default.h" void RendererCanvasRenderRD::_update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4) { p_mat4[0] = p_transform.elements[0][0]; @@ -390,7 +391,7 @@ void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RI r_last_texture = p_texture; } -void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants) { +void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants) { //create an empty push constant RS::CanvasItemTextureFilter current_filter = default_filter; @@ -747,9 +748,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, const Item } else if (c->type == Item::Command::TYPE_PARTICLES) { const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(c); ERR_BREAK(storage->particles_get_mode(pt->particles) != RS::PARTICLES_MODE_2D); + storage->particles_request_process(pt->particles); + if (storage->particles_is_inactive(pt->particles)) { break; } + + RenderingServerDefault::redraw_request(); // active particles means redraw request + + bool local_coords = true; int dpc = storage->particles_get_draw_passes(pt->particles); if (dpc == 0) { break; //nothing to draw @@ -768,6 +775,30 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, const Item mesh = storage->particles_get_draw_pass_mesh(pt->particles, 0); //higher ones are ignored texture = pt->texture; + + if (storage->particles_has_collision(pt->particles) && storage->render_target_is_sdf_enabled(p_render_target)) { + //pass collision information + Transform2D xform; + if (local_coords) { + xform = p_item->final_transform; + } else { + xform = p_canvas_transform_inverse; + } + + RID sdf_texture = storage->render_target_get_sdf_texture(p_render_target); + + Rect2 to_screen; + { + Rect2 sdf_rect = storage->render_target_get_sdf_rect(p_render_target); + + to_screen.size = Vector2(1.0 / sdf_rect.size.width, 1.0 / sdf_rect.size.height); + to_screen.position = -sdf_rect.position * to_screen.size; + } + + storage->particles_set_canvas_sdf_collision(pt->particles, true, xform, to_screen, sdf_texture); + } else { + storage->particles_set_canvas_sdf_collision(pt->particles, false, Transform2D(), Rect2(), RID()); + } } if (mesh.is_null()) { @@ -1052,7 +1083,7 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co } } - _render_item(draw_list, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants); + _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants); prev_material = material; } @@ -1280,6 +1311,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p Item *canvas_group_owner = nullptr; bool update_skeletons = false; + bool time_used = false; while (ci) { if (ci->copy_back_buffer && canvas_group_owner == nullptr) { @@ -1305,6 +1337,9 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p if (md->shader_data->uses_sdf) { r_sdf_used = true; } + if (md->shader_data->uses_time) { + time_used = true; + } if (md->last_frame != RendererCompositorRD::singleton->get_frame_number()) { md->last_frame = RendererCompositorRD::singleton->get_frame_number(); if (!RD::get_singleton()->uniform_set_is_valid(md->uniform_set)) { @@ -1401,6 +1436,10 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p ci = ci->next; } + + if (time_used) { + RenderingServerDefault::redraw_request(); + } } RID RendererCanvasRenderRD::light_create() { @@ -1877,6 +1916,7 @@ void RendererCanvasRenderRD::ShaderData::set_code(const String &p_code) { uniforms.clear(); uses_screen_texture = false; uses_sdf = false; + uses_time = false; if (code == String()) { return; //just invalid, but no error @@ -1901,6 +1941,7 @@ void RendererCanvasRenderRD::ShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["SCREEN_TEXTURE"] = &uses_screen_texture; actions.usage_flag_pointers["texture_sdf"] = &uses_sdf; + actions.usage_flag_pointers["TIME"] = &uses_time; actions.uniforms = &uniforms; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 8129cc6c9b..890a4e3649 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -176,6 +176,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { bool uses_screen_texture = false; bool uses_sdf = false; + bool uses_time = false; virtual void set_code(const String &p_Code); virtual void set_default_texture_param(const StringName &p_name, RID p_texture); @@ -425,7 +426,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { RID _create_base_uniform_set(RID p_to_render_target, bool p_backbuffer); inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size); //recursive, so regular inline used instead. - void _render_item(RenderingDevice::DrawListID p_draw_list, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants); + void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants); void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer = false); _FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4); diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index f419875d58..26ddd74a80 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -4305,6 +4305,15 @@ void RendererStorageRD::particles_remove_collision(RID p_particles, RID p_partic particles->collisions.erase(p_particles_collision_instance); } +void RendererStorageRD::particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) { + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + particles->has_sdf_collision = p_enable; + particles->sdf_collision_transform = p_xform; + particles->sdf_collision_to_screen = p_to_screen; + particles->sdf_collision_texture = p_texture; +} + void RendererStorageRD::_particles_process(Particles *p_particles, float p_delta) { if (p_particles->particles_material_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(p_particles->particles_material_uniform_set)) { Vector<RD::Uniform> uniforms; @@ -4410,6 +4419,50 @@ void RendererStorageRD::_particles_process(Particles *p_particles, float p_delta if (p_particles->use_local_coords) { to_particles = p_particles->emission_transform.affine_inverse(); } + + if (p_particles->has_sdf_collision && RD::get_singleton()->texture_is_valid(p_particles->sdf_collision_texture)) { + //2D collision + + Transform2D xform = p_particles->sdf_collision_transform; //will use dotproduct manually so invert beforehand + Transform2D revert = xform.affine_inverse(); + frame_params.collider_count = 1; + frame_params.colliders[0].transform[0] = xform.elements[0][0]; + frame_params.colliders[0].transform[1] = xform.elements[0][1]; + frame_params.colliders[0].transform[2] = 0; + frame_params.colliders[0].transform[3] = xform.elements[2][0]; + + frame_params.colliders[0].transform[4] = xform.elements[1][0]; + frame_params.colliders[0].transform[5] = xform.elements[1][1]; + frame_params.colliders[0].transform[6] = 0; + frame_params.colliders[0].transform[7] = xform.elements[2][1]; + + frame_params.colliders[0].transform[8] = revert.elements[0][0]; + frame_params.colliders[0].transform[9] = revert.elements[0][1]; + frame_params.colliders[0].transform[10] = 0; + frame_params.colliders[0].transform[11] = revert.elements[2][0]; + + frame_params.colliders[0].transform[12] = revert.elements[1][0]; + frame_params.colliders[0].transform[13] = revert.elements[1][1]; + frame_params.colliders[0].transform[14] = 0; + frame_params.colliders[0].transform[15] = revert.elements[2][1]; + + frame_params.colliders[0].extents[0] = p_particles->sdf_collision_to_screen.size.x; + frame_params.colliders[0].extents[1] = p_particles->sdf_collision_to_screen.size.y; + frame_params.colliders[0].extents[2] = p_particles->sdf_collision_to_screen.position.x; + frame_params.colliders[0].scale = p_particles->sdf_collision_to_screen.position.y; + frame_params.colliders[0].texture_index = 0; + frame_params.colliders[0].type = ParticlesFrameParams::COLLISION_TYPE_2D_SDF; + + collision_heightmap_texture = p_particles->sdf_collision_texture; + + //replace in all other history frames where used because parameters are no longer valid if screen moves + for (uint32_t i = 1; i < p_particles->frame_history.size(); i++) { + if (p_particles->frame_history[i].collider_count > 0 && p_particles->frame_history[i].colliders[0].type == ParticlesFrameParams::COLLISION_TYPE_2D_SDF) { + p_particles->frame_history[i].colliders[0] = frame_params.colliders[0]; + } + } + } + uint32_t collision_3d_textures_used = 0; for (const Set<RID>::Element *E = p_particles->collisions.front(); E; E = E->next()) { ParticlesCollisionInstance *pci = particles_collision_instance_owner.getornull(E->get()); @@ -4657,6 +4710,8 @@ void RendererStorageRD::_particles_process(Particles *p_particles, float p_delta ERR_FAIL_COND(!m); + p_particles->has_collision_cache = m->shader_data->uses_collision; + //todo should maybe compute all particle systems together? RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, m->shader_data->pipeline); @@ -4740,6 +4795,11 @@ void RendererStorageRD::particles_set_view_axis(RID p_particles, const Vector3 & copy_push_constant.trail_total = 1; copy_push_constant.frame_delta = 0.0; } + + copy_push_constant.order_by_lifetime = (particles->draw_order == RS::PARTICLES_DRAW_ORDER_LIFETIME || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME); + copy_push_constant.lifetime_split = MIN(particles->amount * particles->phase, particles->amount - 1); + copy_push_constant.lifetime_reverse = particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME; + copy_push_constant.frame_remainder = particles->interpolate ? particles->frame_remainder : 0.0; copy_push_constant.total_particles = particles->amount; @@ -5019,6 +5079,10 @@ void RendererStorageRD::update_particles() { copy_push_constant.frame_delta = 0.0; } + copy_push_constant.order_by_lifetime = (particles->draw_order == RS::PARTICLES_DRAW_ORDER_LIFETIME || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME); + copy_push_constant.lifetime_split = MIN(particles->amount * particles->phase, particles->amount - 1); + copy_push_constant.lifetime_reverse = particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME; + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[particles->mode == RS::PARTICLES_MODE_2D ? ParticlesShader::COPY_MODE_FILL_INSTANCES_2D : ParticlesShader::COPY_MODE_FILL_INSTANCES]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0); @@ -5049,6 +5113,7 @@ void RendererStorageRD::ParticlesShaderData::set_code(const String &p_code) { valid = false; ubo_size = 0; uniforms.clear(); + uses_collision = false; if (code == String()) { return; //just invalid, but no error @@ -5068,6 +5133,8 @@ void RendererStorageRD::ParticlesShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["TIME"] = &uses_time; */ + actions.usage_flag_pointers["COLLIDED"] = &uses_collision; + actions.uniforms = &uniforms; Error err = base_singleton->particles_shader.compiler.compile(RS::SHADER_PARTICLES, code, &actions, path, gen_code); @@ -7133,6 +7200,20 @@ Rect2i RendererStorageRD::render_target_get_sdf_rect(RID p_render_target) const return _render_target_get_sdf_rect(rt); } +void RendererStorageRD::render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + + rt->sdf_enabled = p_enabled; +} + +bool RendererStorageRD::render_target_is_sdf_enabled(RID p_render_target) const { + const RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND_V(!rt, false); + + return rt->sdf_enabled; +} + RID RendererStorageRD::render_target_get_sdf_texture(RID p_render_target) { RenderTarget *rt = render_target_owner.getornull(p_render_target); ERR_FAIL_COND_V(!rt, RID()); @@ -7200,7 +7281,7 @@ void RendererStorageRD::_render_target_allocate_sdf(RenderTarget *rt) { rt->process_size.x = MAX(rt->process_size.x, 1); rt->process_size.y = MAX(rt->process_size.y, 1); - tformat.format = RD::DATA_FORMAT_R16G16_UINT; + tformat.format = RD::DATA_FORMAT_R16G16_SINT; tformat.width = rt->process_size.width; tformat.height = rt->process_size.height; tformat.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; @@ -7208,7 +7289,7 @@ void RendererStorageRD::_render_target_allocate_sdf(RenderTarget *rt) { rt->sdf_buffer_process[0] = RD::get_singleton()->texture_create(tformat, RD::TextureView()); rt->sdf_buffer_process[1] = RD::get_singleton()->texture_create(tformat, RD::TextureView()); - tformat.format = RD::DATA_FORMAT_R16_UNORM; + tformat.format = RD::DATA_FORMAT_R16_SNORM; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; rt->sdf_buffer_read = RD::get_singleton()->texture_create(tformat, RD::TextureView()); diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.h b/servers/rendering/renderer_rd/renderer_storage_rd.h index 49f7f3dba6..67fbeb3008 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.h +++ b/servers/rendering/renderer_rd/renderer_storage_rd.h @@ -638,7 +638,9 @@ private: COLLISION_TYPE_SPHERE, COLLISION_TYPE_BOX, COLLISION_TYPE_SDF, - COLLISION_TYPE_HEIGHT_FIELD + COLLISION_TYPE_HEIGHT_FIELD, + COLLISION_TYPE_2D_SDF, + }; struct Collider { @@ -710,6 +712,13 @@ private: bool restart_request = false; AABB custom_aabb = AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8)); bool use_local_coords = true; + bool has_collision_cache = false; + + bool has_sdf_collision = false; + Transform2D sdf_collision_transform; + Rect2 sdf_collision_to_screen; + RID sdf_collision_texture; + RID process_material; uint32_t frame_counter = 0; RS::ParticlesTransformAlign transform_align = RS::PARTICLES_TRANSFORM_ALIGN_DISABLED; @@ -820,6 +829,11 @@ private: float align_up[3]; uint32_t align_mode; + + uint32_t order_by_lifetime; + uint32_t lifetime_split; + uint32_t lifetime_reverse; + uint32_t pad; }; enum { @@ -843,6 +857,7 @@ private: struct ParticlesShaderData : public ShaderData { bool valid; RID version; + bool uses_collision = false; //PipelineCacheRD pipelines[SKY_VERSION_MAX]; Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; @@ -1120,6 +1135,8 @@ private: bool flags[RENDER_TARGET_FLAG_MAX]; + bool sdf_enabled = false; + RID backbuffer; //used for effects RID backbuffer_fb; RID backbuffer_mipmap0; @@ -2175,6 +2192,13 @@ public: return particles->amount * r_trail_divisor; } + _FORCE_INLINE_ bool particles_has_collision(RID p_particles) { + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND_V(!particles, 0); + + return particles->has_collision_cache; + } + _FORCE_INLINE_ uint32_t particles_is_using_local_coords(RID p_particles) { Particles *particles = particles_owner.getornull(p_particles); ERR_FAIL_COND_V(!particles, false); @@ -2206,6 +2230,7 @@ public: virtual void particles_add_collision(RID p_particles, RID p_particles_collision_instance); virtual void particles_remove_collision(RID p_particles, RID p_particles_collision_instance); + virtual void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture); /* PARTICLES COLLISION */ @@ -2280,6 +2305,8 @@ public: RID render_target_get_sdf_framebuffer(RID p_render_target); void render_target_sdf_process(RID p_render_target); virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const; + void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled); + bool render_target_is_sdf_enabled(RID p_render_target) const; Size2 render_target_get_size(RID p_render_target); RID render_target_get_rd_framebuffer(RID p_render_target); diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl index cf4c77db0d..2186bd174b 100644 --- a/servers/rendering/renderer_rd/shaders/canvas.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas.glsl @@ -101,36 +101,34 @@ void main() { uint offset = trail_size * stride * gl_InstanceIndex; - mat4 matrix; vec4 pcolor; + vec2 new_vertex; { uint boffset = offset + bone_attrib.x * stride; - matrix = mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weight_attrib.x; - pcolor = transforms.data[boffset + 3] * weight_attrib.x; + new_vertex = (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.x; + pcolor = transforms.data[boffset + 2] * weight_attrib.x; } if (weight_attrib.y > 0.001) { uint boffset = offset + bone_attrib.y * stride; - matrix += mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weight_attrib.y; - pcolor += transforms.data[boffset + 3] * weight_attrib.y; + new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.y; + pcolor += transforms.data[boffset + 2] * weight_attrib.y; } if (weight_attrib.z > 0.001) { uint boffset = offset + bone_attrib.z * stride; - matrix += mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weight_attrib.z; - pcolor += transforms.data[boffset + 3] * weight_attrib.z; + new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.z; + pcolor += transforms.data[boffset + 2] * weight_attrib.z; } if (weight_attrib.w > 0.001) { uint boffset = offset + bone_attrib.w * stride; - matrix += mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weight_attrib.w; - pcolor += transforms.data[boffset + 3] * weight_attrib.w; + new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.w; + pcolor += transforms.data[boffset + 2] * weight_attrib.w; } - instance_custom = transforms.data[offset + 4]; + instance_custom = transforms.data[offset + 3]; + vertex = new_vertex; color *= pcolor; - matrix = transpose(matrix); - world_matrix = world_matrix * matrix; - } else #endif // USE_ATTRIBUTES @@ -283,7 +281,7 @@ vec2 screen_uv_to_sdf(vec2 p_uv) { float texture_sdf(vec2 p_sdf) { vec2 uv = p_sdf * canvas_data.sdf_to_tex.xy + canvas_data.sdf_to_tex.zw; float d = texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv).r; - d = d * SDF_MAX_LENGTH - 1.0; + d *= SDF_MAX_LENGTH; return d * canvas_data.tex_to_sdf; } diff --git a/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl b/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl index 65a554e839..2bdfbabfcf 100644 --- a/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl @@ -7,7 +7,7 @@ layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; layout(r8, set = 0, binding = 1) uniform restrict readonly image2D src_pixels; -layout(r16, set = 0, binding = 2) uniform restrict writeonly image2D dst_sdf; +layout(r16_snorm, set = 0, binding = 2) uniform restrict writeonly image2D dst_sdf; layout(rg16i, set = 0, binding = 3) uniform restrict readonly iimage2D src_process; layout(rg16i, set = 0, binding = 4) uniform restrict writeonly iimage2D dst_process; @@ -32,7 +32,7 @@ void main() { #ifdef MODE_LOAD bool solid = imageLoad(src_pixels, pos).r > 0.5; - imageStore(dst_process, pos, solid ? ivec4(pos, 0, 0) : ivec4(ivec2(32767), 0, 0)); + imageStore(dst_process, pos, solid ? ivec4(ivec2(-32767), 0, 0) : ivec4(ivec2(32767), 0, 0)); #endif #ifdef MODE_LOAD_SHRINK @@ -43,6 +43,8 @@ void main() { ivec2 rel = ivec2(32767); float d = 1e20; + int found = 0; + int solid_found = 0; for (int i = 0; i < s; i++) { for (int j = 0; j < s; j++) { ivec2 src_pos = base + ivec2(i, j); @@ -56,10 +58,17 @@ void main() { d = dist; rel = src_pos; } + solid_found++; } + found++; } } + if (solid_found == found) { + //mark solid only if all are solid + rel = ivec2(-32767); + } + imageStore(dst_process, pos, ivec4(rel, 0, 0)); #endif @@ -70,6 +79,12 @@ void main() { ivec2 rel = imageLoad(src_process, pos).xy; + bool solid = rel.x < 0; + + if (solid) { + rel = -rel - ivec2(1); + } + if (center != rel) { //only process if it does not point to itself const int ofs_table_size = 8; @@ -92,6 +107,15 @@ void main() { continue; } ivec2 src_rel = imageLoad(src_process, src_pos).xy; + bool src_solid = src_rel.x < 0; + if (src_solid) { + src_rel = -src_rel - ivec2(1); + } + + if (src_solid != solid) { + src_rel = ivec2(src_pos << params.shift); //point to itself if of different type + } + float src_dist = length(vec2(src_rel - center)); if (src_dist < dist) { dist = src_dist; @@ -100,18 +124,31 @@ void main() { } } + if (solid) { + rel = -rel - ivec2(1); + } + imageStore(dst_process, pos, ivec4(rel, 0, 0)); #endif #ifdef MODE_STORE ivec2 rel = imageLoad(src_process, pos).xy; + + bool solid = rel.x < 0; + + if (solid) { + rel = -rel - ivec2(1); + } + float d = length(vec2(rel - pos)); - if (d > 0.01) { - d += 1.0; //make it signed + + if (solid) { + d = -d; } + d /= SDF_MAX_LENGTH; - d = clamp(d, 0.0, 1.0); + d = clamp(d, -1.0, 1.0); imageStore(dst_sdf, pos, vec4(d)); #endif @@ -122,13 +159,20 @@ void main() { ivec2 center = base + ivec2(params.shift); ivec2 rel = imageLoad(src_process, pos).xy; + + bool solid = rel.x < 0; + + if (solid) { + rel = -rel - ivec2(1); + } + float d = length(vec2(rel - center)); - if (d > 0.01) { - d += 1.0; //make it signed + if (solid) { + d = -d; } d /= SDF_MAX_LENGTH; - d = clamp(d, 0.0, 1.0); + d = clamp(d, -1.0, 1.0); imageStore(dst_sdf, pos, vec4(d)); #endif diff --git a/servers/rendering/renderer_rd/shaders/particles.glsl b/servers/rendering/renderer_rd/shaders/particles.glsl index beaff10793..9f8410fd8a 100644 --- a/servers/rendering/renderer_rd/shaders/particles.glsl +++ b/servers/rendering/renderer_rd/shaders/particles.glsl @@ -19,6 +19,8 @@ layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; #define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10 #define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11 +#define SDF_MAX_LENGTH 16384.0 + /* SET 0: GLOBAL DATA */ layout(set = 0, binding = 1) uniform sampler material_samplers[12]; @@ -54,6 +56,7 @@ struct Attractor { #define COLLIDER_TYPE_BOX 1 #define COLLIDER_TYPE_SDF 2 #define COLLIDER_TYPE_HEIGHT_FIELD 3 +#define COLLIDER_TYPE_2D_SDF 4 struct Collider { mat4 transform; @@ -452,128 +455,167 @@ void main() { #endif - for (uint i = 0; i < FRAME.collider_count; i++) { - vec3 normal; - float depth; - bool col = false; + if (FRAME.collider_count == 1 && FRAME.colliders[0].type == COLLIDER_TYPE_2D_SDF) { + //2D collision - vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.colliders[i].transform[3].xyz; - vec3 local_pos = rel_vec * mat3(FRAME.colliders[i].transform); + vec2 pos = PARTICLE.xform[3].xy; + vec4 to_sdf_x = FRAME.colliders[0].transform[0]; + vec4 to_sdf_y = FRAME.colliders[0].transform[1]; + vec2 sdf_pos = vec2(dot(vec4(pos, 0, 1), to_sdf_x), dot(vec4(pos, 0, 1), to_sdf_y)); - switch (FRAME.colliders[i].type) { - case COLLIDER_TYPE_SPHERE: { - float d = length(rel_vec) - (particle_size + FRAME.colliders[i].extents.x); + vec4 sdf_to_screen = vec4(FRAME.colliders[0].extents, FRAME.colliders[0].scale); - if (d < 0.0) { - col = true; - depth = -d; - normal = normalize(rel_vec); - } + vec2 uv_pos = sdf_pos * sdf_to_screen.xy + sdf_to_screen.zw; - } break; - case COLLIDER_TYPE_BOX: { - vec3 abs_pos = abs(local_pos); - vec3 sgn_pos = sign(local_pos); + if (all(greaterThan(uv_pos, vec2(0.0))) && all(lessThan(uv_pos, vec2(1.0)))) { + vec2 pos2 = pos + vec2(0, particle_size); + vec2 sdf_pos2 = vec2(dot(vec4(pos2, 0, 1), to_sdf_x), dot(vec4(pos2, 0, 1), to_sdf_y)); + float sdf_particle_size = distance(sdf_pos, sdf_pos2); + + float d = texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos).r * SDF_MAX_LENGTH; + + d -= sdf_particle_size; + + if (d < 0.0) { + const float EPSILON = 0.001; + vec2 n = normalize(vec2( + texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos + vec2(EPSILON, 0.0)).r - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos - vec2(EPSILON, 0.0)).r, + texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos + vec2(0.0, EPSILON)).r - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos - vec2(0.0, EPSILON)).r)); + + collided = true; + sdf_pos2 = sdf_pos + n * d; + pos2 = vec2(dot(vec4(sdf_pos2, 0, 1), FRAME.colliders[0].transform[2]), dot(vec4(sdf_pos2, 0, 1), FRAME.colliders[0].transform[3])); + + n = pos - pos2; + + collision_normal = normalize(vec3(n, 0.0)); + collision_depth = length(n); + } + } + + } else { + for (uint i = 0; i < FRAME.collider_count; i++) { + vec3 normal; + float depth; + bool col = false; - if (any(greaterThan(abs_pos, FRAME.colliders[i].extents))) { - //point outside box + vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.colliders[i].transform[3].xyz; + vec3 local_pos = rel_vec * mat3(FRAME.colliders[i].transform); - vec3 closest = min(abs_pos, FRAME.colliders[i].extents); - vec3 rel = abs_pos - closest; - depth = length(rel) - particle_size; - if (depth < 0.0) { + switch (FRAME.colliders[i].type) { + case COLLIDER_TYPE_SPHERE: { + float d = length(rel_vec) - (particle_size + FRAME.colliders[i].extents.x); + + if (d < 0.0) { col = true; - normal = mat3(FRAME.colliders[i].transform) * (normalize(rel) * sgn_pos); - depth = -depth; + depth = -d; + normal = normalize(rel_vec); } - } else { - //point inside box - vec3 axis_len = FRAME.colliders[i].extents - abs_pos; - // there has to be a faster way to do this? - if (all(lessThan(axis_len.xx, axis_len.yz))) { - normal = vec3(1, 0, 0); - } else if (all(lessThan(axis_len.yy, axis_len.xz))) { - normal = vec3(0, 1, 0); + + } break; + case COLLIDER_TYPE_BOX: { + vec3 abs_pos = abs(local_pos); + vec3 sgn_pos = sign(local_pos); + + if (any(greaterThan(abs_pos, FRAME.colliders[i].extents))) { + //point outside box + + vec3 closest = min(abs_pos, FRAME.colliders[i].extents); + vec3 rel = abs_pos - closest; + depth = length(rel) - particle_size; + if (depth < 0.0) { + col = true; + normal = mat3(FRAME.colliders[i].transform) * (normalize(rel) * sgn_pos); + depth = -depth; + } } else { - normal = vec3(0, 0, 1); + //point inside box + vec3 axis_len = FRAME.colliders[i].extents - abs_pos; + // there has to be a faster way to do this? + if (all(lessThan(axis_len.xx, axis_len.yz))) { + normal = vec3(1, 0, 0); + } else if (all(lessThan(axis_len.yy, axis_len.xz))) { + normal = vec3(0, 1, 0); + } else { + normal = vec3(0, 0, 1); + } + + col = true; + depth = dot(normal * axis_len, vec3(1)) + particle_size; + normal = mat3(FRAME.colliders[i].transform) * (normal * sgn_pos); } - col = true; - depth = dot(normal * axis_len, vec3(1)) + particle_size; - normal = mat3(FRAME.colliders[i].transform) * (normal * sgn_pos); - } + } break; + case COLLIDER_TYPE_SDF: { + vec3 apos = abs(local_pos); + float extra_dist = 0.0; + if (any(greaterThan(apos, FRAME.colliders[i].extents))) { //outside + vec3 mpos = min(apos, FRAME.colliders[i].extents); + extra_dist = distance(mpos, apos); + } - } break; - case COLLIDER_TYPE_SDF: { - vec3 apos = abs(local_pos); - float extra_dist = 0.0; - if (any(greaterThan(apos, FRAME.colliders[i].extents))) { //outside - vec3 mpos = min(apos, FRAME.colliders[i].extents); - extra_dist = distance(mpos, apos); - } + if (extra_dist > particle_size) { + continue; + } - if (extra_dist > particle_size) { - continue; - } + vec3 uvw_pos = (local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5; + float s = texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).r; + s *= FRAME.colliders[i].scale; + s += extra_dist; + if (s < particle_size) { + col = true; + depth = particle_size - s; + const float EPSILON = 0.001; + normal = mat3(FRAME.colliders[i].transform) * + normalize( + vec3( + texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(EPSILON, 0.0, 0.0)).r, + texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, EPSILON, 0.0)).r, + texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, 0.0, EPSILON)).r)); + } - vec3 uvw_pos = (local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5; - float s = texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).r; - s *= FRAME.colliders[i].scale; - s += extra_dist; - if (s < particle_size) { - col = true; - depth = particle_size - s; - const float EPSILON = 0.001; - normal = mat3(FRAME.colliders[i].transform) * - normalize( - vec3( - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(EPSILON, 0.0, 0.0)).r, - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, EPSILON, 0.0)).r, - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, 0.0, EPSILON)).r)); - } + } break; + case COLLIDER_TYPE_HEIGHT_FIELD: { + vec3 local_pos_bottom = local_pos; + local_pos_bottom.y -= particle_size; - } break; - case COLLIDER_TYPE_HEIGHT_FIELD: { - vec3 local_pos_bottom = local_pos; - local_pos_bottom.y -= particle_size; + if (any(greaterThan(abs(local_pos_bottom), FRAME.colliders[i].extents))) { + continue; + } + const float DELTA = 1.0 / 8192.0; - if (any(greaterThan(abs(local_pos_bottom), FRAME.colliders[i].extents))) { - continue; - } + vec3 uvw_pos = vec3(local_pos_bottom / FRAME.colliders[i].extents) * 0.5 + 0.5; - const float DELTA = 1.0 / 8192.0; + float y = 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz).r; - vec3 uvw_pos = vec3(local_pos_bottom / FRAME.colliders[i].extents) * 0.5 + 0.5; + if (y > uvw_pos.y) { + //inside heightfield - float y = 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz).r; + vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents; + vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents; + vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents; - if (y > uvw_pos.y) { - //inside heightfield + normal = normalize(cross(pos1 - pos2, pos1 - pos3)); + float local_y = (vec3(local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5).y; - vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents; - vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents; - vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents; + col = true; + depth = dot(normal, pos1) - dot(normal, local_pos_bottom); + } - normal = normalize(cross(pos1 - pos2, pos1 - pos3)); - float local_y = (vec3(local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5).y; + } break; + } - col = true; - depth = dot(normal, pos1) - dot(normal, local_pos_bottom); + if (col) { + if (!collided) { + collided = true; + collision_normal = normal; + collision_depth = depth; + } else { + vec3 c = collision_normal * collision_depth; + c += normal * max(0.0, depth - dot(normal, c)); + collision_normal = normalize(c); + collision_depth = length(c); } - - } break; - } - - if (col) { - if (!collided) { - collided = true; - collision_normal = normal; - collision_depth = depth; - } else { - vec3 c = collision_normal * collision_depth; - c += normal * max(0.0, depth - dot(normal, c)); - collision_normal = normalize(c); - collision_depth = length(c); } } } diff --git a/servers/rendering/renderer_rd/shaders/particles_copy.glsl b/servers/rendering/renderer_rd/shaders/particles_copy.glsl index e2bebadf1a..4dceeea995 100644 --- a/servers/rendering/renderer_rd/shaders/particles_copy.glsl +++ b/servers/rendering/renderer_rd/shaders/particles_copy.glsl @@ -53,6 +53,11 @@ layout(push_constant, binding = 0, std430) uniform Params { vec3 align_up; uint align_mode; + + bool order_by_lifetime; + uint lifetime_split; + bool lifetime_reverse; + uint pad; } params; @@ -80,7 +85,6 @@ void main() { #ifdef MODE_FILL_INSTANCES uint particle = gl_GlobalInvocationID.x; - uint write_offset = gl_GlobalInvocationID.x * (3 + 1 + 1); //xform + color + custom if (particle >= params.total_particles) { return; //discard @@ -93,7 +97,41 @@ void main() { } else { particle = uint(sort_buffer.data[particle].y); //use index from sort buffer } -#endif +#else + if (params.order_by_lifetime) { + if (params.trail_size > 1) { + uint limit = (params.total_particles / params.trail_size) - params.lifetime_split; + + uint base_index = particle / params.trail_size; + uint base_offset = particle % params.trail_size; + + if (params.lifetime_reverse) { + base_index = (params.total_particles / params.trail_size) - base_index - 1; + } + + if (base_index < limit) { + base_index = params.lifetime_split + base_index; + } else { + base_index -= limit; + } + + particle = base_index * params.trail_size + base_offset; + + } else { + uint limit = params.total_particles - params.lifetime_split; + + if (params.lifetime_reverse) { + particle = params.total_particles - particle - 1; + } + + if (particle < limit) { + particle = params.lifetime_split + particle; + } else { + particle -= limit; + } + } + } +#endif // USE_SORT_BUFFER mat4 txform; @@ -165,12 +203,17 @@ void main() { #ifdef MODE_2D + uint write_offset = gl_GlobalInvocationID.x * (2 + 1 + 1); //xform + color + custom + instances.data[write_offset + 0] = txform[0]; instances.data[write_offset + 1] = txform[1]; instances.data[write_offset + 2] = particles.data[particle].color; instances.data[write_offset + 3] = particles.data[particle].custom; #else + + uint write_offset = gl_GlobalInvocationID.x * (3 + 1 + 1); //xform + color + custom + instances.data[write_offset + 0] = txform[0]; instances.data[write_offset + 1] = txform[1]; instances.data[write_offset + 2] = txform[2]; diff --git a/servers/rendering/renderer_storage.h b/servers/rendering/renderer_storage.h index cc4ff5d55e..75975e909d 100644 --- a/servers/rendering/renderer_storage.h +++ b/servers/rendering/renderer_storage.h @@ -534,6 +534,8 @@ public: virtual void particles_add_collision(RID p_particles, RID p_particles_collision_instance) = 0; virtual void particles_remove_collision(RID p_particles, RID p_particles_collision_instance) = 0; + virtual void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) = 0; + virtual void update_particles() = 0; /* PARTICLES COLLISION */ @@ -603,6 +605,7 @@ public: virtual void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) = 0; virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const = 0; + virtual void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) = 0; virtual RS::InstanceType get_base_type(RID p_rid) const = 0; virtual bool free(RID p_rid) = 0; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 9ac2c1918f..f97e24947d 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -186,8 +186,11 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_ } RSG::canvas_render->render_sdf(p_viewport->render_target, occluders); + RSG::storage->render_target_mark_sdf_enabled(p_viewport->render_target, true); p_viewport->sdf_active = false; // if used, gets set active again + } else { + RSG::storage->render_target_mark_sdf_enabled(p_viewport->render_target, false); } Rect2 shadow_rect; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index e92940b31a..2ce7707257 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -33,6 +33,8 @@ #include "core/string/print_string.h" #include "servers/rendering_server.h" +#define HAS_WARNING(flag) (warning_flags & flag) + static bool _is_text_char(char32_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } @@ -901,6 +903,8 @@ bool ShaderLanguage::is_token_nonvoid_datatype(TokenType p_type) { void ShaderLanguage::clear() { current_function = StringName(); + last_name = StringName(); + last_type = IDENTIFIER_MAX; completion_type = COMPLETION_NONE; completion_block = nullptr; @@ -908,12 +912,20 @@ void ShaderLanguage::clear() { completion_class = SubClassTag::TAG_GLOBAL; completion_struct = StringName(); +#ifdef DEBUG_ENABLED + used_constants.clear(); + used_varyings.clear(); + used_uniforms.clear(); + used_functions.clear(); + used_structs.clear(); + warnings.clear(); +#endif // DEBUG_ENABLED + error_line = 0; tk_line = 1; char_idx = 0; error_set = false; error_str = ""; - last_const = false; while (nodes) { Node *n = nodes; nodes = nodes->next; @@ -921,6 +933,35 @@ void ShaderLanguage::clear() { } } +#ifdef DEBUG_ENABLED +void ShaderLanguage::_parse_used_identifier(const StringName &p_identifier, IdentifierType p_type) { + switch (p_type) { + case IdentifierType::IDENTIFIER_CONSTANT: + if (HAS_WARNING(ShaderWarning::UNUSED_CONSTANT_FLAG) && used_constants.has(p_identifier)) { + used_constants[p_identifier].used = true; + } + break; + case IdentifierType::IDENTIFIER_VARYING: + if (HAS_WARNING(ShaderWarning::UNUSED_VARYING_FLAG) && used_varyings.has(p_identifier)) { + used_varyings[p_identifier].used = true; + } + break; + case IdentifierType::IDENTIFIER_UNIFORM: + if (HAS_WARNING(ShaderWarning::UNUSED_UNIFORM_FLAG) && used_uniforms.has(p_identifier)) { + used_uniforms[p_identifier].used = true; + } + break; + case IdentifierType::IDENTIFIER_FUNCTION: + if (HAS_WARNING(ShaderWarning::UNUSED_FUNCTION_FLAG) && used_functions.has(p_identifier)) { + used_functions[p_identifier].used = true; + } + break; + default: + break; + } +} +#endif // DEBUG_ENABLED + bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name, ConstantNode::Value *r_constant_value) { if (p_function_info.built_ins.has(p_identifier)) { if (r_data_type) { @@ -3602,6 +3643,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (shader->structs.has(identifier)) { pstruct = shader->structs[identifier].shader_struct; +#ifdef DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_STRUCT_FLAG) && used_structs.has(identifier)) { + used_structs[identifier].used = true; + } +#endif // DEBUG_ENABLED struct_init = true; } @@ -3825,11 +3871,17 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } } expr = func; +#ifdef DEBUG_ENABLED + if (check_warnings) { + _parse_used_identifier(name, IdentifierType::IDENTIFIER_FUNCTION); + } +#endif // DEBUG_ENABLED } } else { //an identifier - last_const = false; + last_name = identifier; + last_type = IDENTIFIER_MAX; _set_tkpos(pos); DataType data_type; @@ -3874,12 +3926,16 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } } } - last_const = is_const; if (ident_type == IDENTIFIER_FUNCTION) { _set_error("Can't use function as identifier: " + String(identifier)); return nullptr; } + if (is_const) { + last_type = IDENTIFIER_CONSTANT; + } else { + last_type = ident_type; + } } Node *index_expression = nullptr; @@ -3953,7 +4009,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons arrname->assign_expression = assign_expression; arrname->is_const = is_const; expr = arrname; - } else { VariableNode *varname = alloc_node<VariableNode>(); varname->name = identifier; @@ -3962,6 +4017,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons varname->struct_name = struct_name; expr = varname; } +#ifdef DEBUG_ENABLED + if (check_warnings) { + _parse_used_identifier(identifier, ident_type); + } +#endif // DEBUG_ENABLED } } else if (tk.type == TK_OP_ADD) { continue; //this one does nothing @@ -4290,8 +4350,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (array_size > 0) { tk = _get_token(); if (tk.type == TK_OP_ASSIGN) { - if (last_const) { - last_const = false; + if (last_type == IDENTIFIER_CONSTANT) { _set_error("Constants cannot be modified."); return nullptr; } @@ -4648,9 +4707,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons bool unary = false; bool ternary = false; + Operator op = expression[i].op; int priority; - switch (expression[i].op) { + switch (op) { case OP_EQUAL: priority = 8; break; @@ -4771,6 +4831,12 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons ERR_FAIL_V(nullptr); //unexpected operator } +#if DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::FLOAT_COMPARISON_FLAG) && (op == OP_EQUAL || op == OP_NOT_EQUAL) && expression[i - 1].node->get_datatype() == TYPE_FLOAT && expression[i + 1].node->get_datatype() == TYPE_FLOAT) { + _add_line_warning(ShaderWarning::FLOAT_COMPARISON); + } +#endif // DEBUG_ENABLED + if (priority < min_priority) { // < is used for left to right (default) // <= is used for right to left @@ -5483,7 +5549,6 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun p_block->statements.push_back(vardecl); p_block->variables[name] = var; - if (tk.type == TK_COMMA) { if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR) { _set_error("Multiple declarations in 'for' loop are not implemented yet."); @@ -6313,7 +6378,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct } shader->structs[st.name] = st; shader->vstructs.push_back(st); // struct's order is important! - +#ifdef DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_STRUCT_FLAG)) { + used_structs.insert(st.name, Usage(tk_line)); + } +#endif // DEBUG_ENABLED } break; case TK_GLOBAL: { tk = _get_token(); @@ -6660,6 +6729,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct } shader->uniforms[name] = uniform2; +#ifdef DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_UNIFORM_FLAG)) { + used_uniforms.insert(name, Usage(tk_line)); + } +#endif // DEBUG_ENABLED + //reset scope for next uniform uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL; @@ -6703,6 +6778,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct } shader->varyings[name] = varying; +#ifdef DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_VARYING_FLAG)) { + used_varyings.insert(name, Usage(tk_line)); + } +#endif // DEBUG_ENABLED } } break; @@ -7049,6 +7129,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct shader->constants[name] = constant; shader->vconstants.push_back(constant); +#ifdef DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_CONSTANT_FLAG)) { + used_constants.insert(name, Usage(tk_line)); + } +#endif // DEBUG_ENABLED if (tk.type == TK_COMMA) { tk = _get_token(); @@ -7110,6 +7195,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct if (p_functions.has(name)) { func_node->can_discard = p_functions[name].can_discard; + } else { +#ifdef DEBUG_ENABLED + if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_FUNCTION_FLAG)) { + used_functions.insert(name, Usage(tk_line)); + } +#endif // DEBUG_ENABLED } func_node->body = alloc_node<BlockNode>(); @@ -7443,6 +7534,33 @@ String ShaderLanguage::get_shader_type(const String &p_code) { return String(); } +#ifdef DEBUG_ENABLED +void ShaderLanguage::_check_warning_accums() { + for (Map<ShaderWarning::Code, Map<StringName, Usage> *>::Element *E = warnings_check_map.front(); E; E = E->next()) { + for (const Map<StringName, Usage>::Element *U = (*E->get()).front(); U; U = U->next()) { + if (!U->get().used) { + _add_warning(E->key(), U->get().decl_line, U->key()); + } + } + } +} +List<ShaderWarning>::Element *ShaderLanguage::get_warnings_ptr() { + return warnings.front(); +} +void ShaderLanguage::enable_warning_checking(bool p_enabled) { + check_warnings = p_enabled; +} +bool ShaderLanguage::is_warning_checking_enabled() const { + return check_warnings; +} +void ShaderLanguage::set_warning_flags(uint32_t p_flags) { + warning_flags = p_flags; +} +uint32_t ShaderLanguage::get_warning_flags() const { + return warning_flags; +} +#endif // DEBUG_ENABLED + Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const VaryingFunctionNames &p_varying_function_names, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func) { clear(); @@ -7455,6 +7573,12 @@ Error ShaderLanguage::compile(const String &p_code, const Map<StringName, Functi shader = alloc_node<ShaderNode>(); Error err = _parse_shader(p_functions, p_render_modes, p_shader_types); +#ifdef DEBUG_ENABLED + if (check_warnings) { + _check_warning_accums(); + } +#endif // DEBUG_ENABLED + if (err != OK) { return err; } @@ -7864,6 +7988,14 @@ ShaderLanguage::ShaderNode *ShaderLanguage::get_shader() { ShaderLanguage::ShaderLanguage() { nodes = nullptr; completion_class = TAG_GLOBAL; + +#if DEBUG_ENABLED + warnings_check_map.insert(ShaderWarning::UNUSED_CONSTANT, &used_constants); + warnings_check_map.insert(ShaderWarning::UNUSED_FUNCTION, &used_functions); + warnings_check_map.insert(ShaderWarning::UNUSED_STRUCT, &used_structs); + warnings_check_map.insert(ShaderWarning::UNUSED_UNIFORM, &used_uniforms); + warnings_check_map.insert(ShaderWarning::UNUSED_VARYING, &used_varyings); +#endif // DEBUG_ENABLED } ShaderLanguage::~ShaderLanguage() { diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index cdedc5edbb..c8c5bb1fa7 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -39,6 +39,10 @@ #include "core/typedefs.h" #include "core/variant/variant.h" +#ifdef DEBUG_ENABLED +#include "shader_warnings.h" +#endif // DEBUG_ENABLED + class ShaderLanguage { public: struct TkPos { @@ -803,12 +807,42 @@ private: String error_str; int error_line; +#ifdef DEBUG_ENABLED + struct Usage { + int decl_line; + bool used = false; + Usage(int p_decl_line = -1) { + decl_line = p_decl_line; + } + }; + + Map<StringName, Usage> used_constants; + Map<StringName, Usage> used_varyings; + Map<StringName, Usage> used_uniforms; + Map<StringName, Usage> used_functions; + Map<StringName, Usage> used_structs; + Map<ShaderWarning::Code, Map<StringName, Usage> *> warnings_check_map; + + List<ShaderWarning> warnings; + + bool check_warnings = false; + uint32_t warning_flags; + + void _add_line_warning(ShaderWarning::Code p_code, const StringName &p_subject = "") { + warnings.push_back(ShaderWarning(p_code, tk_line, p_subject)); + } + void _add_warning(ShaderWarning::Code p_code, int p_line, const StringName &p_subject = "") { + warnings.push_back(ShaderWarning(p_code, p_line, p_subject)); + } + void _check_warning_accums(); +#endif // DEBUG_ENABLED + String code; int char_idx; int tk_line; StringName current_function; - bool last_const = false; + StringName last_name; VaryingFunctionNames varying_function_names; @@ -849,9 +883,15 @@ private: IDENTIFIER_LOCAL_VAR, IDENTIFIER_BUILTIN_VAR, IDENTIFIER_CONSTANT, + IDENTIFIER_MAX, }; + IdentifierType last_type = IDENTIFIER_MAX; + bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr, ConstantNode::Value *r_constant_value = nullptr); +#ifdef DEBUG_ENABLED + void _parse_used_identifier(const StringName &p_identifier, IdentifierType p_type); +#endif // DEBUG_ENABLED bool _is_operator_assign(Operator p_op) const; bool _validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message = nullptr); bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = nullptr); @@ -910,6 +950,16 @@ private: Error _find_last_flow_op_in_op(ControlFlowNode *p_flow, FlowOperation p_op); public: +#ifdef DEBUG_ENABLED + List<ShaderWarning>::Element *get_warnings_ptr(); + + void enable_warning_checking(bool p_enabled); + bool is_warning_checking_enabled() const; + + void set_warning_flags(uint32_t p_flags); + uint32_t get_warning_flags() const; +#endif // DEBUG_ENABLED + //static void get_keyword_list(ShaderType p_type,List<String> *p_keywords); void clear(); diff --git a/servers/rendering/shader_warnings.cpp b/servers/rendering/shader_warnings.cpp new file mode 100644 index 0000000000..aa11b4e397 --- /dev/null +++ b/servers/rendering/shader_warnings.cpp @@ -0,0 +1,131 @@ +/*************************************************************************/ +/* shader_warnings.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "shader_warnings.h" +#include "core/variant/variant.h" + +#ifdef DEBUG_ENABLED + +ShaderWarning::Code ShaderWarning::get_code() const { + return code; +} + +int ShaderWarning::get_line() const { + return line; +} + +const StringName &ShaderWarning::get_subject() const { + return subject; +} + +String ShaderWarning::get_message() const { + switch (code) { + case FLOAT_COMPARISON: + return vformat("Direct floating-point comparison (this may not evaluate to `true` as you expect). Instead, use `abs(a - b) < 0.0001` for an approximate but predictable comparison."); + case UNUSED_CONSTANT: + return vformat("The const '%s' is declared but never used.", subject); + case UNUSED_FUNCTION: + return vformat("The function '%s' is declared but never used.", subject); + case UNUSED_STRUCT: + return vformat("The struct '%s' is declared but never used.", subject); + case UNUSED_UNIFORM: + return vformat("The uniform '%s' is declared but never used.", subject); + case UNUSED_VARYING: + return vformat("The varying '%s' is declared but never used.", subject); + default: + break; + } + return String(); +} + +String ShaderWarning::get_name() const { + return get_name_from_code(code); +} + +String ShaderWarning::get_name_from_code(Code p_code) { + ERR_FAIL_INDEX_V(p_code, WARNING_MAX, String()); + + static const char *names[] = { + "FLOAT_COMPARISON", + "UNUSED_CONSTANT", + "UNUSED_FUNCTION", + "UNUSED_STRUCT", + "UNUSED_UNIFORM", + "UNUSED_VARYING", + }; + + static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names."); + + return names[(int)p_code]; +} + +ShaderWarning::Code ShaderWarning::get_code_from_name(const String &p_name) { + for (int i = 0; i < WARNING_MAX; i++) { + if (get_name_from_code((Code)i) == p_name) { + return (Code)i; + } + } + + ERR_FAIL_V_MSG(WARNING_MAX, "Invalid shader warning name: " + p_name); +} + +static Map<int, uint32_t> *code_to_flags_map = nullptr; + +static void init_code_to_flags_map() { + code_to_flags_map = memnew((Map<int, uint32_t>)); + code_to_flags_map->insert(ShaderWarning::FLOAT_COMPARISON, ShaderWarning::FLOAT_COMPARISON_FLAG); + code_to_flags_map->insert(ShaderWarning::UNUSED_CONSTANT, ShaderWarning::UNUSED_CONSTANT_FLAG); + code_to_flags_map->insert(ShaderWarning::UNUSED_FUNCTION, ShaderWarning::UNUSED_FUNCTION_FLAG); + code_to_flags_map->insert(ShaderWarning::UNUSED_STRUCT, ShaderWarning::UNUSED_STRUCT_FLAG); + code_to_flags_map->insert(ShaderWarning::UNUSED_UNIFORM, ShaderWarning::UNUSED_UNIFORM_FLAG); + code_to_flags_map->insert(ShaderWarning::UNUSED_VARYING, ShaderWarning::UNUSED_VARYING_FLAG); +} + +ShaderWarning::CodeFlags ShaderWarning::get_flags_from_codemap(const Map<Code, bool> &p_map) { + uint32_t result = 0U; + + if (code_to_flags_map == nullptr) { + init_code_to_flags_map(); + } + + for (Map<Code, bool>::Element *E = p_map.front(); E; E = E->next()) { + if (E->get()) { + ERR_FAIL_COND_V(!code_to_flags_map->has((int)E->key()), ShaderWarning::NONE_FLAG); + result |= (*code_to_flags_map)[(int)E->key()]; + } + } + return (CodeFlags)result; +} + +ShaderWarning::ShaderWarning(Code p_code, int p_line, const StringName &p_subject) : + code(p_code), line(p_line), subject(p_subject) { +} + +#endif // DEBUG_ENABLED diff --git a/servers/rendering/shader_warnings.h b/servers/rendering/shader_warnings.h new file mode 100644 index 0000000000..c40aeefa2d --- /dev/null +++ b/servers/rendering/shader_warnings.h @@ -0,0 +1,83 @@ +/*************************************************************************/ +/* shader_warnings.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SHADER_WARNINGS +#define SHADER_WARNINGS + +#ifdef DEBUG_ENABLED + +#include "core/string/string_name.h" +#include "core/templates/list.h" +#include "core/templates/map.h" + +class ShaderWarning { +public: + enum Code { + FLOAT_COMPARISON, + UNUSED_CONSTANT, + UNUSED_FUNCTION, + UNUSED_STRUCT, + UNUSED_UNIFORM, + UNUSED_VARYING, + WARNING_MAX, + }; + + enum CodeFlags : uint32_t { + NONE_FLAG = 0U, + FLOAT_COMPARISON_FLAG = 1U, + UNUSED_CONSTANT_FLAG = 2U, + UNUSED_FUNCTION_FLAG = 4U, + UNUSED_STRUCT_FLAG = 8U, + UNUSED_UNIFORM_FLAG = 16U, + UNUSED_VARYING_FLAG = 32U, + }; + +private: + Code code; + int line; + StringName subject; + +public: + Code get_code() const; + int get_line() const; + const StringName &get_subject() const; + String get_message() const; + String get_name() const; + + static String get_name_from_code(Code p_code); + static Code get_code_from_name(const String &p_name); + static CodeFlags get_flags_from_codemap(const Map<Code, bool> &p_map); + + ShaderWarning(Code p_code = WARNING_MAX, int p_line = -1, const StringName &p_subject = ""); +}; + +#endif // DEBUG_ENABLED + +#endif // SHADER_WARNINGS |