diff options
Diffstat (limited to 'servers/rendering')
82 files changed, 2685 insertions, 545 deletions
diff --git a/servers/rendering/dummy/rasterizer_dummy.h b/servers/rendering/dummy/rasterizer_dummy.h index a4f353359a..c3f5f3102d 100644 --- a/servers/rendering/dummy/rasterizer_dummy.h +++ b/servers/rendering/dummy/rasterizer_dummy.h @@ -88,7 +88,7 @@ public: void blit_render_targets_to_screen(int p_screen, const BlitToScreen *p_render_targets, int p_amount) override {} - void end_viewport(bool p_swap_buffers) override {} + void gl_end_frame(bool p_swap_buffers) override {} void end_frame(bool p_swap_buffers) override { if (p_swap_buffers) { diff --git a/servers/rendering/dummy/rasterizer_scene_dummy.h b/servers/rendering/dummy/rasterizer_scene_dummy.h index 083493003f..a699a58b1f 100644 --- a/servers/rendering/dummy/rasterizer_scene_dummy.h +++ b/servers/rendering/dummy/rasterizer_scene_dummy.h @@ -186,6 +186,7 @@ public: virtual void decals_set_filter(RS::DecalFilter p_filter) override {} virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override {} + virtual void lightmaps_set_bicubic_filter(bool p_enable) override {} RasterizerSceneDummy() {} ~RasterizerSceneDummy() {} diff --git a/servers/rendering/dummy/storage/light_storage.cpp b/servers/rendering/dummy/storage/light_storage.cpp new file mode 100644 index 0000000000..443e047b37 --- /dev/null +++ b/servers/rendering/dummy/storage/light_storage.cpp @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* light_storage.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "light_storage.h" + +using namespace RendererDummy; + +LightStorage *LightStorage::singleton = nullptr; + +LightStorage *LightStorage::get_singleton() { + return singleton; +} + +LightStorage::LightStorage() { + singleton = this; +} + +LightStorage::~LightStorage() { + singleton = nullptr; +} + +bool LightStorage::free(RID p_rid) { + if (owns_lightmap(p_rid)) { + lightmap_free(p_rid); + return true; + } else if (owns_lightmap_instance(p_rid)) { + lightmap_instance_free(p_rid); + return true; + } + + return false; +} + +/* LIGHTMAP API */ + +RID LightStorage::lightmap_allocate() { + return lightmap_owner.allocate_rid(); +} + +void LightStorage::lightmap_initialize(RID p_lightmap) { + lightmap_owner.initialize_rid(p_lightmap, Lightmap()); +} + +void LightStorage::lightmap_free(RID p_rid) { + lightmap_set_textures(p_rid, RID(), false); + lightmap_owner.free(p_rid); +} + +/* LIGHTMAP INSTANCE */ + +RID LightStorage::lightmap_instance_create(RID p_lightmap) { + LightmapInstance li; + li.lightmap = p_lightmap; + return lightmap_instance_owner.make_rid(li); +} + +void LightStorage::lightmap_instance_free(RID p_lightmap) { + lightmap_instance_owner.free(p_lightmap); +} diff --git a/servers/rendering/dummy/storage/light_storage.h b/servers/rendering/dummy/storage/light_storage.h index 0a9602b603..c3b63cdbf6 100644 --- a/servers/rendering/dummy/storage/light_storage.h +++ b/servers/rendering/dummy/storage/light_storage.h @@ -36,7 +36,29 @@ namespace RendererDummy { class LightStorage : public RendererLightStorage { +private: + static LightStorage *singleton; + /* LIGHTMAP */ + struct Lightmap { + // dummy lightmap, no data + }; + + mutable RID_Owner<Lightmap, true> lightmap_owner; + /* LIGHTMAP INSTANCE */ + + struct LightmapInstance { + RID lightmap; + }; + + mutable RID_Owner<LightmapInstance> lightmap_instance_owner; + public: + static LightStorage *get_singleton(); + + LightStorage(); + virtual ~LightStorage(); + + bool free(RID p_rid); /* Light API */ virtual RID directional_light_allocate() override { return RID(); } @@ -146,9 +168,11 @@ public: /* LIGHTMAP CAPTURE */ - virtual RID lightmap_allocate() override { return RID(); } - virtual void lightmap_initialize(RID p_rid) override {} - virtual void lightmap_free(RID p_rid) override {} + bool owns_lightmap(RID p_rid) { return lightmap_owner.owns(p_rid); } + + virtual RID lightmap_allocate() override; + virtual void lightmap_initialize(RID p_rid) override; + virtual void lightmap_free(RID p_rid) override; virtual void lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) override {} virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override {} @@ -167,8 +191,10 @@ public: /* LIGHTMAP INSTANCE */ - RID lightmap_instance_create(RID p_lightmap) override { return RID(); } - void lightmap_instance_free(RID p_lightmap) override {} + bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); } + + RID lightmap_instance_create(RID p_lightmap) override; + void lightmap_instance_free(RID p_lightmap) override; void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override {} /* SHADOW ATLAS API */ diff --git a/servers/rendering/dummy/storage/material_storage.cpp b/servers/rendering/dummy/storage/material_storage.cpp index 64f6b55172..e8b553ca76 100644 --- a/servers/rendering/dummy/storage/material_storage.cpp +++ b/servers/rendering/dummy/storage/material_storage.cpp @@ -102,11 +102,7 @@ void MaterialStorage::get_shader_parameter_list(RID p_shader, List<PropertyInfo> if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) { continue; } - if (E.value.texture_order >= 0) { - filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.texture_order + 100000)); - } else { - filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.order)); - } + filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.prop_order)); } int uniform_count = filtered_uniforms.size(); sorter.sort(filtered_uniforms.ptr(), uniform_count); diff --git a/servers/rendering/dummy/storage/mesh_storage.cpp b/servers/rendering/dummy/storage/mesh_storage.cpp index 0b7ade1762..3f90c80826 100644 --- a/servers/rendering/dummy/storage/mesh_storage.cpp +++ b/servers/rendering/dummy/storage/mesh_storage.cpp @@ -64,22 +64,22 @@ void MeshStorage::mesh_clear(RID p_mesh) { m->surfaces.clear(); } -RID MeshStorage::multimesh_allocate() { +RID MeshStorage::_multimesh_allocate() { return multimesh_owner.allocate_rid(); } -void MeshStorage::multimesh_initialize(RID p_rid) { +void MeshStorage::_multimesh_initialize(RID p_rid) { multimesh_owner.initialize_rid(p_rid, DummyMultiMesh()); } -void MeshStorage::multimesh_free(RID p_rid) { +void MeshStorage::_multimesh_free(RID p_rid) { DummyMultiMesh *multimesh = multimesh_owner.get_or_null(p_rid); ERR_FAIL_NULL(multimesh); multimesh_owner.free(p_rid); } -void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { +void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { DummyMultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); multimesh->buffer.resize(p_buffer.size()); @@ -87,7 +87,7 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b memcpy(cache_data, p_buffer.ptr(), p_buffer.size() * sizeof(float)); } -Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const { +Vector<float> MeshStorage::_multimesh_get_buffer(RID p_multimesh) const { DummyMultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Vector<float>()); diff --git a/servers/rendering/dummy/storage/mesh_storage.h b/servers/rendering/dummy/storage/mesh_storage.h index d98b2e2ee7..ec19562147 100644 --- a/servers/rendering/dummy/storage/mesh_storage.h +++ b/servers/rendering/dummy/storage/mesh_storage.h @@ -146,34 +146,36 @@ public: bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); } - virtual RID multimesh_allocate() override; - virtual void multimesh_initialize(RID p_rid) override; - virtual void multimesh_free(RID p_rid) override; - - virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override {} - virtual int multimesh_get_instance_count(RID p_multimesh) const override { return 0; } - - virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override {} - virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override {} - virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override {} - virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override {} - virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override {} - - virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override {} - virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const override { return AABB(); } - - virtual RID multimesh_get_mesh(RID p_multimesh) const override { return RID(); } - virtual AABB multimesh_get_aabb(RID p_multimesh) const override { return AABB(); } - - virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override { return Transform3D(); } - virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override { return Transform2D(); } - virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override { return Color(); } - virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override { return Color(); } - virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; - virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const override; - - virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override {} - virtual int multimesh_get_visible_instances(RID p_multimesh) const override { return 0; } + virtual RID _multimesh_allocate() override; + virtual void _multimesh_initialize(RID p_rid) override; + virtual void _multimesh_free(RID p_rid) override; + + virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override {} + virtual int _multimesh_get_instance_count(RID p_multimesh) const override { return 0; } + + virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) override {} + virtual void _multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override {} + virtual void _multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override {} + virtual void _multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override {} + virtual void _multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override {} + + virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override {} + virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override { return AABB(); } + + virtual RID _multimesh_get_mesh(RID p_multimesh) const override { return RID(); } + virtual AABB _multimesh_get_aabb(RID p_multimesh) const override { return AABB(); } + + virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override { return Transform3D(); } + virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override { return Transform2D(); } + virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override { return Color(); } + virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override { return Color(); } + virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; + virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const override; + + virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) override {} + virtual int _multimesh_get_visible_instances(RID p_multimesh) const override { return 0; } + + MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override { return nullptr; } /* SKELETON API */ diff --git a/servers/rendering/dummy/storage/utilities.h b/servers/rendering/dummy/storage/utilities.h index 6e8af9afac..ae83547afd 100644 --- a/servers/rendering/dummy/storage/utilities.h +++ b/servers/rendering/dummy/storage/utilities.h @@ -31,6 +31,7 @@ #ifndef UTILITIES_DUMMY_H #define UTILITIES_DUMMY_H +#include "light_storage.h" #include "material_storage.h" #include "mesh_storage.h" #include "servers/rendering/storage/utilities.h" @@ -55,12 +56,16 @@ public: return RS::INSTANCE_MESH; } else if (RendererDummy::MeshStorage::get_singleton()->owns_multimesh(p_rid)) { return RS::INSTANCE_MULTIMESH; + } else if (RendererDummy::LightStorage::get_singleton()->owns_lightmap(p_rid)) { + return RS::INSTANCE_LIGHTMAP; } return RS::INSTANCE_NONE; } virtual bool free(RID p_rid) override { - if (RendererDummy::TextureStorage::get_singleton()->owns_texture(p_rid)) { + if (RendererDummy::LightStorage::get_singleton()->free(p_rid)) { + return true; + } else if (RendererDummy::TextureStorage::get_singleton()->owns_texture(p_rid)) { RendererDummy::TextureStorage::get_singleton()->texture_free(p_rid); return true; } else if (RendererDummy::MeshStorage::get_singleton()->owns_mesh(p_rid)) { diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index e92050a323..0ec161d8cf 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -51,7 +51,7 @@ void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas memset(z_last_list, 0, z_range * sizeof(RendererCanvasRender::Item *)); for (int i = 0; i < p_child_item_count; i++) { - _cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask, p_child_items[i].mirror, 1); + _cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask, Point2(), 1, nullptr); } RendererCanvasRender::Item *list = nullptr; @@ -97,6 +97,7 @@ void _collect_ysort_children(RendererCanvasCull::Item *p_canvas_item, const Tran if (!child_items[i]->repeat_source) { child_items[i]->repeat_size = p_canvas_item->repeat_size; child_items[i]->repeat_times = p_canvas_item->repeat_times; + child_items[i]->repeat_source_item = p_canvas_item->repeat_source_item; } // Y sorted canvas items are flattened into r_items. Calculate their absolute z index to use when rendering r_items. @@ -229,10 +230,13 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item * ci->visibility_notifier->visible_in_frame = RSG::rasterizer->get_frame_number(); } + } else if (ci->repeat_source) { + // If repeat source does not draw itself it still needs transform updated as its child items' repeat offsets are relative to it. + ci->final_transform = p_transform; } } -void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times) { +void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times, RendererCanvasRender::Item *p_repeat_source_item) { Item *ci = p_canvas_item; if (!ci->visible) { @@ -268,29 +272,45 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 Point2 repeat_size = p_repeat_size; int repeat_times = p_repeat_times; + RendererCanvasRender::Item *repeat_source_item = p_repeat_source_item; if (ci->repeat_source) { repeat_size = ci->repeat_size; repeat_times = ci->repeat_times; + repeat_source_item = ci; } else { ci->repeat_size = repeat_size; ci->repeat_times = repeat_times; - } - - if (repeat_size.x || repeat_size.y) { - Size2 scale = final_xform.get_scale(); - rect.size += repeat_size * repeat_times / scale; - rect.position -= repeat_size / scale * (repeat_times / 2); + ci->repeat_source_item = repeat_source_item; } if (snapping_2d_transforms_to_pixel) { - final_xform.columns[2] = final_xform.columns[2].round(); - parent_xform.columns[2] = parent_xform.columns[2].round(); + final_xform.columns[2] = (final_xform.columns[2] + Point2(0.5, 0.5)).floor(); + parent_xform.columns[2] = (parent_xform.columns[2] + Point2(0.5, 0.5)).floor(); } final_xform = parent_xform * final_xform; Rect2 global_rect = final_xform.xform(rect); + if (repeat_source_item && (repeat_size.x || repeat_size.y)) { + // Top-left repeated rect. + Rect2 corner_rect = global_rect; + corner_rect.position -= repeat_source_item->final_transform.basis_xform((repeat_times / 2) * repeat_size); + global_rect = corner_rect; + + // Plus top-right repeated rect. + Size2 size_x_offset = repeat_source_item->final_transform.basis_xform(repeat_times * Size2(repeat_size.x, 0)); + corner_rect.position += size_x_offset; + global_rect = global_rect.merge(corner_rect); + + // Plus bottom-right repeated rect. + corner_rect.position += repeat_source_item->final_transform.basis_xform(repeat_times * Size2(0, repeat_size.y)); + global_rect = global_rect.merge(corner_rect); + + // Plus bottom-left repeated rect. + corner_rect.position -= size_x_offset; + global_rect = global_rect.merge(corner_rect); + } global_rect.position += p_clip_rect.position; if (ci->use_parent_material && p_material_owner) { @@ -357,7 +377,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 sorter.sort(child_items, child_item_count); for (i = 0; i < child_item_count; i++) { - _cull_canvas_item(child_items[i], final_xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, p_canvas_cull_mask, child_items[i]->repeat_size, child_items[i]->repeat_times); + _cull_canvas_item(child_items[i], final_xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, p_canvas_cull_mask, child_items[i]->repeat_size, child_items[i]->repeat_times, child_items[i]->repeat_source_item); } } else { RendererCanvasRender::Item *canvas_group_from = nullptr; @@ -381,14 +401,14 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2 if (!child_items[i]->behind && !use_canvas_group) { continue; } - _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times); + _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times, repeat_source_item); } _attach_canvas_item_for_draw(ci, p_canvas_clip, r_z_list, r_z_last_list, final_xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from); for (int i = 0; i < child_item_count; i++) { if (child_items[i]->behind || use_canvas_group) { continue; } - _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times); + _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times, repeat_source_item); } } } @@ -431,14 +451,22 @@ void RendererCanvasCull::canvas_set_item_mirroring(RID p_canvas, RID p_item, con int idx = canvas->find_item(canvas_item); ERR_FAIL_COND(idx == -1); - canvas->child_items.write[idx].mirror = p_mirroring; + + bool is_repeat_source = (p_mirroring.x || p_mirroring.y); + canvas_item->repeat_source = is_repeat_source; + canvas_item->repeat_source_item = is_repeat_source ? canvas_item : nullptr; + canvas_item->repeat_size = p_mirroring; + canvas_item->repeat_times = 1; } void RendererCanvasCull::canvas_set_item_repeat(RID p_item, const Point2 &p_repeat_size, int p_repeat_times) { + ERR_FAIL_COND(p_repeat_times < 0); Item *canvas_item = canvas_item_owner.get_or_null(p_item); ERR_FAIL_NULL(canvas_item); - canvas_item->repeat_source = true; + bool is_repeat_source = (p_repeat_size.x || p_repeat_size.y) && p_repeat_times; + canvas_item->repeat_source = is_repeat_source; + canvas_item->repeat_source_item = is_repeat_source ? canvas_item : nullptr; canvas_item->repeat_size = p_repeat_size; canvas_item->repeat_times = p_repeat_times; } diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h index 9f8cbea2e9..91c03054f7 100644 --- a/servers/rendering/renderer_canvas_cull.h +++ b/servers/rendering/renderer_canvas_cull.h @@ -126,7 +126,6 @@ public: struct Canvas : public RendererViewport::CanvasBase { HashSet<RID> viewports; struct ChildItem { - Point2 mirror; Item *item = nullptr; bool operator<(const ChildItem &p_item) const { return item->index < p_item.item->index; @@ -188,7 +187,7 @@ public: private: void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info = nullptr); - void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times); + void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times, RendererCanvasRender::Item *p_repeat_source_item); static constexpr int z_range = RS::CANVAS_ITEM_Z_MAX - RS::CANVAS_ITEM_Z_MIN + 1; diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h index 4a56548932..c57abee165 100644 --- a/servers/rendering/renderer_canvas_render.h +++ b/servers/rendering/renderer_canvas_render.h @@ -364,6 +364,7 @@ public: bool repeat_source; Point2 repeat_size; int repeat_times = 1; + Item *repeat_source_item = nullptr; Rect2 global_rect_cache; @@ -545,8 +546,13 @@ public: virtual void set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) = 0; - RendererCanvasRender() { singleton = this; } - virtual ~RendererCanvasRender() {} + RendererCanvasRender() { + ERR_FAIL_COND_MSG(singleton != nullptr, "A RendererCanvasRender singleton already exists."); + singleton = this; + } + virtual ~RendererCanvasRender() { + singleton = nullptr; + } }; #endif // RENDERER_CANVAS_RENDER_H diff --git a/servers/rendering/renderer_compositor.cpp b/servers/rendering/renderer_compositor.cpp index d364de5633..428cecead1 100644 --- a/servers/rendering/renderer_compositor.cpp +++ b/servers/rendering/renderer_compositor.cpp @@ -47,6 +47,7 @@ bool RendererCompositor::is_xr_enabled() const { } RendererCompositor::RendererCompositor() { + ERR_FAIL_COND_MSG(singleton != nullptr, "A RendererCompositor singleton already exists."); singleton = this; #ifndef _3D_DISABLED @@ -57,3 +58,7 @@ RendererCompositor::RendererCompositor() { } #endif // _3D_DISABLED } + +RendererCompositor::~RendererCompositor() { + singleton = nullptr; +} diff --git a/servers/rendering/renderer_compositor.h b/servers/rendering/renderer_compositor.h index 3c49e31516..933b9e457f 100644 --- a/servers/rendering/renderer_compositor.h +++ b/servers/rendering/renderer_compositor.h @@ -98,7 +98,7 @@ public: virtual void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) = 0; - virtual void end_viewport(bool p_swap_buffers) = 0; + virtual void gl_end_frame(bool p_swap_buffers) = 0; virtual void end_frame(bool p_swap_buffers) = 0; virtual void finalize() = 0; virtual uint64_t get_frame_number() const = 0; @@ -110,7 +110,7 @@ public: static RendererCompositor *get_singleton() { return singleton; } RendererCompositor(); - virtual ~RendererCompositor() {} + virtual ~RendererCompositor(); }; #endif // RENDERER_COMPOSITOR_H diff --git a/servers/rendering/renderer_rd/cluster_builder_rd.h b/servers/rendering/renderer_rd/cluster_builder_rd.h index 3ca7af70ca..1badce2b81 100644 --- a/servers/rendering/renderer_rd/cluster_builder_rd.h +++ b/servers/rendering/renderer_rd/cluster_builder_rd.h @@ -185,7 +185,14 @@ private: }; uint32_t cluster_size = 32; +#if defined(MACOS_ENABLED) || defined(IOS_ENABLED) + // Results in visual artifacts on macOS and iOS when using MSAA and subgroups. + // Using subgroups and disabling MSAA is the optimal solution for now and also works + // with MoltenVK. + bool use_msaa = false; +#else bool use_msaa = true; +#endif Divisor divisor = DIVISOR_4; Size2i screen_size; diff --git a/servers/rendering/renderer_rd/effects/fsr2.cpp b/servers/rendering/renderer_rd/effects/fsr2.cpp index 925352a7d1..551ea5dd97 100644 --- a/servers/rendering/renderer_rd/effects/fsr2.cpp +++ b/servers/rendering/renderer_rd/effects/fsr2.cpp @@ -800,9 +800,6 @@ FSR2Effect::~FSR2Effect() { RD::get_singleton()->free(device.linear_clamp_sampler); for (uint32_t i = 0; i < FFX_FSR2_PASS_COUNT; i++) { - if (device.passes[i].pipeline.pipeline_rid.is_valid()) { - RD::get_singleton()->free(device.passes[i].pipeline.pipeline_rid); - } device.passes[i].shader->version_free(device.passes[i].shader_version); } } diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index c7752f8a86..12ff28d7b0 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -1790,6 +1790,10 @@ void GI::SDFGI::debug_probes(RID p_framebuffer, const uint32_t p_view_count, con } void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_render_data) { + if (p_render_data->sdfgi_update_data == nullptr) { + return; + } + RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); /* Update general SDFGI Buffer */ diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index b5d31f5414..5dc67725f1 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -1348,7 +1348,7 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, Basis local_view = Basis::looking_at(view_normals[i], view_up[i]); RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd, p_render_buffers); - cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, Rect2(), RDD::BreadcrumbMarker::SKY_PASS | uint32_t(i)); _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, cm, local_view, p_global_pos, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index aae32f0b3e..36bd22b723 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -587,7 +587,7 @@ void RenderForwardClustered::_render_list_with_draw_list(RenderListParameters *p RD::get_singleton()->draw_list_end(); } -void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_opaque_render_buffers, bool p_apply_alpha_multiplier, bool p_pancake_shadows, int p_index) { +void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, const Color &p_default_bg_color, bool p_opaque_render_buffers, bool p_apply_alpha_multiplier, bool p_pancake_shadows, int p_index) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); Ref<RenderSceneBuffersRD> rd = p_render_data->render_buffers; @@ -603,7 +603,7 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat } } - p_render_data->scene_data->update_ubo(scene_state.uniform_buffers[p_index], get_debug_draw_mode(), env, reflection_probe_instance, p_render_data->camera_attributes, p_flip_y, p_pancake_shadows, p_screen_size, p_default_bg_color, _render_buffers_get_luminance_multiplier(), p_opaque_render_buffers, p_apply_alpha_multiplier); + p_render_data->scene_data->update_ubo(scene_state.uniform_buffers[p_index], get_debug_draw_mode(), env, reflection_probe_instance, p_render_data->camera_attributes, p_pancake_shadows, p_screen_size, p_default_bg_color, _render_buffers_get_luminance_multiplier(), p_opaque_render_buffers, p_apply_alpha_multiplier); // now do implementation UBO @@ -1103,9 +1103,17 @@ void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data, RID lightmap = light_storage->lightmap_instance_get_lightmap(p_lightmaps[i]); + // Transform (for directional lightmaps). Basis to_lm = light_storage->lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis; to_lm = to_lm.inverse().transposed(); //will transform normals RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform); + + // Light texture size. + Vector2i lightmap_size = light_storage->lightmap_get_light_texture_size(lightmap); + scene_state.lightmaps[i].texture_size[0] = lightmap_size[0]; + scene_state.lightmaps[i].texture_size[1] = lightmap_size[1]; + + // Exposure. scene_state.lightmaps[i].exposure_normalization = 1.0; if (p_render_data->camera_attributes.is_valid()) { float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap); @@ -1126,6 +1134,10 @@ void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data, /* SDFGI */ void RenderForwardClustered::_update_sdfgi(RenderDataRD *p_render_data) { + if (p_render_data->sdfgi_update_data == nullptr) { + return; + } + Ref<RenderSceneBuffersRD> rb; if (p_render_data && p_render_data->render_buffers.is_valid()) { rb = p_render_data->render_buffers; @@ -1732,7 +1744,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co _setup_lightmaps(p_render_data, *p_render_data->lightmaps, p_render_data->scene_data->cam_transform); _setup_voxelgis(*p_render_data->voxel_gi_instances); - _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); + _setup_environment(p_render_data, is_reflection_probe, screen_size, p_default_bg_color, false); // May have changed due to the above (light buffer enlarged, as an example). _update_render_base_uniform_set(); @@ -1745,7 +1757,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co int *render_info = p_render_data->render_info ? p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE] : (int *)nullptr; _fill_instance_data(RENDER_LIST_OPAQUE, render_info); _fill_instance_data(RENDER_LIST_MOTION, render_info); - _fill_instance_data(RENDER_LIST_ALPHA); + _fill_instance_data(RENDER_LIST_ALPHA, render_info); RD::get_singleton()->draw_command_end_label(); @@ -1823,7 +1835,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - if (!p_render_data->transparent_bg && rb->has_custom_data(RB_SCOPE_FOG) && environment_get_fog_enabled(p_render_data->environment)) { + if (!p_render_data->transparent_bg && (rb->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment))) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } @@ -1833,7 +1845,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co clear_color.r *= bg_energy_multiplier; clear_color.g *= bg_energy_multiplier; clear_color.b *= bg_energy_multiplier; - if (!p_render_data->transparent_bg && rb->has_custom_data(RB_SCOPE_FOG) && environment_get_fog_enabled(p_render_data->environment)) { + if (!p_render_data->transparent_bg && (rb->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment))) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); } @@ -1995,7 +2007,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co // Shadow pass can change the base uniform set samplers. _update_render_base_uniform_set(); - _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, true, using_motion_pass); + _setup_environment(p_render_data, is_reflection_probe, screen_size, p_default_bg_color, true, using_motion_pass); RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, p_render_data, radiance_texture, samplers, true); @@ -2209,7 +2221,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, samplers, true); - _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); + _setup_environment(p_render_data, is_reflection_probe, screen_size, p_default_bg_color, false); { uint32_t transparent_color_pass_flags = (color_pass_flags | COLOR_PASS_FLAG_TRANSPARENT) & ~(COLOR_PASS_FLAG_SEPARATE_SPECULAR); @@ -2219,7 +2231,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } RID alpha_framebuffer = rb_data.is_valid() ? rb_data->get_color_pass_fb(transparent_color_pass_flags) : color_only_framebuffer; - RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), false, PASS_MODE_COLOR, transparent_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, spec_constant_base_flags); + RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, transparent_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, spec_constant_base_flags); _render_list_with_draw_list(&render_list_params, alpha_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); } @@ -2562,6 +2574,7 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page SceneState::ShadowPass shadow_pass; RenderSceneDataRD scene_data; + scene_data.flip_y = !p_flip_y; // Q: Why is this inverted? Do we assume flip in shadow logic? scene_data.cam_projection = p_projection; scene_data.cam_transform = p_transform; scene_data.view_projection[0] = p_projection; @@ -2581,7 +2594,7 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page render_data.instances = &p_instances; render_data.render_info = p_render_info; - _setup_environment(&render_data, true, p_viewport_size, !p_flip_y, Color(), false, false, p_use_pancake, shadow_pass_index); + _setup_environment(&render_data, true, p_viewport_size, Color(), false, false, p_use_pancake, shadow_pass_index); if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) { scene_data.screen_mesh_lod_threshold = 0.0; @@ -2654,6 +2667,7 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con RD::get_singleton()->draw_command_begin_label("Render Collider Heightfield"); RenderSceneDataRD scene_data; + scene_data.flip_y = true; scene_data.cam_projection = p_cam_projection; scene_data.cam_transform = p_cam_transform; scene_data.view_projection[0] = p_cam_projection; @@ -2673,7 +2687,7 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con _update_render_base_uniform_set(); - _setup_environment(&render_data, true, Vector2(1, 1), true, Color(), false, false, false); + _setup_environment(&render_data, true, Vector2(1, 1), Color(), false, false, false); PassMode pass_mode = PASS_MODE_SHADOW; @@ -2720,7 +2734,7 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform _update_render_base_uniform_set(); - _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); + _setup_environment(&render_data, true, Vector2(1, 1), Color()); PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL; _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); @@ -2771,7 +2785,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance _update_render_base_uniform_set(); - _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); + _setup_environment(&render_data, true, Vector2(1, 1), Color()); PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL; _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); @@ -2887,7 +2901,7 @@ void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_bu RendererRD::MaterialStorage::store_transform(to_bounds.affine_inverse() * scene_data.cam_transform, scene_state.ubo.sdf_to_bounds); scene_data.emissive_exposure_normalization = p_exposure_normalization; - _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); + _setup_environment(&render_data, true, Vector2(1, 1), Color()); RID rp_uniform_set = _setup_sdfgi_render_pass_uniform_set(p_albedo_texture, p_emission_texture, p_emission_aniso_texture, p_geom_facing_texture, RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default()); @@ -3585,22 +3599,31 @@ RID RenderForwardClustered::_render_buffers_get_velocity_texture(Ref<RenderScene } void RenderForwardClustered::environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) { + ERR_FAIL_NULL(ss_effects); + ERR_FAIL_COND(p_quality < RS::EnvironmentSSAOQuality::ENV_SSAO_QUALITY_VERY_LOW || p_quality > RS::EnvironmentSSAOQuality::ENV_SSAO_QUALITY_ULTRA); ss_effects->ssao_set_quality(p_quality, p_half_size, p_adaptive_target, p_blur_passes, p_fadeout_from, p_fadeout_to); } void RenderForwardClustered::environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) { + ERR_FAIL_NULL(ss_effects); + ERR_FAIL_COND(p_quality < RS::EnvironmentSSILQuality::ENV_SSIL_QUALITY_VERY_LOW || p_quality > RS::EnvironmentSSILQuality::ENV_SSIL_QUALITY_ULTRA); ss_effects->ssil_set_quality(p_quality, p_half_size, p_adaptive_target, p_blur_passes, p_fadeout_from, p_fadeout_to); } void RenderForwardClustered::environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) { + ERR_FAIL_NULL(ss_effects); + ERR_FAIL_COND(p_quality < RS::EnvironmentSSRRoughnessQuality::ENV_SSR_ROUGHNESS_QUALITY_DISABLED || p_quality > RS::EnvironmentSSRRoughnessQuality::ENV_SSR_ROUGHNESS_QUALITY_HIGH); ss_effects->ssr_set_roughness_quality(p_quality); } void RenderForwardClustered::sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) { + ERR_FAIL_NULL(ss_effects); + ERR_FAIL_COND(p_quality < RS::SubSurfaceScatteringQuality::SUB_SURFACE_SCATTERING_QUALITY_DISABLED || p_quality > RS::SubSurfaceScatteringQuality::SUB_SURFACE_SCATTERING_QUALITY_HIGH); ss_effects->sss_set_quality(p_quality); } void RenderForwardClustered::sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) { + ERR_FAIL_NULL(ss_effects); ss_effects->sss_set_scale(p_scale, p_depth_scale); } @@ -4227,6 +4250,11 @@ void RenderForwardClustered::_update_shader_quality_settings() { spec_constants.push_back(sc); + sc.constant_id = SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER; + sc.bool_value = lightmap_filter_bicubic_get(); + + spec_constants.push_back(sc); + scene_shader.set_default_specialization_constants(spec_constants); base_uniforms_changed(); //also need this diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index ae9e5e7c10..5d14653db6 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -73,6 +73,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { SPEC_CONSTANT_DECAL_FILTER = 10, SPEC_CONSTANT_PROJECTOR_FILTER = 11, SPEC_CONSTANT_USE_DEPTH_FOG = 12, + SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER = 13, }; enum { @@ -235,8 +236,9 @@ class RenderForwardClustered : public RendererSceneRenderRD { struct LightmapData { float normal_xform[12]; - float pad[3]; + float texture_size[2]; float exposure_normalization; + float pad; }; struct LightmapCaptureData { @@ -361,7 +363,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { static RenderForwardClustered *singleton; - void _setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_opaque_render_buffers = false, bool p_apply_alpha_multiplier = false, bool p_pancake_shadows = false, int p_index = 0); + void _setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, const Color &p_default_bg_color, bool p_opaque_render_buffers = false, bool p_apply_alpha_multiplier = false, bool p_pancake_shadows = false, int p_index = 0); void _setup_voxelgis(const PagedArray<RID> &p_voxelgis); void _setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform); diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index 9e0dacc1f2..6846c3f693 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -636,11 +636,12 @@ void SceneShaderForwardClustered::init(const String p_defines) { actions.renames["CUSTOM2"] = "custom2_attrib"; actions.renames["CUSTOM3"] = "custom3_attrib"; actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; + actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR"; actions.renames["LIGHT_VERTEX"] = "light_vertex"; actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz"; actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz"; - actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.view_matrix[3].xyz"; + actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.inv_view_matrix[2].xyz"; actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers"; actions.renames["NODE_POSITION_VIEW"] = "(scene_data.view_matrix * read_model_matrix)[3].xyz"; diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 194a70dc22..48c226133d 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -563,9 +563,17 @@ void RenderForwardMobile::_setup_lightmaps(const RenderDataRD *p_render_data, co RID lightmap = light_storage->lightmap_instance_get_lightmap(p_lightmaps[i]); + // Transform (for directional lightmaps). Basis to_lm = light_storage->lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis; to_lm = to_lm.inverse().transposed(); //will transform normals RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform); + + // Light texture size. + Vector2i lightmap_size = light_storage->lightmap_get_light_texture_size(lightmap); + scene_state.lightmaps[i].texture_size[0] = lightmap_size[0]; + scene_state.lightmaps[i].texture_size[1] = lightmap_size[1]; + + // Exposure. scene_state.lightmaps[i].exposure_normalization = 1.0; if (p_render_data->camera_attributes.is_valid()) { float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap); @@ -804,7 +812,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RD::get_singleton()->draw_command_begin_label("Render Setup"); _setup_lightmaps(p_render_data, *p_render_data->lightmaps, p_render_data->scene_data->cam_transform); - _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); + _setup_environment(p_render_data, is_reflection_probe, screen_size, p_default_bg_color, false); // May have changed due to the above (light buffer enlarged, as an example). _update_render_base_uniform_set(); @@ -939,10 +947,13 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } { + RDD::BreadcrumbMarker breadcrumb; if (rb_data.is_valid()) { RD::get_singleton()->draw_command_begin_label("Render 3D Pass"); + breadcrumb = RDD::BreadcrumbMarker::OPAQUE_PASS; } else { RD::get_singleton()->draw_command_begin_label("Render Reflection Probe Pass"); + breadcrumb = RDD::BreadcrumbMarker::REFLECTION_PROBES; } // opaque pass @@ -953,7 +964,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color // Shadow pass can change the base uniform set samplers. _update_render_base_uniform_set(); - _setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, p_render_data->render_buffers.is_valid()); + _setup_environment(p_render_data, is_reflection_probe, screen_size, p_default_bg_color, p_render_data->render_buffers.is_valid()); if (merge_transparent_pass && using_subpass_post_process) { RENDER_TIMESTAMP("Render Opaque + Transparent + Tonemap"); @@ -984,7 +995,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } } - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 0.0, 0); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 0.0, 0, Rect2(), breadcrumb); RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); if (copy_canvas) { @@ -1018,11 +1029,6 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RD::get_singleton()->draw_command_end_label(); // Draw Sky } - // rendering effects - if (ce_has_pre_transparent) { - _process_compositor_effects(RS::COMPOSITOR_EFFECT_CALLBACK_TYPE_PRE_TRANSPARENT, p_render_data); - } - if (merge_transparent_pass) { if (render_list[RENDER_LIST_ALPHA].element_info.size() > 0) { // transparent pass @@ -1058,6 +1064,11 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RD::get_singleton()->draw_command_end_label(); // Render 3D Pass / Render Reflection Probe Pass + // rendering effects + if (ce_has_pre_transparent) { + _process_compositor_effects(RS::COMPOSITOR_EFFECT_CALLBACK_TYPE_PRE_TRANSPARENT, p_render_data); + } + if (scene_state.used_screen_texture) { // Copy screen texture to backbuffer so we can read from it _render_buffers_copy_screen_texture(p_render_data); @@ -1075,13 +1086,13 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_ALPHA, p_render_data, radiance_texture, samplers, true); // this may be needed if we re-introduced steps that change info, not sure which do so in the previous implementation - //_setup_environment(p_render_data, is_reflection_probe, screen_size, !is_reflection_probe, p_default_bg_color, false); + //_setup_environment(p_render_data, is_reflection_probe, screen_size, p_default_bg_color, false); RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, spec_constant_base_flags, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); render_list_params.framebuffer_format = fb_format; render_list_params.subpass = RD::get_singleton()->draw_list_get_current_pass(); // Should now always be 0. - draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); + draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, Vector<Color>(), 0, 0, Rect2(), breadcrumb); _render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count); RD::get_singleton()->draw_list_end(); @@ -1310,6 +1321,7 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr } RenderSceneDataRD scene_data; + scene_data.flip_y = !p_flip_y; // Q: Why is this inverted? Do we assume flip in shadow logic? scene_data.cam_projection = p_projection; scene_data.cam_transform = p_transform; scene_data.view_projection[0] = p_projection; @@ -1327,7 +1339,7 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr render_data.instances = &p_instances; render_data.render_info = p_render_info; - _setup_environment(&render_data, true, Vector2(1, 1), !p_flip_y, Color(), false, p_use_pancake, shadow_pass_index); + _setup_environment(&render_data, true, Vector2(1, 1), Color(), false, p_use_pancake, shadow_pass_index); if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) { scene_data.screen_mesh_lod_threshold = 0.0; @@ -1415,7 +1427,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c render_data.scene_data = &scene_data; render_data.instances = &p_instances; - _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); + _setup_environment(&render_data, true, Vector2(1, 1), Color()); PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL; _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); @@ -1460,7 +1472,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *> render_data.scene_data = &scene_data; render_data.instances = &p_instances; - _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); + _setup_environment(&render_data, true, Vector2(1, 1), Color()); PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL; _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode); @@ -1526,6 +1538,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const _update_render_base_uniform_set(); RenderSceneDataRD scene_data; + scene_data.flip_y = true; scene_data.cam_projection = p_cam_projection; scene_data.cam_transform = p_cam_transform; scene_data.view_projection[0] = p_cam_projection; @@ -1541,7 +1554,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const render_data.scene_data = &scene_data; render_data.instances = &p_instances; - _setup_environment(&render_data, true, Vector2(1, 1), true, Color(), false, false); + _setup_environment(&render_data, true, Vector2(1, 1), Color(), false, false); PassMode pass_mode = PASS_MODE_SHADOW; @@ -1974,7 +1987,7 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const } } -void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_opaque_render_buffers, bool p_pancake_shadows, int p_index) { +void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, const Color &p_default_bg_color, bool p_opaque_render_buffers, bool p_pancake_shadows, int p_index) { RID env = is_environment(p_render_data->environment) ? p_render_data->environment : RID(); RID reflection_probe_instance = p_render_data->reflection_probe.is_valid() ? RendererRD::LightStorage::get_singleton()->reflection_probe_instance_get_probe(p_render_data->reflection_probe) : RID(); @@ -1987,7 +2000,7 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, } } - p_render_data->scene_data->update_ubo(scene_state.uniform_buffers[p_index], get_debug_draw_mode(), env, reflection_probe_instance, p_render_data->camera_attributes, p_flip_y, p_pancake_shadows, p_screen_size, p_default_bg_color, _render_buffers_get_luminance_multiplier(), p_opaque_render_buffers, false); + p_render_data->scene_data->update_ubo(scene_state.uniform_buffers[p_index], get_debug_draw_mode(), env, reflection_probe_instance, p_render_data->camera_attributes, p_pancake_shadows, p_screen_size, p_default_bg_color, _render_buffers_get_luminance_multiplier(), p_opaque_render_buffers, false); } /// RENDERING /// @@ -2056,6 +2069,10 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr uint32_t base_spec_constants = p_params->spec_constant_base_flags; + if (bool(inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH)) { + base_spec_constants |= 1 << SPEC_CONSTANT_IS_MULTIMESH; + } + SceneState::PushConstant push_constant; push_constant.base_index = i + p_params->element_offset; @@ -2167,9 +2184,7 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr } break; } - PipelineCacheRD *pipeline = nullptr; - - pipeline = &shader->pipelines[cull_variant][primitive][shader_version]; + PipelineCacheRD *pipeline = &shader->pipelines[cull_variant][primitive][shader_version]; RD::VertexFormatID vertex_format = -1; RID vertex_array_rd; @@ -2199,8 +2214,6 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr RID pipeline_rd = pipeline->get_render_pipeline(vertex_format, framebuffer_format, p_params->force_wireframe, p_params->subpass, base_spec_constants); if (pipeline_rd != prev_pipeline_rd) { - // checking with prev shader does not make so much sense, as - // the pipeline may still be different. RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline_rd); prev_pipeline_rd = pipeline_rd; } @@ -2775,6 +2788,11 @@ void RenderForwardMobile::_update_shader_quality_settings() { spec_constants.push_back(sc); + sc.constant_id = SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER; + sc.bool_value = lightmap_filter_bicubic_get(); + + spec_constants.push_back(sc); + scene_shader.set_default_specialization_constants(spec_constants); base_uniforms_changed(); //also need this diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index aa1b8f34b2..fc60c770e8 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -81,7 +81,8 @@ private: SPEC_CONSTANT_DISABLE_DECALS = 13, SPEC_CONSTANT_DISABLE_FOG = 14, SPEC_CONSTANT_USE_DEPTH_FOG = 16, - + SPEC_CONSTANT_IS_MULTIMESH = 17, + SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER = 18, }; enum { @@ -197,7 +198,7 @@ private: void _fill_instance_data(RenderListType p_render_list, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true); void _fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, bool p_append = false); - void _setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_opaque_render_buffers = false, bool p_pancake_shadows = false, int p_index = 0); + void _setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, const Color &p_default_bg_color, bool p_opaque_render_buffers = false, bool p_pancake_shadows = false, int p_index = 0); void _setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform); RID render_base_uniform_set; @@ -207,8 +208,9 @@ private: struct LightmapData { float normal_xform[12]; - float pad[3]; + float texture_size[2]; float exposure_normalization; + float pad; }; struct LightmapCaptureData { diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index dd722cc2dd..08982096c5 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -540,11 +540,12 @@ void SceneShaderForwardMobile::init(const String p_defines) { actions.renames["CUSTOM2"] = "custom2_attrib"; actions.renames["CUSTOM3"] = "custom3_attrib"; actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; + actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR"; actions.renames["LIGHT_VERTEX"] = "light_vertex"; actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz"; actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz"; - actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.view_matrix[3].xyz"; + actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.inv_view_matrix[2].xyz"; actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers"; actions.renames["NODE_POSITION_VIEW"] = "(scene_data.view_matrix * read_model_matrix)[3].xyz"; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index fa8cf9c028..f51b4ae8d0 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -407,7 +407,7 @@ _FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primit return (p_indices - subtractor[p_primitive]) / divisor[p_primitive]; } -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, bool &r_sdf_used, const Point2 &p_offset, RenderingMethod::RenderInfo *r_render_info) { +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, bool &r_sdf_used, const Point2 &p_repeat_offset, RenderingMethod::RenderInfo *r_render_info) { //create an empty push constant RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); @@ -425,11 +425,11 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend } PushConstant push_constant; - Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform; - - if (p_offset.x || p_offset.y) { - base_transform *= Transform2D(0, p_offset / p_item->xform_curr.get_scale()); // TODO: Interpolate or explain why not needed. + Transform2D base_transform = p_item->final_transform; + if (p_item->repeat_source_item && (p_repeat_offset.x || p_repeat_offset.y)) { + base_transform.columns[2] += p_item->repeat_source_item->final_transform.basis_xform(p_repeat_offset); } + base_transform = p_canvas_transform_inverse * base_transform; Transform2D draw_transform; _update_transform_2d_to_mat2x3(base_transform, push_constant.world); @@ -509,11 +509,16 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend current_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; } + Color modulated = rect->modulate * base_color; + if (use_linear_colors) { + modulated = modulated.srgb_to_linear(); + } + //bind pipeline if (rect->flags & CANVAS_RECT_LCD) { RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_QUAD_LCD_BLEND].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); - RD::get_singleton()->draw_list_set_blend_constants(p_draw_list, rect->modulate); + RD::get_singleton()->draw_list_set_blend_constants(p_draw_list, modulated); } else { RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_QUAD].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); @@ -582,11 +587,6 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend push_constant.flags |= FLAGS_USE_LCD; } - Color modulated = rect->modulate * base_color; - if (use_linear_colors) { - modulated = modulated.srgb_to_linear(); - } - push_constant.modulation[0] = modulated.r; push_constant.modulation[1] = modulated.g; push_constant.modulation[2] = modulated.b; @@ -1183,7 +1183,7 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors, 1, 0, Rect2(), RDD::BreadcrumbMarker::UI_PASS); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, fb_uniform_set, BASE_UNIFORM_SET); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, state.default_transforms_uniform_set, TRANSFORMS_UNIFORM_SET); @@ -1250,18 +1250,17 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, Point2(), r_render_info); } else { Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2); - Point2 end_pos = ci->repeat_size * ci->repeat_times + ci->repeat_size + start_pos; - Point2 pos = start_pos; - - do { - do { - _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, pos, r_render_info); - pos.y += ci->repeat_size.y; - } while (pos.y < end_pos.y); - - pos.x += ci->repeat_size.x; - pos.y = start_pos.y; - } while (pos.x < end_pos.x); + Point2 offset; + + int repeat_times_x = ci->repeat_size.x ? ci->repeat_times : 0; + int repeat_times_y = ci->repeat_size.y ? ci->repeat_times : 0; + for (int ry = 0; ry <= repeat_times_y; ry++) { + offset.y = start_pos.y + ry * ci->repeat_size.y; + for (int rx = 0; rx <= repeat_times_x; rx++) { + offset.x = start_pos.x + rx * ci->repeat_size.x; + _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, offset, r_render_info); + } + } } prev_material = material; @@ -1451,10 +1450,15 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p normal_transform.columns[2] = Vector2(); _update_transform_2d_to_mat4(normal_transform, state_buffer.canvas_normal_transform); - state_buffer.canvas_modulate[0] = p_modulate.r; - state_buffer.canvas_modulate[1] = p_modulate.g; - state_buffer.canvas_modulate[2] = p_modulate.b; - state_buffer.canvas_modulate[3] = p_modulate.a; + bool use_linear_colors = texture_storage->render_target_is_using_hdr(p_to_render_target); + Color modulate = p_modulate; + if (use_linear_colors) { + modulate = p_modulate.srgb_to_linear(); + } + state_buffer.canvas_modulate[0] = modulate.r; + state_buffer.canvas_modulate[1] = modulate.g; + state_buffer.canvas_modulate[2] = modulate.b; + state_buffer.canvas_modulate[3] = modulate.a; Size2 render_target_size = texture_storage->render_target_get_size(p_to_render_target); state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x; @@ -1734,6 +1738,7 @@ void RendererCanvasRenderRD::_update_shadow_atlas() { state.shadow_fb = RD::get_singleton()->framebuffer_create(fb_textures); } } + void RendererCanvasRenderRD::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) { CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); ERR_FAIL_COND(!cl->shadow.enabled); @@ -1819,7 +1824,7 @@ void RendererCanvasRenderRD::light_update_directional_shadow(RID p_rid, int p_sh Vector2 center = p_clip_rect.get_center(); - float to_edge_distance = ABS(light_dir.dot(p_clip_rect.get_support(light_dir)) - light_dir.dot(center)); + float to_edge_distance = ABS(light_dir.dot(p_clip_rect.get_support(-light_dir)) - light_dir.dot(center)); Vector2 from_pos = center - light_dir * (to_edge_distance + p_cull_distance); float distance = to_edge_distance * 2.0 + p_cull_distance; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index c7c5d34314..9deb4814c7 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -78,7 +78,6 @@ class RendererCanvasRenderRD : public RendererCanvasRender { FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR = (1 << 11), FLAGS_NINEPACH_DRAW_CENTER = (1 << 12), - FLAGS_USING_PARTICLES = (1 << 13), FLAGS_USE_SKELETON = (1 << 15), FLAGS_NINEPATCH_H_MODE_SHIFT = 16, @@ -424,7 +423,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { double debug_redraw_time = 1.0; 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, bool p_texture_is_data = false); //recursive, so regular inline used instead. - 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, bool &r_sdf_used, const Point2 &p_offset, RenderingMethod::RenderInfo *r_render_info = nullptr); + 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, bool &r_sdf_used, const Point2 &p_repeat_offset, RenderingMethod::RenderInfo *r_render_info = nullptr); void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr); _FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4); diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index 14d138181f..84ea6a5da2 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -299,6 +299,7 @@ RendererCompositorRD::RendererCompositorRD() { } } + ERR_FAIL_COND_MSG(singleton != nullptr, "A RendererCompositorRD singleton already exists."); singleton = this; utilities = memnew(RendererRD::Utilities); @@ -330,6 +331,7 @@ RendererCompositorRD::RendererCompositorRD() { } RendererCompositorRD::~RendererCompositorRD() { + singleton = nullptr; memdelete(uniform_set_cache); memdelete(framebuffer_cache); ShaderRD::set_shader_cache_dir(String()); diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h index 95c2b812d9..0222a99577 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.h +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h @@ -122,7 +122,7 @@ public: void begin_frame(double frame_step); void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount); - void end_viewport(bool p_swap_buffers) {} + void gl_end_frame(bool p_swap_buffers) {} void end_frame(bool p_swap_buffers); void finalize(); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 1e4880e67a..7d6d5018d0 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -423,6 +423,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende Size2i target_size = rb->get_target_size(); bool can_use_effects = target_size.x >= 8 && target_size.y >= 8; // FIXME I think this should check internal size, we do all our post processing at this size... + can_use_effects &= _debug_draw_can_use_effects(debug_draw); bool can_use_storage = _render_buffers_can_be_storage(); bool use_fsr = fsr && can_use_effects && rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_FSR; @@ -699,7 +700,7 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr // FIXME: Our input it our internal_texture, shouldn't this be using internal_size ?? // Seeing we don't support FSR in our mobile renderer right now target_size = internal_size... Size2i target_size = rb->get_target_size(); - bool can_use_effects = target_size.x >= 8 && target_size.y >= 8; + bool can_use_effects = target_size.x >= 8 && target_size.y >= 8 && debug_draw == RS::VIEWPORT_DEBUG_DRAW_DISABLED; RD::DrawListID draw_list = RD::get_singleton()->draw_list_switch_to_next_pass(); @@ -764,6 +765,56 @@ void RendererSceneRenderRD::_disable_clear_request(const RenderDataRD *p_render_ texture_storage->render_target_disable_clear_request(p_render_data->render_buffers->get_render_target()); } +bool RendererSceneRenderRD::_debug_draw_can_use_effects(RS::ViewportDebugDraw p_debug_draw) { + bool can_use_effects = true; + switch (p_debug_draw) { + // No debug draw, use camera effects + case RS::VIEWPORT_DEBUG_DRAW_DISABLED: + can_use_effects = true; + break; + // Modes that completely override rendering to draw debug information should disable camera effects. + case RS::VIEWPORT_DEBUG_DRAW_UNSHADED: + case RS::VIEWPORT_DEBUG_DRAW_OVERDRAW: + case RS::VIEWPORT_DEBUG_DRAW_WIREFRAME: + case RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_ALBEDO: + case RS::VIEWPORT_DEBUG_DRAW_CLUSTER_OMNI_LIGHTS: + case RS::VIEWPORT_DEBUG_DRAW_CLUSTER_SPOT_LIGHTS: + case RS::VIEWPORT_DEBUG_DRAW_CLUSTER_DECALS: + case RS::VIEWPORT_DEBUG_DRAW_CLUSTER_REFLECTION_PROBES: + case RS::VIEWPORT_DEBUG_DRAW_INTERNAL_BUFFER: + can_use_effects = false; + break; + // Modes that draws information over part of the viewport needs camera effects because we see partially the normal draw mode. + case RS::VIEWPORT_DEBUG_DRAW_SHADOW_ATLAS: + case RS::VIEWPORT_DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS: + case RS::VIEWPORT_DEBUG_DRAW_DECAL_ATLAS: + case RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS: + // Modes that draws a buffer over viewport needs camera effects because if the buffer is not available it will be equivalent to normal draw mode. + case RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER: + case RS::VIEWPORT_DEBUG_DRAW_SSAO: + case RS::VIEWPORT_DEBUG_DRAW_SSIL: + case RS::VIEWPORT_DEBUG_DRAW_SDFGI: + case RS::VIEWPORT_DEBUG_DRAW_GI_BUFFER: + case RS::VIEWPORT_DEBUG_DRAW_OCCLUDERS: + can_use_effects = true; + break; + // Other debug draw modes keep camera effects. + case RS::VIEWPORT_DEBUG_DRAW_LIGHTING: + case RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_LIGHTING: + case RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_EMISSION: + case RS::VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE: + case RS::VIEWPORT_DEBUG_DRAW_PSSM_SPLITS: + case RS::VIEWPORT_DEBUG_DRAW_SDFGI_PROBES: + case RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD: + can_use_effects = true; + break; + default: + break; + } + + return can_use_effects; +} + void RendererSceneRenderRD::_render_buffers_debug_draw(const RenderDataRD *p_render_data) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); @@ -989,6 +1040,14 @@ void RendererSceneRenderRD::light_projectors_set_filter(RenderingServer::LightPr _update_shader_quality_settings(); } +void RendererSceneRenderRD::lightmaps_set_bicubic_filter(bool p_enable) { + if (lightmap_filter_bicubic == p_enable) { + return; + } + lightmap_filter_bicubic = p_enable; + _update_shader_quality_settings(); +} + int RendererSceneRenderRD::get_roughness_layers() const { return sky.roughness_layers; } @@ -1073,6 +1132,7 @@ void RendererSceneRenderRD::render_scene(const Ref<RenderSceneBuffers> &p_render scene_data.camera_visible_layers = p_camera_data->visible_layers; scene_data.taa_jitter = p_camera_data->taa_jitter; scene_data.main_cam_transform = p_camera_data->main_transform; + scene_data.flip_y = !p_reflection_probe.is_valid(); scene_data.view_count = p_camera_data->view_count; for (uint32_t v = 0; v < p_camera_data->view_count; v++) { @@ -1431,6 +1491,7 @@ void RendererSceneRenderRD::init() { decals_set_filter(RS::DecalFilter(int(GLOBAL_GET("rendering/textures/decals/filter")))); light_projectors_set_filter(RS::LightProjectorFilter(int(GLOBAL_GET("rendering/textures/light_projectors/filter")))); + lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter")); cull_argument.set_page_pool(&cull_argument_pool); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index 5c5f11aba6..022a4560f8 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -117,6 +117,7 @@ protected: RendererRD::GI gi; virtual void _update_shader_quality_settings() {} + static bool _debug_draw_can_use_effects(RS::ViewportDebugDraw p_debug_draw); private: RS::ViewportDebugDraw debug_draw = RS::VIEWPORT_DEBUG_DRAW_DISABLED; @@ -132,6 +133,7 @@ private: float *directional_soft_shadow_kernel = nullptr; float *penumbra_shadow_kernel = nullptr; float *soft_shadow_kernel = nullptr; + bool lightmap_filter_bicubic = false; int directional_penumbra_shadow_samples = 0; int directional_soft_shadow_samples = 0; int penumbra_shadow_samples = 0; @@ -261,6 +263,7 @@ public: virtual void decals_set_filter(RS::DecalFilter p_filter) override; virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override; + virtual void lightmaps_set_bicubic_filter(bool p_enable) override; _FORCE_INLINE_ RS::ShadowQuality shadows_quality_get() const { return shadows_quality; @@ -291,6 +294,9 @@ public: _FORCE_INLINE_ int directional_penumbra_shadow_samples_get() const { return directional_penumbra_shadow_samples; } + _FORCE_INLINE_ bool lightmap_filter_bicubic_get() const { + return lightmap_filter_bicubic; + } _FORCE_INLINE_ int directional_soft_shadow_samples_get() const { return directional_soft_shadow_samples; } diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp index 1c1b8366e5..e6a745d3b4 100644 --- a/servers/rendering/renderer_rd/shader_rd.cpp +++ b/servers/rendering/renderer_rd/shader_rd.cpp @@ -191,9 +191,14 @@ void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, c for (const KeyValue<StringName, CharString> &E : p_version->code_sections) { builder.append(String("#define ") + String(E.key) + "_CODE_USED\n"); } -#if defined(MACOS_ENABLED) || defined(IOS_ENABLED) - builder.append("#define MOLTENVK_USED\n"); +#if (defined(MACOS_ENABLED) || defined(IOS_ENABLED)) + if (RD::get_singleton()->get_device_capabilities().device_family == RDD::DEVICE_VULKAN) { + builder.append("#define MOLTENVK_USED\n"); + } + // Image atomics are supported on Metal 3.1 but no support in MoltenVK or SPIRV-Cross yet. + builder.append("#define NO_IMAGE_ATOMICS\n"); #endif + builder.append(String("#define RENDER_DRIVER_") + OS::get_singleton()->get_current_rendering_driver_name().to_upper() + "\n"); } break; case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: { diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl index dbff09c301..4426d9eb66 100644 --- a/servers/rendering/renderer_rd/shaders/canvas.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas.glsl @@ -193,13 +193,6 @@ void main() { } } -#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE) - if (bool(draw_data.flags & FLAGS_USING_PARTICLES)) { - //scale by texture size - vertex /= draw_data.color_texture_pixel_size; - } -#endif - #ifdef USE_POINT_SIZE float point_size = 1.0; #endif diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl index 7ac7cf9c07..8649f4710b 100644 --- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl @@ -14,7 +14,6 @@ #define FLAGS_TRANSPOSE_RECT (1 << 10) #define FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR (1 << 11) #define FLAGS_NINEPACH_DRAW_CENTER (1 << 12) -#define FLAGS_USING_PARTICLES (1 << 13) #define FLAGS_NINEPATCH_H_MODE_SHIFT 16 #define FLAGS_NINEPATCH_V_MODE_SHIFT 18 diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl index 2010b58474..f2b93059b0 100644 --- a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl @@ -11,9 +11,22 @@ layout(location = 0) out vec2 uv_interp; /* clang-format on */ void main() { - vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); - gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); - uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 + // old code, ARM driver bug on Mali-GXXx GPUs and Vulkan API 1.3.xxx + // https://github.com/godotengine/godot/pull/92817#issuecomment-2168625982 + //vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + //gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); + //uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 + + vec2 vertex_base; + if (gl_VertexIndex == 0) { + vertex_base = vec2(-1.0, -1.0); + } else if (gl_VertexIndex == 1) { + vertex_base = vec2(-1.0, 3.0); + } else { + vertex_base = vec2(3.0, -1.0); + } + gl_Position = vec4(vertex_base, 0.0, 1.0); + uv_interp = clamp(vertex_base, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 } /* clang-format off */ diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl index 8618f083b3..b730b2c819 100644 --- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl @@ -90,7 +90,7 @@ void main() { if (sc_multiview) { view_dir = normalize(vertex + scene_data.eye_offset[params.view_index].xyz); } else { - view_dir = normalize(vertex); + view_dir = params.orthogonal ? vec3(0.0, 0.0, -1.0) : normalize(vertex); } vec3 ray_dir = normalize(reflect(view_dir, normal)); @@ -237,7 +237,7 @@ void main() { // This is an ad-hoc term to fade out the SSR as roughness increases. Values used // are meant to match the visual appearance of a ReflectionProbe. - float roughness_fade = smoothstep(0.4, 0.7, 1.0 - normal_roughness.w); + float roughness_fade = smoothstep(0.4, 0.7, 1.0 - roughness); // Schlick term. float metallic = texelFetch(source_metallic, ssC << 1, 0).w; diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_inc.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_inc.glsl index 26405ab040..9b692824a1 100644 --- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection_inc.glsl @@ -20,7 +20,7 @@ vec3 reconstructCSPosition(vec2 screen_pos, float z) { return pos.xyz; } else { if (params.orthogonal) { - return vec3((screen_pos.xy * params.proj_info.xy + params.proj_info.zw), z); + return vec3(-(screen_pos.xy * params.proj_info.xy + params.proj_info.zw), z); } else { return vec3((screen_pos.xy * params.proj_info.xy + params.proj_info.zw) * z, z); } diff --git a/servers/rendering/renderer_rd/shaders/effects/ss_effects_downsample.glsl b/servers/rendering/renderer_rd/shaders/effects/ss_effects_downsample.glsl index 4f81e36c58..0332e23993 100644 --- a/servers/rendering/renderer_rd/shaders/effects/ss_effects_downsample.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/ss_effects_downsample.glsl @@ -50,7 +50,7 @@ layout(r16f, set = 2, binding = 3) uniform restrict writeonly image2DArray dest_ vec4 screen_space_to_view_space_depth(vec4 p_depth) { if (params.orthogonal) { vec4 depth = p_depth * 2.0 - 1.0; - return ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0; + return -(depth * (params.z_far - params.z_near) - (params.z_far + params.z_near)) / 2.0; } float depth_linearize_mul = params.z_near; @@ -68,7 +68,7 @@ vec4 screen_space_to_view_space_depth(vec4 p_depth) { float screen_space_to_view_space_depth(float p_depth) { if (params.orthogonal) { float depth = p_depth * 2.0 - 1.0; - return ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / (2.0 * params.z_far); + return -(depth * (params.z_far - params.z_near) - (params.z_far + params.z_near)) / 2.0; } float depth_linearize_mul = params.z_near; diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl index 38eec2b61a..841f02f673 100644 --- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl @@ -13,9 +13,22 @@ layout(location = 0) out vec2 uv_interp; void main() { - vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); - gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); - uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 + // old code, ARM driver bug on Mali-GXXx GPUs and Vulkan API 1.3.xxx + // https://github.com/godotengine/godot/pull/92817#issuecomment-2168625982 + //vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + //gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); + //uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 + + vec2 vertex_base; + if (gl_VertexIndex == 0) { + vertex_base = vec2(-1.0, -1.0); + } else if (gl_VertexIndex == 1) { + vertex_base = vec2(-1.0, 3.0); + } else { + vertex_base = vec2(3.0, -1.0); + } + gl_Position = vec4(vertex_base, 0.0, 1.0); + uv_interp = clamp(vertex_base, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 } #[fragment] diff --git a/servers/rendering/renderer_rd/shaders/environment/sky.glsl b/servers/rendering/renderer_rd/shaders/environment/sky.glsl index 35457a2482..5aa3735494 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sky.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sky.glsl @@ -255,10 +255,6 @@ void main() { frag_color.rgb = color; frag_color.a = alpha; - // For mobile renderer we're multiplying by 0.5 as we're using a UNORM buffer. - // For both mobile and clustered, we also bake in the exposure value for the environment and camera. - frag_color.rgb = frag_color.rgb * params.luminance_multiplier; - #if !defined(DISABLE_FOG) && !defined(USE_CUBEMAP_PASS) // Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky. @@ -278,6 +274,10 @@ void main() { #endif // DISABLE_FOG + // For mobile renderer we're multiplying by 0.5 as we're using a UNORM buffer. + // For both mobile and clustered, we also bake in the exposure value for the environment and camera. + frag_color.rgb = frag_color.rgb * params.luminance_multiplier; + // Blending is disabled for Sky, so alpha doesn't blend. // Alpha is used for subsurface scattering so make sure it doesn't get applied to Sky. if (!AT_CUBEMAP_PASS && !AT_HALF_RES_PASS && !AT_QUARTER_RES_PASS) { diff --git a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog.glsl b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog.glsl index 6ba1fe2819..2360375c23 100644 --- a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog.glsl @@ -34,7 +34,7 @@ layout(push_constant, std430) uniform Params { } params; -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS layout(set = 1, binding = 1) volatile buffer emissive_only_map_buffer { uint emissive_only_map[]; }; @@ -64,7 +64,7 @@ layout(set = 1, binding = 2, std140) uniform SceneParams { } scene_params; -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS layout(set = 1, binding = 3) volatile buffer density_only_map_buffer { uint density_only_map[]; }; @@ -117,7 +117,7 @@ void main() { if (any(greaterThanEqual(pos, scene_params.fog_volume_size))) { return; //do not compute } -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS uint lpos = pos.z * scene_params.fog_volume_size.x * scene_params.fog_volume_size.y + pos.y * scene_params.fog_volume_size.x + pos.x; #endif @@ -222,7 +222,7 @@ void main() { density *= cull_mask; if (abs(density) > 0.001) { int final_density = int(density * DENSITY_SCALE); -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS atomicAdd(density_only_map[lpos], uint(final_density)); #else imageAtomicAdd(density_only_map, pos, uint(final_density)); @@ -236,7 +236,7 @@ void main() { uvec3 emission_u = uvec3(emission.r * 511.0, emission.g * 511.0, emission.b * 255.0); // R and G have 11 bits each and B has 10. Then pack them into a 32 bit uint uint final_emission = emission_u.r << 21 | emission_u.g << 10 | emission_u.b; -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS uint prev_emission = atomicAdd(emissive_only_map[lpos], final_emission); #else uint prev_emission = imageAtomicAdd(emissive_only_map, pos, final_emission); @@ -252,7 +252,7 @@ void main() { if (any(overflowing)) { uvec3 overflow_factor = mix(uvec3(0), uvec3(2047 << 21, 2047 << 10, 1023), overflowing); uint force_max = overflow_factor.r | overflow_factor.g | overflow_factor.b; -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS atomicOr(emissive_only_map[lpos], force_max); #else imageAtomicOr(emissive_only_map, pos, force_max); @@ -267,7 +267,7 @@ void main() { uvec3 scattering_u = uvec3(scattering.r * 2047.0, scattering.g * 2047.0, scattering.b * 1023.0); // R and G have 11 bits each and B has 10. Then pack them into a 32 bit uint uint final_scattering = scattering_u.r << 21 | scattering_u.g << 10 | scattering_u.b; -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS uint prev_scattering = atomicAdd(light_only_map[lpos], final_scattering); #else uint prev_scattering = imageAtomicAdd(light_only_map, pos, final_scattering); @@ -283,7 +283,7 @@ void main() { if (any(overflowing)) { uvec3 overflow_factor = mix(uvec3(0), uvec3(2047 << 21, 2047 << 10, 1023), overflowing); uint force_max = overflow_factor.r | overflow_factor.g | overflow_factor.b; -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS atomicOr(light_only_map[lpos], force_max); #else imageAtomicOr(light_only_map, pos, force_max); diff --git a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl index d0cfe6a3b8..a797891ab6 100644 --- a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl @@ -190,7 +190,7 @@ params; #ifndef MODE_COPY layout(set = 0, binding = 15) uniform texture3D prev_density_texture; -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS layout(set = 0, binding = 16) buffer density_only_map_buffer { uint density_only_map[]; }; @@ -287,7 +287,7 @@ void main() { if (any(greaterThanEqual(pos, params.fog_volume_size))) { return; //do not compute } -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS uint lpos = pos.z * params.fog_volume_size.x * params.fog_volume_size.y + pos.y * params.fog_volume_size.x + pos.x; #endif @@ -353,7 +353,7 @@ void main() { vec3 total_light = vec3(0.0); float total_density = params.base_density; -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS uint local_density = density_only_map[lpos]; #else uint local_density = imageLoad(density_only_map, pos).x; @@ -362,7 +362,7 @@ void main() { total_density += float(int(local_density)) / DENSITY_SCALE; total_density = max(0.0, total_density); -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS uint scattering_u = light_only_map[lpos]; #else uint scattering_u = imageLoad(light_only_map, pos).x; @@ -370,7 +370,7 @@ void main() { vec3 scattering = vec3(scattering_u >> 21, (scattering_u << 11) >> 21, scattering_u % 1024) / vec3(2047.0, 2047.0, 1023.0); scattering += params.base_scattering * params.base_density; -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS uint emission_u = emissive_only_map[lpos]; #else uint emission_u = imageLoad(emissive_only_map, pos).x; @@ -710,7 +710,7 @@ void main() { final_density = mix(final_density, reprojected_density, reproject_amount); imageStore(density_map, pos, final_density); -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS density_only_map[lpos] = 0; light_only_map[lpos] = 0; emissive_only_map[lpos] = 0; diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index a7148aa0e2..aafb9b4764 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -7,6 +7,7 @@ #include "scene_forward_clustered_inc.glsl" #define SHADER_IS_SRGB false +#define SHADER_SPACE_FAR 0.0 /* INPUT ATTRIBS */ @@ -352,7 +353,7 @@ void vertex_shader(vec3 vertex_input, } #ifdef OVERRIDE_POSITION - vec4 position; + vec4 position = vec4(1.0); #endif #ifdef USE_MULTIVIEW @@ -638,6 +639,7 @@ void main() { #VERSION_DEFINES #define SHADER_IS_SRGB false +#define SHADER_SPACE_FAR 0.0 /* Specialization Constants (Toggles) */ @@ -657,6 +659,7 @@ layout(constant_id = 9) const uint sc_directional_penumbra_shadow_samples = 4; layout(constant_id = 10) const bool sc_decal_use_mipmaps = true; layout(constant_id = 11) const bool sc_projector_use_mipmaps = true; layout(constant_id = 12) const bool sc_use_depth_fog = false; +layout(constant_id = 13) const bool sc_use_lightmap_bicubic_filter = false; // not used in clustered renderer but we share some code with the mobile renderer that requires this. const float sc_luminance_multiplier = 1.0; @@ -701,6 +704,67 @@ layout(location = 9) in float dp_clip; layout(location = 10) in flat uint instance_index_interp; +#ifdef USE_LIGHTMAP +// w0, w1, w2, and w3 are the four cubic B-spline basis functions +float w0(float a) { + return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); +} + +float w1(float a) { + return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); +} + +float w2(float a) { + return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); +} + +float w3(float a) { + return (1.0 / 6.0) * (a * a * a); +} + +// g0 and g1 are the two amplitude functions +float g0(float a) { + return w0(a) + w1(a); +} + +float g1(float a) { + return w2(a) + w3(a); +} + +// h0 and h1 are the two offset functions +float h0(float a) { + return -1.0 + w1(a) / (w0(a) + w1(a)); +} + +float h1(float a) { + return 1.0 + w3(a) / (w2(a) + w3(a)); +} + +vec4 textureArray_bicubic(texture2DArray tex, vec3 uv, vec2 texture_size) { + vec2 texel_size = vec2(1.0) / texture_size; + + uv.xy = uv.xy * texture_size + vec2(0.5); + + vec2 iuv = floor(uv.xy); + vec2 fuv = fract(uv.xy); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size; + + return (g0(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p0, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p1, uv.z)))) + + (g1(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p2, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p3, uv.z)))); +} +#endif //USE_LIGHTMAP + #ifdef USE_MULTIVIEW #ifdef has_VK_KHR_multiview #define ViewIndex gl_ViewIndex @@ -1030,6 +1094,13 @@ void fragment_shader(in SceneData scene_data) { vec3 light_vertex = vertex; #endif //LIGHT_VERTEX_USED + mat3 model_normal_matrix; + if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_NON_UNIFORM_SCALE)) { + model_normal_matrix = transpose(inverse(mat3(read_model_matrix))); + } else { + model_normal_matrix = mat3(read_model_matrix); + } + mat4 read_view_matrix = scene_data.view_matrix; vec2 read_viewport_size = scene_data.viewport_size; { @@ -1310,6 +1381,8 @@ void fragment_shader(in SceneData scene_data) { #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) +#ifndef AMBIENT_LIGHT_DISABLED + if (scene_data.use_reflection_cubemap) { #ifdef LIGHT_ANISOTROPY_USED // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy @@ -1397,14 +1470,15 @@ void fragment_shader(in SceneData scene_data) { #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a; } -#endif +#endif // LIGHT_CLEARCOAT_USED +#endif // !AMBIENT_LIGHT_DISABLED #endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) //radiance /// GI /// #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) - +#ifndef AMBIENT_LIGHT_DISABLED #ifdef USE_LIGHTMAP //lightmap @@ -1439,27 +1513,38 @@ void fragment_shader(in SceneData scene_data) { if (uses_sh) { uvw.z *= 4.0; //SH textures use 4 times more data - vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; - vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; - vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; - vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + + vec3 lm_light_l0; + vec3 lm_light_l1n1; + vec3 lm_light_l1_0; + vec3 lm_light_l1p1; + + if (sc_use_lightmap_bicubic_filter) { + lm_light_l0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 0.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1n1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 1.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1_0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 2.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1p1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 3.0), lightmaps.data[ofs].light_texture_size).rgb; + } else { + lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; + lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; + lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; + lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + } vec3 n = normalize(lightmaps.data[ofs].normal_xform * normal); float en = lightmaps.data[ofs].exposure_normalization; - ambient_light += lm_light_l0 * 0.282095f * en; - ambient_light += lm_light_l1n1 * 0.32573 * n.y * en; - ambient_light += lm_light_l1_0 * 0.32573 * n.z * en; - ambient_light += lm_light_l1p1 * 0.32573 * n.x * en; - if (metallic > 0.01) { // since the more direct bounced light is lost, we can kind of fake it with this trick - vec3 r = reflect(normalize(-vertex), normal); - specular_light += lm_light_l1n1 * 0.32573 * r.y * en; - specular_light += lm_light_l1_0 * 0.32573 * r.z * en; - specular_light += lm_light_l1p1 * 0.32573 * r.x * en; - } + ambient_light += lm_light_l0 * en; + ambient_light += lm_light_l1n1 * n.y * en; + ambient_light += lm_light_l1_0 * n.z * en; + ambient_light += lm_light_l1p1 * n.x * en; } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + if (sc_use_lightmap_bicubic_filter) { + ambient_light += textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization; + } else { + ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + } } } #else @@ -1698,9 +1783,6 @@ void fragment_shader(in SceneData scene_data) { //finalize ambient light here { -#if defined(AMBIENT_LIGHT_DISABLED) - ambient_light = vec3(0.0, 0.0, 0.0); -#else ambient_light *= albedo.rgb; ambient_light *= ao; @@ -1713,15 +1795,14 @@ void fragment_shader(in SceneData scene_data) { ambient_light *= 1.0 - ssil.a; ambient_light += ssil.rgb * albedo.rgb; } -#endif // AMBIENT_LIGHT_DISABLED } - +#endif // AMBIENT_LIGHT_DISABLED // convert ao to direct light ao ao = mix(1.0, ao, ao_light_affect); //this saves some VGPRs vec3 f0 = F0(metallic, specular, albedo); - +#ifndef AMBIENT_LIGHT_DISABLED { #if defined(DIFFUSE_TOON) //simplify for toon, as @@ -1743,6 +1824,7 @@ void fragment_shader(in SceneData scene_data) { #endif } +#endif // !AMBIENT_LIGHT_DISABLED #endif //GI !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) #if !defined(MODE_RENDER_DEPTH) @@ -2243,7 +2325,7 @@ void fragment_shader(in SceneData scene_data) { alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); #if defined(ALPHA_SCISSOR_USED) - if (alpha < alpha_scissor) { + if (alpha < alpha_scissor_threshold) { discard; } #endif // ALPHA_SCISSOR_USED @@ -2289,7 +2371,7 @@ void fragment_shader(in SceneData scene_data) { } } -#ifdef MOLTENVK_USED +#ifdef NO_IMAGE_ATOMICS imageStore(geom_facing_grid, grid_pos, uvec4(imageLoad(geom_facing_grid, grid_pos).r | facing_bits)); //store facing bits #else imageAtomicOr(geom_facing_grid, grid_pos, facing_bits); //store facing bits diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl index 441cf3c80c..03511aa3a8 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl @@ -94,8 +94,9 @@ directional_lights; struct Lightmap { mat3 normal_xform; - vec3 pad; + vec2 light_texture_size; float exposure_normalization; + float pad; }; layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index 1637326b48..c266161834 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -8,6 +8,7 @@ #include "scene_forward_mobile_inc.glsl" #define SHADER_IS_SRGB false +#define SHADER_SPACE_FAR 0.0 /* INPUT ATTRIBS */ @@ -76,6 +77,10 @@ void axis_angle_to_tbn(vec3 axis, float angle, out vec3 tangent, out vec3 binorm normal = omc_axis.zzz * axis + vec3(-s_axis.y, s_axis.x, c); } +/* Spec Constants */ + +layout(constant_id = 17) const bool sc_is_multimesh = false; + /* Varyings */ layout(location = 0) highp out vec3 vertex_interp; @@ -178,8 +183,6 @@ void main() { color_interp = color_attrib; #endif - bool is_multimesh = bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH); - mat4 model_matrix = instances.data[draw_call.instance_index].transform; mat4 inv_view_matrix = scene_data.inv_view_matrix; #ifdef USE_DOUBLE_PRECISION @@ -203,7 +206,7 @@ void main() { mat4 matrix; mat4 read_model_matrix = model_matrix; - if (is_multimesh) { + if (sc_is_multimesh) { //multimesh, instances are for it #ifdef USE_PARTICLE_TRAILS @@ -350,7 +353,7 @@ void main() { } #ifdef OVERRIDE_POSITION - vec4 position; + vec4 position = vec4(1.0); #endif #ifdef USE_MULTIVIEW @@ -399,7 +402,7 @@ void main() { // Then we combine the translations from the model matrix and the view matrix using emulated doubles. // We add the result to the vertex and ignore the final lost precision. vec3 model_origin = model_matrix[3].xyz; - if (is_multimesh) { + if (sc_is_multimesh) { vertex = mat3(matrix) * vertex; model_origin = double_add_vec3(model_origin, model_precision, matrix[3].xyz, vec3(0.0), model_precision); } @@ -496,6 +499,7 @@ void main() { #VERSION_DEFINES #define SHADER_IS_SRGB false +#define SHADER_SPACE_FAR 0.0 /* Specialization Constants */ @@ -519,6 +523,7 @@ layout(constant_id = 9) const bool sc_disable_omni_lights = false; layout(constant_id = 10) const bool sc_disable_spot_lights = false; layout(constant_id = 11) const bool sc_disable_reflection_probes = false; layout(constant_id = 12) const bool sc_disable_directional_lights = false; +layout(constant_id = 18) const bool sc_use_lightmap_bicubic_filter = false; #endif //!MODE_UNSHADED @@ -565,6 +570,67 @@ layout(location = 9) highp in float dp_clip; #endif +#ifdef USE_LIGHTMAP +// w0, w1, w2, and w3 are the four cubic B-spline basis functions +float w0(float a) { + return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); +} + +float w1(float a) { + return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); +} + +float w2(float a) { + return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); +} + +float w3(float a) { + return (1.0 / 6.0) * (a * a * a); +} + +// g0 and g1 are the two amplitude functions +float g0(float a) { + return w0(a) + w1(a); +} + +float g1(float a) { + return w2(a) + w3(a); +} + +// h0 and h1 are the two offset functions +float h0(float a) { + return -1.0 + w1(a) / (w0(a) + w1(a)); +} + +float h1(float a) { + return 1.0 + w3(a) / (w2(a) + w3(a)); +} + +vec4 textureArray_bicubic(texture2DArray tex, vec3 uv, vec2 texture_size) { + vec2 texel_size = vec2(1.0) / texture_size; + + uv.xy = uv.xy * texture_size + vec2(0.5); + + vec2 iuv = floor(uv.xy); + vec2 fuv = fract(uv.xy); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size; + + return (g0(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p0, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p1, uv.z)))) + + (g1(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p2, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p3, uv.z)))); +} +#endif //USE_LIGHTMAP + #ifdef USE_MULTIVIEW #ifdef has_VK_KHR_multiview #define ViewIndex gl_ViewIndex @@ -837,6 +903,13 @@ void main() { vec3 light_vertex = vertex; #endif //LIGHT_VERTEX_USED + mat3 model_normal_matrix; + if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_NON_UNIFORM_SCALE)) { + model_normal_matrix = transpose(inverse(mat3(read_model_matrix))); + } else { + model_normal_matrix = mat3(read_model_matrix); + } + mat4 read_view_matrix = scene_data.view_matrix; vec2 read_viewport_size = scene_data.viewport_size; @@ -1069,6 +1142,8 @@ void main() { #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) +#ifndef AMBIENT_LIGHT_DISABLED + if (scene_data.use_reflection_cubemap) { #ifdef LIGHT_ANISOTROPY_USED // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy @@ -1156,13 +1231,14 @@ void main() { #endif //USE_RADIANCE_CUBEMAP_ARRAY specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a; } -#endif +#endif // LIGHT_CLEARCOAT_USED +#endif // !AMBIENT_LIGHT_DISABLED #endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) //radiance #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) - +#ifndef AMBIENT_LIGHT_DISABLED #ifdef USE_LIGHTMAP //lightmap @@ -1197,27 +1273,36 @@ void main() { if (uses_sh) { uvw.z *= 4.0; //SH textures use 4 times more data - vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; - vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; - vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; - vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + vec3 lm_light_l0; + vec3 lm_light_l1n1; + vec3 lm_light_l1_0; + vec3 lm_light_l1p1; + + if (sc_use_lightmap_bicubic_filter) { + lm_light_l0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 0.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1n1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 1.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1_0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 2.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1p1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 3.0), lightmaps.data[ofs].light_texture_size).rgb; + } else { + lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; + lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; + lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; + lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + } vec3 n = normalize(lightmaps.data[ofs].normal_xform * normal); float exposure_normalization = lightmaps.data[ofs].exposure_normalization; - ambient_light += lm_light_l0 * 0.282095f; - ambient_light += lm_light_l1n1 * 0.32573 * n.y * exposure_normalization; - ambient_light += lm_light_l1_0 * 0.32573 * n.z * exposure_normalization; - ambient_light += lm_light_l1p1 * 0.32573 * n.x * exposure_normalization; - if (metallic > 0.01) { // since the more direct bounced light is lost, we can kind of fake it with this trick - vec3 r = reflect(normalize(-vertex), normal); - specular_light += lm_light_l1n1 * 0.32573 * r.y * exposure_normalization; - specular_light += lm_light_l1_0 * 0.32573 * r.z * exposure_normalization; - specular_light += lm_light_l1p1 * 0.32573 * r.x * exposure_normalization; - } - + ambient_light += lm_light_l0 * exposure_normalization; + ambient_light += lm_light_l1n1 * n.y * exposure_normalization; + ambient_light += lm_light_l1_0 * n.z * exposure_normalization; + ambient_light += lm_light_l1p1 * n.x * exposure_normalization; } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + if (sc_use_lightmap_bicubic_filter) { + ambient_light += textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization; + } else { + ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + } } } @@ -1272,14 +1357,11 @@ void main() { } //Reflection probes // finalize ambient light here - { -#if defined(AMBIENT_LIGHT_DISABLED) - ambient_light = vec3(0.0, 0.0, 0.0); -#else - ambient_light *= albedo.rgb; - ambient_light *= ao; -#endif // AMBIENT_LIGHT_DISABLED - } + + ambient_light *= albedo.rgb; + ambient_light *= ao; + +#endif // !AMBIENT_LIGHT_DISABLED // convert ao to direct light ao ao = mix(1.0, ao, ao_light_affect); @@ -1287,6 +1369,7 @@ void main() { //this saves some VGPRs vec3 f0 = F0(metallic, specular, albedo); +#ifndef AMBIENT_LIGHT_DISABLED { #if defined(DIFFUSE_TOON) //simplify for toon, as @@ -1308,6 +1391,7 @@ void main() { #endif } +#endif // !AMBIENT_LIGHT_DISABLED #endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) #if !defined(MODE_RENDER_DEPTH) @@ -1763,7 +1847,7 @@ void main() { alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); #if defined(ALPHA_SCISSOR_USED) - if (alpha < alpha_scissor) { + if (alpha < alpha_scissor_threshold) { discard; } #endif // !ALPHA_SCISSOR_USED diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl index 7674e905e1..d971ff04c5 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl @@ -71,8 +71,9 @@ directional_lights; struct Lightmap { mediump mat3 normal_xform; - vec3 pad; + vec2 light_texture_size; float exposure_normalization; + float pad; }; layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { diff --git a/servers/rendering/renderer_rd/shaders/particles.glsl b/servers/rendering/renderer_rd/shaders/particles.glsl index 60c49bacae..7c5291038f 100644 --- a/servers/rendering/renderer_rd/shaders/particles.glsl +++ b/servers/rendering/renderer_rd/shaders/particles.glsl @@ -292,6 +292,24 @@ void main() { PARTICLE.velocity = particles.data[src_idx].velocity; PARTICLE.flags = PARTICLE_FLAG_TRAILED | ((frame_history.data[0].frame & PARTICLE_FRAME_MASK) << PARTICLE_FRAME_SHIFT); //mark it as trailed, save in which frame it will start PARTICLE.xform = particles.data[src_idx].xform; +#ifdef USERDATA1_USED + PARTICLE.userdata1 = particles.data[src_idx].userdata1; +#endif +#ifdef USERDATA2_USED + PARTICLE.userdata2 = particles.data[src_idx].userdata2; +#endif +#ifdef USERDATA3_USED + PARTICLE.userdata3 = particles.data[src_idx].userdata3; +#endif +#ifdef USERDATA4_USED + PARTICLE.userdata4 = particles.data[src_idx].userdata4; +#endif +#ifdef USERDATA5_USED + PARTICLE.userdata5 = particles.data[src_idx].userdata5; +#endif +#ifdef USERDATA6_USED + PARTICLE.userdata6 = particles.data[src_idx].userdata6; +#endif } if (!bool(particles.data[src_idx].flags & PARTICLE_FLAG_ACTIVE)) { // Disable the entire trail if the parent is no longer active. diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index 8b2bb14b76..b07063cfda 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -1813,6 +1813,7 @@ void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_use } t->lightmap_users.insert(p_lightmap); + lm->light_texture_size = Vector2i(t->width, t->height); if (using_lightmap_array) { if (lm->array_index < 0) { diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h index f152cc5dae..1db58d72f9 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h @@ -49,7 +49,7 @@ namespace RendererRD { class LightStorage : public RendererLightStorage { public: - enum ShadowAtlastQuadrant { + enum ShadowAtlastQuadrant : uint32_t { QUADRANT_SHIFT = 27, OMNI_LIGHT_FLAG = 1 << 26, SHADOW_INDEX_MASK = OMNI_LIGHT_FLAG - 1, @@ -332,6 +332,7 @@ private: bool interior = false; AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); float baked_exposure = 1.0; + Vector2i light_texture_size; int32_t array_index = -1; //unassigned PackedVector3Array points; PackedColorArray point_sh; @@ -985,6 +986,10 @@ public: const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); return lm->uses_spherical_harmonics; } + _FORCE_INLINE_ Vector2i lightmap_get_light_texture_size(RID p_lightmap) const { + const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + return lm->light_texture_size; + } _FORCE_INLINE_ uint64_t lightmap_array_get_version() const { ERR_FAIL_COND_V(!using_lightmap_array, 0); //only for arrays return lightmap_array_version; diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index d1c8c71b7f..63dc54e24c 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp @@ -580,11 +580,7 @@ void MaterialStorage::ShaderData::get_shader_uniform_list(List<PropertyInfo> *p_ if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) { continue; } - if (E.value.texture_order >= 0) { - filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.texture_order + 100000)); - } else { - filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.order)); - } + filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.prop_order)); } int uniform_count = filtered_uniforms.size(); sorter.sort(filtered_uniforms.ptr(), uniform_count); @@ -634,7 +630,7 @@ bool MaterialStorage::ShaderData::is_parameter_texture(const StringName &p_param return false; } - return uniforms[p_param].texture_order >= 0; + return uniforms[p_param].is_texture(); } /////////////////////////////////////////////////////////////////////////// @@ -645,7 +641,7 @@ void MaterialStorage::MaterialData::update_uniform_buffer(const HashMap<StringNa bool uses_global_buffer = false; for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : p_uniforms) { - if (E.value.order < 0) { + if (E.value.is_texture()) { continue; // texture, does not go here } @@ -807,7 +803,8 @@ void MaterialStorage::MaterialData::update_textures(const HashMap<StringName, Va if (V->value.is_array()) { Array array = (Array)V->value; if (uniform_array_size > 0) { - for (int j = 0; j < array.size(); j++) { + int size = MIN(uniform_array_size, array.size()); + for (int j = 0; j < size; j++) { textures.push_back(array[j]); } } else { @@ -1165,8 +1162,8 @@ MaterialStorage::MaterialStorage() { global_shader_uniforms.buffer_values = memnew_arr(GlobalShaderUniforms::Value, global_shader_uniforms.buffer_size); memset(global_shader_uniforms.buffer_values, 0, sizeof(GlobalShaderUniforms::Value) * global_shader_uniforms.buffer_size); global_shader_uniforms.buffer_usage = memnew_arr(GlobalShaderUniforms::ValueUsage, global_shader_uniforms.buffer_size); - global_shader_uniforms.buffer_dirty_regions = memnew_arr(bool, global_shader_uniforms.buffer_size / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE); - memset(global_shader_uniforms.buffer_dirty_regions, 0, sizeof(bool) * global_shader_uniforms.buffer_size / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE); + global_shader_uniforms.buffer_dirty_regions = memnew_arr(bool, 1 + (global_shader_uniforms.buffer_size / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE)); + memset(global_shader_uniforms.buffer_dirty_regions, 0, sizeof(bool) * (1 + (global_shader_uniforms.buffer_size / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE))); global_shader_uniforms.buffer = RD::get_singleton()->storage_buffer_create(sizeof(GlobalShaderUniforms::Value) * global_shader_uniforms.buffer_size); } @@ -1769,7 +1766,7 @@ void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, i void MaterialStorage::_update_global_shader_uniforms() { MaterialStorage *material_storage = MaterialStorage::get_singleton(); if (global_shader_uniforms.buffer_dirty_region_count > 0) { - uint32_t total_regions = global_shader_uniforms.buffer_size / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE; + uint32_t total_regions = 1 + (global_shader_uniforms.buffer_size / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE); if (total_regions / global_shader_uniforms.buffer_dirty_region_count <= 4) { // 25% of regions dirty, just update all buffer RD::get_singleton()->buffer_update(global_shader_uniforms.buffer, 0, sizeof(GlobalShaderUniforms::Value) * global_shader_uniforms.buffer_size, global_shader_uniforms.buffer_values); diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 99622996d4..539bdcbbd0 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -1056,8 +1056,9 @@ void MeshStorage::update_mesh_instances() { mi->surfaces[i].previous_buffer = mi->surfaces[i].current_buffer; - if (uses_motion_vectors && (frame - mi->surfaces[i].last_change) == 1) { - // Previous buffer's data can only be one frame old to be able to use motion vectors. + if (uses_motion_vectors && mi->surfaces[i].last_change && (frame - mi->surfaces[i].last_change) <= 2) { + // Use a 2-frame tolerance so that stepped skeletal animations have correct motion vectors + // (stepped animation is common for distant NPCs). uint32_t new_buffer_index = mi->surfaces[i].current_buffer ^ 1; if (mi->surfaces[i].uniform_set[new_buffer_index].is_null()) { @@ -1380,14 +1381,16 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V ////////////////// MULTIMESH -RID MeshStorage::multimesh_allocate() { +RID MeshStorage::_multimesh_allocate() { return multimesh_owner.allocate_rid(); } -void MeshStorage::multimesh_initialize(RID p_rid) { +void MeshStorage::_multimesh_initialize(RID p_rid) { multimesh_owner.initialize_rid(p_rid, MultiMesh()); } -void MeshStorage::multimesh_free(RID p_rid) { +void MeshStorage::_multimesh_free(RID p_rid) { + // Remove from interpolator. + _interpolation_data.notify_free_multimesh(p_rid); _update_dirty_multimeshes(); multimesh_allocate_data(p_rid, 0, RS::MULTIMESH_TRANSFORM_2D); MultiMesh *multimesh = multimesh_owner.get_or_null(p_rid); @@ -1395,7 +1398,7 @@ void MeshStorage::multimesh_free(RID p_rid) { multimesh_owner.free(p_rid); } -void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { +void MeshStorage::_multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); @@ -1439,12 +1442,10 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS:: multimesh->motion_vectors_current_offset = 0; multimesh->motion_vectors_previous_offset = 0; multimesh->motion_vectors_last_change = -1; + multimesh->motion_vectors_enabled = false; if (multimesh->instances) { uint32_t buffer_size = multimesh->instances * multimesh->stride_cache * sizeof(float); - if (multimesh->motion_vectors_enabled) { - buffer_size *= 2; - } multimesh->buffer = RD::get_singleton()->storage_buffer_create(buffer_size); } @@ -1507,13 +1508,13 @@ bool MeshStorage::_multimesh_uses_motion_vectors_offsets(RID p_multimesh) { return _multimesh_uses_motion_vectors(multimesh); } -int MeshStorage::multimesh_get_instance_count(RID p_multimesh) const { +int MeshStorage::_multimesh_get_instance_count(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, 0); return multimesh->instances; } -void MeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { +void MeshStorage::_multimesh_set_mesh(RID p_multimesh, RID p_mesh) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); if (multimesh->mesh == p_mesh) { @@ -1703,7 +1704,7 @@ void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p multimesh->aabb = aabb; } -void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) { +void MeshStorage::_multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); @@ -1740,7 +1741,7 @@ void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, _multimesh_mark_dirty(multimesh, p_index, true); } -void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { +void MeshStorage::_multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); @@ -1767,7 +1768,7 @@ void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_ind _multimesh_mark_dirty(multimesh, p_index, true); } -void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { +void MeshStorage::_multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); @@ -1790,7 +1791,7 @@ void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, con _multimesh_mark_dirty(multimesh, p_index, false); } -void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { +void MeshStorage::_multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); @@ -1813,7 +1814,7 @@ void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_inde _multimesh_mark_dirty(multimesh, p_index, false); } -RID MeshStorage::multimesh_get_mesh(RID p_multimesh) const { +RID MeshStorage::_multimesh_get_mesh(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, RID()); @@ -1827,7 +1828,7 @@ Dependency *MeshStorage::multimesh_get_dependency(RID p_multimesh) const { return &multimesh->dependency; } -Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { +Transform3D MeshStorage::_multimesh_instance_get_transform(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Transform3D()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform3D()); @@ -1858,7 +1859,7 @@ Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p return t; } -Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { +Transform2D MeshStorage::_multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Transform2D()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform2D()); @@ -1883,7 +1884,7 @@ Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, in return t; } -Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) const { +Color MeshStorage::_multimesh_instance_get_color(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Color()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color()); @@ -1906,7 +1907,7 @@ Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) co return c; } -Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { +Color MeshStorage::_multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Color()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color()); @@ -1929,11 +1930,12 @@ Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_ind return c; } -void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { +void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache)); + bool used_motion_vectors = multimesh->motion_vectors_enabled; bool uses_motion_vectors = (RSG::viewport->get_num_viewports_with_motion_vectors() > 0) || (RendererCompositorStorage::get_singleton()->get_num_compositor_effects_with_motion_vectors() > 0); if (uses_motion_vectors) { _multimesh_enable_motion_vectors(multimesh); @@ -1952,6 +1954,11 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b { const float *r = p_buffer.ptr(); RD::get_singleton()->buffer_update(multimesh->buffer, multimesh->motion_vectors_current_offset * multimesh->stride_cache * sizeof(float), p_buffer.size() * sizeof(float), r); + if (multimesh->motion_vectors_enabled && !used_motion_vectors) { + // Motion vectors were just enabled, and the other half of the buffer will be empty. + // Need to ensure that both halves are filled for correct operation. + RD::get_singleton()->buffer_update(multimesh->buffer, multimesh->motion_vectors_previous_offset * multimesh->stride_cache * sizeof(float), p_buffer.size() * sizeof(float), r); + } multimesh->buffer_set = true; } @@ -1970,7 +1977,7 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b } } -Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const { +Vector<float> MeshStorage::_multimesh_get_buffer(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, Vector<float>()); if (multimesh->buffer.is_null()) { @@ -1992,7 +1999,7 @@ Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const { } } -void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { +void MeshStorage::_multimesh_set_visible_instances(RID p_multimesh, int p_visible) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); ERR_FAIL_COND(p_visible < -1 || p_visible > multimesh->instances); @@ -2014,26 +2021,26 @@ void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES); } -int MeshStorage::multimesh_get_visible_instances(RID p_multimesh) const { +int MeshStorage::_multimesh_get_visible_instances(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, 0); return multimesh->visible_instances; } -void MeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) { +void MeshStorage::_multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL(multimesh); multimesh->custom_aabb = p_aabb; multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } -AABB MeshStorage::multimesh_get_custom_aabb(RID p_multimesh) const { +AABB MeshStorage::_multimesh_get_custom_aabb(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, AABB()); return multimesh->custom_aabb; } -AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const { +AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, AABB()); if (multimesh->custom_aabb != AABB()) { @@ -2046,6 +2053,13 @@ AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const { return multimesh->aabb; } +MeshStorage::MultiMeshInterpolator *MeshStorage::_multimesh_get_interpolator(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_NULL_V_MSG(multimesh, nullptr, "Multimesh not found: " + itos(p_multimesh.get_id())); + + return &multimesh->interpolator; +} + void MeshStorage::_update_dirty_multimeshes() { while (multimesh_dirty_list) { MultiMesh *multimesh = multimesh_dirty_list; diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index 5491f637bc..4344db783d 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -244,6 +244,8 @@ private: bool dirty = false; MultiMesh *dirty_list = nullptr; + RendererMeshStorage::MultiMeshInterpolator interpolator; + Dependency dependency; }; @@ -621,36 +623,38 @@ public: bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); }; - virtual RID multimesh_allocate() override; - virtual void multimesh_initialize(RID p_multimesh) override; - virtual void multimesh_free(RID p_rid) override; + virtual RID _multimesh_allocate() override; + virtual void _multimesh_initialize(RID p_multimesh) override; + virtual void _multimesh_free(RID p_rid) override; + + virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override; + virtual int _multimesh_get_instance_count(RID p_multimesh) const override; - virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override; - virtual int multimesh_get_instance_count(RID p_multimesh) const override; + virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) override; + virtual void _multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override; + virtual void _multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override; + virtual void _multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override; + virtual void _multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override; - virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override; - virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override; - virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override; - virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override; - virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override; + virtual RID _multimesh_get_mesh(RID p_multimesh) const override; - virtual RID multimesh_get_mesh(RID p_multimesh) const override; + virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override; + virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override; + virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override; + virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override; - virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override; - virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override; - virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override; - virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override; + virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; + virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const override; - virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; - virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const override; + virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) override; + virtual int _multimesh_get_visible_instances(RID p_multimesh) const override; - virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override; - virtual int multimesh_get_visible_instances(RID p_multimesh) const override; + virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override; + virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override; - virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override; - virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const override; + virtual AABB _multimesh_get_aabb(RID p_multimesh) const override; - virtual AABB multimesh_get_aabb(RID p_multimesh) const override; + virtual MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override; void _update_dirty_multimeshes(); void _multimesh_get_motion_vectors_offsets(RID p_multimesh, uint32_t &r_current_offset, uint32_t &r_prev_offset); diff --git a/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp index ac4fbba75b..ddc4d09279 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp @@ -30,9 +30,6 @@ #include "render_data_rd.h" -void RenderDataRD::_bind_methods() { -} - Ref<RenderSceneBuffers> RenderDataRD::get_render_scene_buffers() const { return render_buffers; } diff --git a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h index 3cd397b8ed..888527e1ef 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h @@ -38,9 +38,6 @@ class RenderDataRD : public RenderData { GDCLASS(RenderDataRD, RenderData); -protected: - static void _bind_methods(); - public: // Access methods to expose data externally virtual Ref<RenderSceneBuffers> get_render_scene_buffers() const override; @@ -76,6 +73,8 @@ public: uint32_t directional_light_count = 0; bool directional_light_soft_shadows = false; + bool lightmap_bicubic_filter = false; + RenderingMethod::RenderInfo *render_info = nullptr; /* Viewport data */ diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp index b5fdf8bebb..2f44096dc8 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -70,8 +70,14 @@ void RenderSceneBuffersRD::_bind_methods() { ClassDB::bind_method(D_METHOD("get_render_target"), &RenderSceneBuffersRD::get_render_target); ClassDB::bind_method(D_METHOD("get_view_count"), &RenderSceneBuffersRD::get_view_count); ClassDB::bind_method(D_METHOD("get_internal_size"), &RenderSceneBuffersRD::get_internal_size); + ClassDB::bind_method(D_METHOD("get_target_size"), &RenderSceneBuffersRD::get_target_size); + ClassDB::bind_method(D_METHOD("get_scaling_3d_mode"), &RenderSceneBuffersRD::get_scaling_3d_mode); + ClassDB::bind_method(D_METHOD("get_fsr_sharpness"), &RenderSceneBuffersRD::get_fsr_sharpness); ClassDB::bind_method(D_METHOD("get_msaa_3d"), &RenderSceneBuffersRD::get_msaa_3d); + ClassDB::bind_method(D_METHOD("get_texture_samples"), &RenderSceneBuffersRD::get_texture_samples); + ClassDB::bind_method(D_METHOD("get_screen_space_aa"), &RenderSceneBuffersRD::get_screen_space_aa); ClassDB::bind_method(D_METHOD("get_use_taa"), &RenderSceneBuffersRD::get_use_taa); + ClassDB::bind_method(D_METHOD("get_use_debanding"), &RenderSceneBuffersRD::get_use_debanding); } void RenderSceneBuffersRD::update_sizes(NamedTexture &p_named_texture) { diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp index dc1e64ddcc..148a556b46 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp @@ -34,15 +34,16 @@ #include "servers/rendering/renderer_rd/storage_rd/texture_storage.h" #include "servers/rendering/rendering_server_default.h" -void RenderSceneDataRD::_bind_methods() { -} - Transform3D RenderSceneDataRD::get_cam_transform() const { return cam_transform; } Projection RenderSceneDataRD::get_cam_projection() const { - return cam_projection; + Projection correction; + correction.set_depth_correction(flip_y); + correction.add_jitter_offset(taa_jitter); + + return correction * cam_projection; } uint32_t RenderSceneDataRD::get_view_count() const { @@ -58,14 +59,18 @@ Vector3 RenderSceneDataRD::get_view_eye_offset(uint32_t p_view) const { Projection RenderSceneDataRD::get_view_projection(uint32_t p_view) const { ERR_FAIL_UNSIGNED_INDEX_V(p_view, view_count, Projection()); - return view_projection[p_view]; + Projection correction; + correction.set_depth_correction(flip_y); + correction.add_jitter_offset(taa_jitter); + + return correction * view_projection[p_view]; } RID RenderSceneDataRD::create_uniform_buffer() { return RD::get_singleton()->uniform_buffer_create(sizeof(UBODATA)); } -void RenderSceneDataRD::update_ubo(RID p_uniform_buffer, RS::ViewportDebugDraw p_debug_mode, RID p_env, RID p_reflection_probe_instance, RID p_camera_attributes, bool p_flip_y, bool p_pancake_shadows, const Size2i &p_screen_size, const Color &p_default_bg_color, float p_luminance_multiplier, bool p_opaque_render_buffers, bool p_apply_alpha_multiplier) { +void RenderSceneDataRD::update_ubo(RID p_uniform_buffer, RS::ViewportDebugDraw p_debug_mode, RID p_env, RID p_reflection_probe_instance, RID p_camera_attributes, bool p_pancake_shadows, const Size2i &p_screen_size, const Color &p_default_bg_color, float p_luminance_multiplier, bool p_opaque_render_buffers, bool p_apply_alpha_multiplier) { RendererSceneRenderRD *render_scene_render = RendererSceneRenderRD::get_singleton(); UBODATA ubo_data; @@ -76,7 +81,7 @@ void RenderSceneDataRD::update_ubo(RID p_uniform_buffer, RS::ViewportDebugDraw p UBO &prev_ubo = ubo_data.prev_ubo; Projection correction; - correction.set_depth_correction(p_flip_y); + correction.set_depth_correction(flip_y); correction.add_jitter_offset(taa_jitter); Projection projection = correction * cam_projection; diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h index f6785942ed..b2c93acd44 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h @@ -50,6 +50,7 @@ public: Vector2 taa_jitter; uint32_t camera_visible_layers; bool cam_orthogonal = false; + bool flip_y = false; // For billboards to cast correct shadows. Transform3D main_cam_transform; @@ -90,12 +91,10 @@ public: virtual Projection get_view_projection(uint32_t p_view) const override; RID create_uniform_buffer(); - void update_ubo(RID p_uniform_buffer, RS::ViewportDebugDraw p_debug_mode, RID p_env, RID p_reflection_probe_instance, RID p_camera_attributes, bool p_flip_y, bool p_pancake_shadows, const Size2i &p_screen_size, const Color &p_default_bg_color, float p_luminance_multiplier, bool p_opaque_render_buffers, bool p_apply_alpha_multiplier); + void update_ubo(RID p_uniform_buffer, RS::ViewportDebugDraw p_debug_mode, RID p_env, RID p_reflection_probe_instance, RID p_camera_attributes, bool p_pancake_shadows, const Size2i &p_screen_size, const Color &p_default_bg_color, float p_luminance_multiplier, bool p_opaque_render_buffers, bool p_apply_alpha_multiplier); virtual RID get_uniform_buffer() const override; private: - static void _bind_methods(); - RID uniform_buffer; // loaded into this uniform buffer (supplied externally) // This struct is loaded into Set 1 - Binding 0, populated at start of rendering a frame, must match with shader code diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 6e5e8f63e0..be29716f45 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -1470,8 +1470,24 @@ void TextureStorage::texture_debug_usage(List<RS::TextureInfo> *r_info) { tinfo.format = t->format; tinfo.width = t->width; tinfo.height = t->height; - tinfo.depth = t->depth; - tinfo.bytes = Image::get_image_data_size(t->width, t->height, t->format, t->mipmaps); + tinfo.bytes = Image::get_image_data_size(t->width, t->height, t->format, t->mipmaps > 1); + + switch (t->type) { + case TextureType::TYPE_3D: + tinfo.depth = t->depth; + tinfo.bytes *= t->depth; + break; + + case TextureType::TYPE_LAYERED: + tinfo.depth = t->layers; + tinfo.bytes *= t->layers; + break; + + default: + tinfo.depth = 0; + break; + } + r_info->push_back(tinfo); } } diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index b02d3def88..1d25dec633 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -34,10 +34,16 @@ #include "core/object/worker_thread_pool.h" #include "core/os/os.h" #include "rendering_light_culler.h" +#include "rendering_server_constants.h" #include "rendering_server_default.h" #include <new> +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) +// This is used only to obtain node paths for user-friendly physics interpolation warnings. +#include "scene/main/node.h" +#endif + /* HALTON SEQUENCE */ #ifndef _3D_DISABLED @@ -53,6 +59,20 @@ static float get_halton_value(int p_index, int p_base) { } #endif // _3D_DISABLED +/* EVENT QUEUING */ + +void RendererSceneCull::tick() { + if (_interpolation_data.interpolation_enabled) { + update_interpolation_tick(true); + } +} + +void RendererSceneCull::pre_draw(bool p_will_draw) { + if (_interpolation_data.interpolation_enabled) { + update_interpolation_frame(p_will_draw); + } +} + /* CAMERA API */ RID RendererSceneCull::camera_allocate() { @@ -93,6 +113,7 @@ void RendererSceneCull::camera_set_frustum(RID p_camera, float p_size, Vector2 p void RendererSceneCull::camera_set_transform(RID p_camera, const Transform3D &p_transform) { Camera *camera = camera_owner.get_or_null(p_camera); ERR_FAIL_NULL(camera); + camera->transform = p_transform.orthonormalized(); } @@ -924,8 +945,45 @@ void RendererSceneCull::instance_set_transform(RID p_instance, const Transform3D Instance *instance = instance_owner.get_or_null(p_instance); ERR_FAIL_NULL(instance); - if (instance->transform == p_transform) { - return; //must be checked to avoid worst evil +#ifdef RENDERING_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("instance_set_transform " + rtos(p_transform.origin.x) + " .. tick " + itos(Engine::get_singleton()->get_physics_frames())); +#endif + + if (!_interpolation_data.interpolation_enabled || !instance->interpolated || !instance->scenario) { + if (instance->transform == p_transform) { + return; // Must be checked to avoid worst evil. + } + +#ifdef DEBUG_ENABLED + + for (int i = 0; i < 4; i++) { + const Vector3 &v = i < 3 ? p_transform.basis.rows[i] : p_transform.origin; + ERR_FAIL_COND(!v.is_finite()); + } + +#endif + instance->transform = p_transform; + _instance_queue_update(instance, true); + +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (_interpolation_data.interpolation_enabled && !instance->interpolated && Engine::get_singleton()->is_in_physics_frame()) { + PHYSICS_INTERPOLATION_NODE_WARNING(instance->object_id, "Non-interpolated instance triggered from physics process"); + } +#endif + + return; + } + + float new_checksum = TransformInterpolator::checksum_transform_3d(p_transform); + bool checksums_match = (instance->transform_checksum_curr == new_checksum) && (instance->transform_checksum_prev == new_checksum); + + // We can't entirely reject no changes because we need the interpolation + // system to keep on stewing. + + // Optimized check. First checks the checksums. If they pass it does the slow check at the end. + // Alternatively we can do this non-optimized and ignore the checksum... if no change. + if (checksums_match && (instance->transform_curr == p_transform) && (instance->transform_prev == p_transform)) { + return; } #ifdef DEBUG_ENABLED @@ -936,8 +994,69 @@ void RendererSceneCull::instance_set_transform(RID p_instance, const Transform3D } #endif - instance->transform = p_transform; + + instance->transform_curr = p_transform; + +#ifdef RENDERING_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("\tprev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x)); +#endif + + // Keep checksums up to date. + instance->transform_checksum_curr = new_checksum; + + if (!instance->on_interpolate_transform_list) { + _interpolation_data.instance_transform_update_list_curr->push_back(p_instance); + instance->on_interpolate_transform_list = true; + } else { + DEV_ASSERT(_interpolation_data.instance_transform_update_list_curr->size()); + } + + // If the instance is invisible, then we are simply updating the data flow, there is no need to calculate the interpolated + // transform or anything else. + // Ideally we would not even call the VisualServer::set_transform() when invisible but that would entail having logic + // to keep track of the previous transform on the SceneTree side. The "early out" below is less efficient but a lot cleaner codewise. + if (!instance->visible) { + return; + } + + // Decide on the interpolation method... slerp if possible. + instance->interpolation_method = TransformInterpolator::find_method(instance->transform_prev.basis, instance->transform_curr.basis); + + if (!instance->on_interpolate_list) { + _interpolation_data.instance_interpolate_update_list.push_back(p_instance); + instance->on_interpolate_list = true; + } else { + DEV_ASSERT(_interpolation_data.instance_interpolate_update_list.size()); + } + _instance_queue_update(instance, true); + +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (!Engine::get_singleton()->is_in_physics_frame()) { + PHYSICS_INTERPOLATION_NODE_WARNING(instance->object_id, "Interpolated instance triggered from outside physics process"); + } +#endif +} + +void RendererSceneCull::instance_set_interpolated(RID p_instance, bool p_interpolated) { + Instance *instance = instance_owner.get_or_null(p_instance); + ERR_FAIL_NULL(instance); + instance->interpolated = p_interpolated; +} + +void RendererSceneCull::instance_reset_physics_interpolation(RID p_instance) { + Instance *instance = instance_owner.get_or_null(p_instance); + ERR_FAIL_NULL(instance); + + if (_interpolation_data.interpolation_enabled && instance->interpolated) { + instance->transform_prev = instance->transform_curr; + instance->transform_checksum_prev = instance->transform_checksum_curr; + +#ifdef RENDERING_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("instance_reset_physics_interpolation .. tick " + itos(Engine::get_singleton()->get_physics_frames())); + print_line("\tprev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x)); +#endif + } } void RendererSceneCull::instance_attach_object_instance_id(RID p_instance, ObjectID p_id) { @@ -990,6 +1109,23 @@ void RendererSceneCull::instance_set_visible(RID p_instance, bool p_visible) { if (p_visible) { if (instance->scenario != nullptr) { + // Special case for physics interpolation, we want to ensure the interpolated data is up to date + if (_interpolation_data.interpolation_enabled && instance->interpolated && !instance->on_interpolate_list) { + // Do all the extra work we normally do on instance_set_transform(), because this is optimized out for hidden instances. + // This prevents a glitch of stale interpolation transform data when unhiding before the next physics tick. + instance->interpolation_method = TransformInterpolator::find_method(instance->transform_prev.basis, instance->transform_curr.basis); + _interpolation_data.instance_interpolate_update_list.push_back(p_instance); + instance->on_interpolate_list = true; + + // We must also place on the transform update list for a tick, so the system + // can auto-detect if the instance is no longer moving, and remove from the interpolate lists again. + // If this step is ignored, an unmoving instance could remain on the interpolate lists indefinitely + // (or rather until the object is deleted) and cause unnecessary updates and drawcalls. + if (!instance->on_interpolate_transform_list) { + _interpolation_data.instance_transform_update_list_curr->push_back(p_instance); + instance->on_interpolate_transform_list = true; + } + } _instance_queue_update(instance, true, false); } } else if (instance->indexer_id.is_valid()) { @@ -1574,11 +1710,22 @@ void RendererSceneCull::instance_geometry_get_shader_parameter_list(RID p_instan void RendererSceneCull::_update_instance(Instance *p_instance) { p_instance->version++; + // When not using interpolation the transform is used straight. + const Transform3D *instance_xform = &p_instance->transform; + + // Can possibly use the most up to date current transform here when using physics interpolation ... + // uncomment the next line for this.. + //if (_interpolation_data.interpolation_enabled && p_instance->interpolated) { + // instance_xform = &p_instance->transform_curr; + //} + // However it does seem that using the interpolated transform (transform) works for keeping AABBs + // up to date to avoid culling errors. + if (p_instance->base_type == RS::INSTANCE_LIGHT) { InstanceLightData *light = static_cast<InstanceLightData *>(p_instance->base_data); - RSG::light_storage->light_instance_set_transform(light->instance, p_instance->transform); - RSG::light_storage->light_instance_set_aabb(light->instance, p_instance->transform.xform(p_instance->aabb)); + RSG::light_storage->light_instance_set_transform(light->instance, *instance_xform); + RSG::light_storage->light_instance_set_aabb(light->instance, instance_xform->xform(p_instance->aabb)); light->make_shadow_dirty(); RS::LightBakeMode bake_mode = RSG::light_storage->light_get_bake_mode(p_instance->base); @@ -1601,7 +1748,7 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { } else if (p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE) { InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(p_instance->base_data); - RSG::light_storage->reflection_probe_instance_set_transform(reflection_probe->instance, p_instance->transform); + RSG::light_storage->reflection_probe_instance_set_transform(reflection_probe->instance, *instance_xform); if (p_instance->scenario && p_instance->array_index >= 0) { InstanceData &idata = p_instance->scenario->instance_data[p_instance->array_index]; @@ -1610,17 +1757,17 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { } else if (p_instance->base_type == RS::INSTANCE_DECAL) { InstanceDecalData *decal = static_cast<InstanceDecalData *>(p_instance->base_data); - RSG::texture_storage->decal_instance_set_transform(decal->instance, p_instance->transform); + RSG::texture_storage->decal_instance_set_transform(decal->instance, *instance_xform); } else if (p_instance->base_type == RS::INSTANCE_LIGHTMAP) { InstanceLightmapData *lightmap = static_cast<InstanceLightmapData *>(p_instance->base_data); - RSG::light_storage->lightmap_instance_set_transform(lightmap->instance, p_instance->transform); + RSG::light_storage->lightmap_instance_set_transform(lightmap->instance, *instance_xform); } else if (p_instance->base_type == RS::INSTANCE_VOXEL_GI) { InstanceVoxelGIData *voxel_gi = static_cast<InstanceVoxelGIData *>(p_instance->base_data); - scene_render->voxel_gi_instance_set_transform_to_data(voxel_gi->probe_instance, p_instance->transform); + scene_render->voxel_gi_instance_set_transform_to_data(voxel_gi->probe_instance, *instance_xform); } else if (p_instance->base_type == RS::INSTANCE_PARTICLES) { - RSG::particles_storage->particles_set_emission_transform(p_instance->base, p_instance->transform); + RSG::particles_storage->particles_set_emission_transform(p_instance->base, *instance_xform); } else if (p_instance->base_type == RS::INSTANCE_PARTICLES_COLLISION) { InstanceParticlesCollisionData *collision = static_cast<InstanceParticlesCollisionData *>(p_instance->base_data); @@ -1628,13 +1775,13 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { if (RSG::particles_storage->particles_collision_is_heightfield(p_instance->base)) { heightfield_particle_colliders_update_list.insert(p_instance); } - RSG::particles_storage->particles_collision_instance_set_transform(collision->instance, p_instance->transform); + RSG::particles_storage->particles_collision_instance_set_transform(collision->instance, *instance_xform); } else if (p_instance->base_type == RS::INSTANCE_FOG_VOLUME) { InstanceFogVolumeData *volume = static_cast<InstanceFogVolumeData *>(p_instance->base_data); - scene_render->fog_volume_instance_set_transform(volume->instance, p_instance->transform); + scene_render->fog_volume_instance_set_transform(volume->instance, *instance_xform); } else if (p_instance->base_type == RS::INSTANCE_OCCLUDER) { if (p_instance->scenario) { - RendererSceneOcclusionCull::get_singleton()->scenario_set_instance(p_instance->scenario->self, p_instance->self, p_instance->base, p_instance->transform, p_instance->visible); + RendererSceneOcclusionCull::get_singleton()->scenario_set_instance(p_instance->scenario->self, p_instance->self, p_instance->base, *instance_xform, p_instance->visible); } } @@ -1654,7 +1801,7 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { } AABB new_aabb; - new_aabb = p_instance->transform.xform(p_instance->aabb); + new_aabb = instance_xform->xform(p_instance->aabb); p_instance->transformed_aabb = new_aabb; if ((1 << p_instance->base_type) & RS::INSTANCE_GEOMETRY_MASK) { @@ -1681,11 +1828,11 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { } ERR_FAIL_NULL(geom->geometry_instance); - geom->geometry_instance->set_transform(p_instance->transform, p_instance->aabb, p_instance->transformed_aabb); + geom->geometry_instance->set_transform(*instance_xform, p_instance->aabb, p_instance->transformed_aabb); } // note: we had to remove is equal approx check here, it meant that det == 0.000004 won't work, which is the case for some of our scenes. - if (p_instance->scenario == nullptr || !p_instance->visible || p_instance->transform.basis.determinant() == 0) { + if (p_instance->scenario == nullptr || !p_instance->visible || instance_xform->basis.determinant() == 0) { p_instance->prev_transformed_aabb = p_instance->transformed_aabb; return; } @@ -3089,7 +3236,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c Vector<Instance *> lights_with_shadow; for (Instance *E : scenario->directional_lights) { - if (!E->visible) { + if (!E->visible || !(E->layer_mask & p_visible_layers)) { continue; } @@ -4180,6 +4327,8 @@ bool RendererSceneCull::free(RID p_rid) { Instance *instance = instance_owner.get_or_null(p_rid); + _interpolation_data.notify_free_instance(p_rid, *instance); + instance_geometry_set_lightmap(p_rid, RID(), Rect2(), 0); instance_set_scenario(p_rid, RID()); instance_set_base(p_rid, RID()); @@ -4240,6 +4389,108 @@ void RendererSceneCull::set_scene_render(RendererSceneRender *p_scene_render) { geometry_instance_pair_mask = scene_render->geometry_instance_get_pair_mask(); } +/* INTERPOLATION API */ + +void RendererSceneCull::update_interpolation_tick(bool p_process) { + // MultiMesh: Update interpolation in storage. + RSG::mesh_storage->update_interpolation_tick(p_process); + + // INSTANCES + + // Detect any that were on the previous transform list that are no longer active; + // we should remove them from the interpolate list. + + for (const RID &rid : *_interpolation_data.instance_transform_update_list_prev) { + Instance *instance = instance_owner.get_or_null(rid); + + bool active = true; + + // No longer active? (Either the instance deleted or no longer being transformed.) + if (instance && !instance->on_interpolate_transform_list) { + active = false; + instance->on_interpolate_list = false; + + // Make sure the most recent transform is set... + instance->transform = instance->transform_curr; + + // ... and that both prev and current are the same, just in case of any interpolations. + instance->transform_prev = instance->transform_curr; + + // Make sure instances are updated one more time to ensure the AABBs are correct. + _instance_queue_update(instance, true); + } + + if (!instance) { + active = false; + } + + if (!active) { + _interpolation_data.instance_interpolate_update_list.erase(rid); + } + } + + // Now for any in the transform list (being actively interpolated), keep the previous transform + // value up to date, ready for the next tick. + if (p_process) { + for (const RID &rid : *_interpolation_data.instance_transform_update_list_curr) { + Instance *instance = instance_owner.get_or_null(rid); + if (instance) { + instance->transform_prev = instance->transform_curr; + instance->transform_checksum_prev = instance->transform_checksum_curr; + instance->on_interpolate_transform_list = false; + } + } + } + + // We maintain a mirror list for the transform updates, so we can detect when an instance + // is no longer being transformed, and remove it from the interpolate list. + SWAP(_interpolation_data.instance_transform_update_list_curr, _interpolation_data.instance_transform_update_list_prev); + + // Prepare for the next iteration. + _interpolation_data.instance_transform_update_list_curr->clear(); +} + +void RendererSceneCull::update_interpolation_frame(bool p_process) { + // MultiMesh: Update interpolation in storage. + RSG::mesh_storage->update_interpolation_frame(p_process); + + if (p_process) { + real_t f = Engine::get_singleton()->get_physics_interpolation_fraction(); + + for (const RID &rid : _interpolation_data.instance_interpolate_update_list) { + Instance *instance = instance_owner.get_or_null(rid); + if (instance) { + TransformInterpolator::interpolate_transform_3d_via_method(instance->transform_prev, instance->transform_curr, instance->transform, f, instance->interpolation_method); + +#ifdef RENDERING_SERVER_DEBUG_PHYSICS_INTERPOLATION + print_line("\t\tinterpolated: " + rtos(instance->transform.origin.x) + "\t( prev " + rtos(instance->transform_prev.origin.x) + ", curr " + rtos(instance->transform_curr.origin.x) + " ) on tick " + itos(Engine::get_singleton()->get_physics_frames())); +#endif + + // Make sure AABBs are constantly up to date through the interpolation. + _instance_queue_update(instance, true); + } + } + } +} + +void RendererSceneCull::set_physics_interpolation_enabled(bool p_enabled) { + _interpolation_data.interpolation_enabled = p_enabled; +} + +void RendererSceneCull::InterpolationData::notify_free_instance(RID p_rid, Instance &r_instance) { + r_instance.on_interpolate_list = false; + r_instance.on_interpolate_transform_list = false; + + if (!interpolation_enabled) { + return; + } + + // If the instance was on any of the lists, remove. + instance_interpolate_update_list.erase_multiple_unordered(p_rid); + instance_transform_update_list_curr->erase_multiple_unordered(p_rid); + instance_transform_update_list_prev->erase_multiple_unordered(p_rid); +} + RendererSceneCull::RendererSceneCull() { render_pass = 1; singleton = this; diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index 0039d14475..972f66d325 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -32,6 +32,7 @@ #define RENDERER_SCENE_CULL_H #include "core/math/dynamic_bvh.h" +#include "core/math/transform_interpolator.h" #include "core/templates/bin_sorted_array.h" #include "core/templates/local_vector.h" #include "core/templates/paged_allocator.h" @@ -66,6 +67,11 @@ public: static RendererSceneCull *singleton; + /* EVENT QUEUING */ + + void tick(); + void pre_draw(bool p_will_draw); + /* CAMERA API */ struct Camera { @@ -406,8 +412,16 @@ public: RID mesh_instance; //only used for meshes and when skeleton/blendshapes exist + // This is the main transform to be drawn with ... + // This will either be the interpolated transform (when using fixed timestep interpolation) + // or the ONLY transform (when not using FTI). Transform3D transform; + // For interpolation we store the current transform (this physics tick) + // and the transform in the previous tick. + Transform3D transform_curr; + Transform3D transform_prev; + float lod_bias; bool ignore_occlusion_culling; @@ -418,13 +432,23 @@ public: RS::ShadowCastingSetting cast_shadows; uint32_t layer_mask; - //fit in 32 bits - bool mirror : 8; - bool receive_shadows : 8; - bool visible : 8; - bool baked_light : 2; //this flag is only to know if it actually did use baked light - bool dynamic_gi : 2; //same above for dynamic objects - bool redraw_if_visible : 4; + // Fit in 32 bits. + bool mirror : 1; + bool receive_shadows : 1; + bool visible : 1; + bool baked_light : 1; // This flag is only to know if it actually did use baked light. + bool dynamic_gi : 1; // Same as above for dynamic objects. + bool redraw_if_visible : 1; + + bool on_interpolate_list : 1; + bool on_interpolate_transform_list : 1; + bool interpolated : 1; + TransformInterpolator::Method interpolation_method : 3; + + // For fixed timestep interpolation. + // Note 32 bits is plenty for checksum, no need for real_t + float transform_checksum_curr; + float transform_checksum_prev; Instance *lightmap = nullptr; Rect2 lightmap_uv_scale; @@ -574,6 +598,14 @@ public: baked_light = true; dynamic_gi = false; redraw_if_visible = false; + + on_interpolate_list = false; + on_interpolate_transform_list = false; + interpolated = true; + interpolation_method = TransformInterpolator::INTERP_LERP; + transform_checksum_curr = 0.0; + transform_checksum_prev = 0.0; + lightmap_slice_index = 0; lightmap = nullptr; lightmap_cull_index = 0; @@ -1027,6 +1059,8 @@ public: virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask); virtual void instance_set_pivot_data(RID p_instance, float p_sorting_offset, bool p_use_aabb_center); virtual void instance_set_transform(RID p_instance, const Transform3D &p_transform); + virtual void instance_set_interpolated(RID p_instance, bool p_interpolated); + virtual void instance_reset_physics_interpolation(RID p_instance); virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id); virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight); virtual void instance_set_surface_override_material(RID p_instance, int p_surface, RID p_material); @@ -1384,6 +1418,7 @@ public: PASS1(decals_set_filter, RS::DecalFilter) PASS1(light_projectors_set_filter, RS::LightProjectorFilter) + PASS1(lightmaps_set_bicubic_filter, bool) virtual void update(); @@ -1393,6 +1428,22 @@ public: virtual void update_visibility_notifiers(); + /* INTERPOLATION */ + + void update_interpolation_tick(bool p_process = true); + void update_interpolation_frame(bool p_process = true); + virtual void set_physics_interpolation_enabled(bool p_enabled); + + struct InterpolationData { + void notify_free_instance(RID p_rid, Instance &r_instance); + LocalVector<RID> instance_interpolate_update_list; + LocalVector<RID> instance_transform_update_lists[2]; + LocalVector<RID> *instance_transform_update_list_curr = &instance_transform_update_lists[0]; + LocalVector<RID> *instance_transform_update_list_prev = &instance_transform_update_lists[1]; + + bool interpolation_enabled = false; + } _interpolation_data; + RendererSceneCull(); virtual ~RendererSceneCull(); }; diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h index 719efa4df2..3446f5dd1b 100644 --- a/servers/rendering/renderer_scene_render.h +++ b/servers/rendering/renderer_scene_render.h @@ -338,6 +338,7 @@ public: virtual void decals_set_filter(RS::DecalFilter p_filter) = 0; virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) = 0; + virtual void lightmaps_set_bicubic_filter(bool p_enable) = 0; virtual void update() = 0; virtual ~RendererSceneRender() {} diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 80c1f67d8a..7e45eba1de 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -786,6 +786,7 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) { if (OS::get_singleton()->get_current_rendering_driver_name().begins_with("opengl3")) { if (blits.size() > 0) { RSG::rasterizer->blit_render_targets_to_screen(vp->viewport_to_screen, blits.ptr(), blits.size()); + RSG::rasterizer->gl_end_frame(p_swap_buffers); } } else if (blits.size() > 0) { if (!blit_to_screen_list.has(vp->viewport_to_screen)) { @@ -796,7 +797,6 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) { blit_to_screen_list[vp->viewport_to_screen].push_back(blits[b]); } } - RSG::rasterizer->end_viewport(p_swap_buffers && blits.size() > 0); } } } else @@ -826,10 +826,10 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) { Vector<BlitToScreen> blit_to_screen_vec; blit_to_screen_vec.push_back(blit); RSG::rasterizer->blit_render_targets_to_screen(vp->viewport_to_screen, blit_to_screen_vec.ptr(), 1); + RSG::rasterizer->gl_end_frame(p_swap_buffers); } else { blit_to_screen_list[vp->viewport_to_screen].push_back(blit); } - RSG::rasterizer->end_viewport(p_swap_buffers); } } @@ -899,6 +899,7 @@ void RendererViewport::viewport_set_use_xr(RID p_viewport, bool p_use_xr) { void RendererViewport::viewport_set_scaling_3d_mode(RID p_viewport, RS::ViewportScaling3DMode p_mode) { Viewport *viewport = viewport_owner.get_or_null(p_viewport); ERR_FAIL_NULL(viewport); + ERR_FAIL_COND_EDMSG(p_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR && OS::get_singleton()->get_current_rendering_method() != "forward_plus", "FSR1 is only available when using the Forward+ renderer."); ERR_FAIL_COND_EDMSG(p_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR2 && OS::get_singleton()->get_current_rendering_method() != "forward_plus", "FSR2 is only available when using the Forward+ renderer."); if (viewport->scaling_3d_mode == p_mode) { @@ -1270,6 +1271,13 @@ void RendererViewport::viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d RSG::texture_storage->render_target_set_use_hdr(viewport->render_target, p_use_hdr_2d); } +bool RendererViewport::viewport_is_using_hdr_2d(RID p_viewport) const { + Viewport *viewport = viewport_owner.get_or_null(p_viewport); + ERR_FAIL_NULL_V(viewport, false); + + return viewport->use_hdr_2d; +} + void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode) { Viewport *viewport = viewport_owner.get_or_null(p_viewport); ERR_FAIL_NULL(viewport); diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index b36fc7f57f..bf97905f86 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -262,6 +262,8 @@ public: void viewport_set_transparent_background(RID p_viewport, bool p_enabled); void viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d); + bool viewport_is_using_hdr_2d(RID p_viewport) const; + void viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform); void viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer); diff --git a/servers/rendering/rendering_context_driver.cpp b/servers/rendering/rendering_context_driver.cpp index 19c0b0838c..b623be4098 100644 --- a/servers/rendering/rendering_context_driver.cpp +++ b/servers/rendering/rendering_context_driver.cpp @@ -83,3 +83,88 @@ void RenderingContextDriver::window_destroy(DisplayServer::WindowID p_window) { window_surface_map.erase(p_window); } + +String RenderingContextDriver::get_driver_and_device_memory_report() const { + String report; + + const uint32_t num_tracked_obj_types = static_cast<uint32_t>(get_tracked_object_type_count()); + + report += "=== Driver Memory Report ==="; + + report += "\nLaunch with --extra-gpu-memory-tracking and build with " + "DEBUG_ENABLED for this functionality to work."; + report += "\nDevice memory may be unavailable if the API does not support it" + "(e.g. VK_EXT_device_memory_report is unsupported)."; + report += "\n"; + + report += "\nTotal Driver Memory:"; + report += String::num_real(double(get_driver_total_memory()) / (1024.0 * 1024.0)); + report += " MB"; + report += "\nTotal Driver Num Allocations: "; + report += String::num_uint64(get_driver_allocation_count()); + + report += "\nTotal Device Memory:"; + report += String::num_real(double(get_device_total_memory()) / (1024.0 * 1024.0)); + report += " MB"; + report += "\nTotal Device Num Allocations: "; + report += String::num_uint64(get_device_allocation_count()); + + report += "\n\nMemory use by object type (CSV format):"; + report += "\n\nCategory; Driver memory in MB; Driver Allocation Count; " + "Device memory in MB; Device Allocation Count"; + + for (uint32_t i = 0u; i < num_tracked_obj_types; ++i) { + report += "\n"; + report += get_tracked_object_name(i); + report += ";"; + report += String::num_real(double(get_driver_memory_by_object_type(i)) / (1024.0 * 1024.0)); + report += ";"; + report += String::num_uint64(get_driver_allocs_by_object_type(i)); + report += ";"; + report += String::num_real(double(get_device_memory_by_object_type(i)) / (1024.0 * 1024.0)); + report += ";"; + report += String::num_uint64(get_device_allocs_by_object_type(i)); + } + + return report; +} + +const char *RenderingContextDriver::get_tracked_object_name(uint32_t p_type_index) const { + return "Tracking Unsupported by API"; +} + +uint64_t RenderingContextDriver::get_tracked_object_type_count() const { + return 0; +} + +uint64_t RenderingContextDriver::get_driver_total_memory() const { + return 0; +} + +uint64_t RenderingContextDriver::get_driver_allocation_count() const { + return 0; +} + +uint64_t RenderingContextDriver::get_driver_memory_by_object_type(uint32_t) const { + return 0; +} + +uint64_t RenderingContextDriver::get_driver_allocs_by_object_type(uint32_t) const { + return 0; +} + +uint64_t RenderingContextDriver::get_device_total_memory() const { + return 0; +} + +uint64_t RenderingContextDriver::get_device_allocation_count() const { + return 0; +} + +uint64_t RenderingContextDriver::get_device_memory_by_object_type(uint32_t) const { + return 0; +} + +uint64_t RenderingContextDriver::get_device_allocs_by_object_type(uint32_t) const { + return 0; +} diff --git a/servers/rendering/rendering_context_driver.h b/servers/rendering/rendering_context_driver.h index 539b3814a0..2e5951ae4f 100644 --- a/servers/rendering/rendering_context_driver.h +++ b/servers/rendering/rendering_context_driver.h @@ -101,6 +101,21 @@ public: virtual bool surface_get_needs_resize(SurfaceID p_surface) const = 0; virtual void surface_destroy(SurfaceID p_surface) = 0; virtual bool is_debug_utils_enabled() const = 0; + + String get_driver_and_device_memory_report() const; + + virtual const char *get_tracked_object_name(uint32_t p_type_index) const; + virtual uint64_t get_tracked_object_type_count() const; + + virtual uint64_t get_driver_total_memory() const; + virtual uint64_t get_driver_allocation_count() const; + virtual uint64_t get_driver_memory_by_object_type(uint32_t p_type) const; + virtual uint64_t get_driver_allocs_by_object_type(uint32_t p_type) const; + + virtual uint64_t get_device_total_memory() const; + virtual uint64_t get_device_allocation_count() const; + virtual uint64_t get_device_memory_by_object_type(uint32_t p_type) const; + virtual uint64_t get_device_allocs_by_object_type(uint32_t p_type) const; }; #endif // RENDERING_CONTEXT_DRIVER_H diff --git a/servers/rendering/rendering_device.compat.inc b/servers/rendering/rendering_device.compat.inc index ee9481280a..77e44bbc5e 100644 --- a/servers/rendering/rendering_device.compat.inc +++ b/servers/rendering/rendering_device.compat.inc @@ -86,7 +86,11 @@ RenderingDevice::FinalAction RenderingDevice::_convert_final_action_84976(FinalA } RenderingDevice::DrawListID RenderingDevice::_draw_list_begin_bind_compat_84976(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures) { - return draw_list_begin(p_framebuffer, _convert_initial_action_84976(p_initial_color_action), _convert_final_action_84976(p_final_color_action), _convert_initial_action_84976(p_initial_depth_action), _convert_final_action_84976(p_final_depth_action), p_clear_color_values, p_clear_depth, p_clear_stencil, p_region); + return draw_list_begin(p_framebuffer, _convert_initial_action_84976(p_initial_color_action), _convert_final_action_84976(p_final_color_action), _convert_initial_action_84976(p_initial_depth_action), _convert_final_action_84976(p_final_depth_action), p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, RDD::BreadcrumbMarker::NONE); +} + +RenderingDevice::DrawListID RenderingDevice::_draw_list_begin_bind_compat_90993(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region) { + return draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, RDD::BreadcrumbMarker::NONE); } RenderingDevice::ComputeListID RenderingDevice::_compute_list_begin_bind_compat_84976(bool p_allow_draw_overlap) { @@ -123,9 +127,11 @@ RenderingDevice::FramebufferFormatID RenderingDevice::_screen_get_framebuffer_fo void RenderingDevice::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("shader_create_from_bytecode", "binary_data"), &RenderingDevice::_shader_create_from_bytecode_bind_compat_79606); + ClassDB::bind_compatibility_method(D_METHOD("draw_list_end", "post_barrier"), &RenderingDevice::_draw_list_end_bind_compat_81356, DEFVAL(7)); ClassDB::bind_compatibility_method(D_METHOD("compute_list_end", "post_barrier"), &RenderingDevice::_compute_list_end_bind_compat_81356, DEFVAL(7)); ClassDB::bind_compatibility_method(D_METHOD("barrier", "from", "to"), &RenderingDevice::_barrier_bind_compat_81356, DEFVAL(7), DEFVAL(7)); + ClassDB::bind_compatibility_method(D_METHOD("draw_list_end", "post_barrier"), &RenderingDevice::_draw_list_end_bind_compat_84976, DEFVAL(0x7FFF)); ClassDB::bind_compatibility_method(D_METHOD("compute_list_end", "post_barrier"), &RenderingDevice::_compute_list_end_bind_compat_84976, DEFVAL(0x7FFF)); ClassDB::bind_compatibility_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "storage_textures"), &RenderingDevice::_draw_list_begin_bind_compat_84976, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(TypedArray<RID>())); @@ -136,7 +142,10 @@ void RenderingDevice::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("texture_copy", "from_texture", "to_texture", "from_pos", "to_pos", "size", "src_mipmap", "dst_mipmap", "src_layer", "dst_layer", "post_barrier"), &RenderingDevice::_texture_copy_bind_compat_84976, DEFVAL(0x7FFF)); ClassDB::bind_compatibility_method(D_METHOD("texture_clear", "texture", "color", "base_mipmap", "mipmap_count", "base_layer", "layer_count", "post_barrier"), &RenderingDevice::_texture_clear_bind_compat_84976, DEFVAL(0x7FFF)); ClassDB::bind_compatibility_method(D_METHOD("texture_resolve_multisample", "from_texture", "to_texture", "post_barrier"), &RenderingDevice::_texture_resolve_multisample_bind_compat_84976, DEFVAL(0x7FFF)); + ClassDB::bind_compatibility_method(D_METHOD("screen_get_framebuffer_format"), &RenderingDevice::_screen_get_framebuffer_format_bind_compat_87340); + + ClassDB::bind_compatibility_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region"), &RenderingDevice::_draw_list_begin_bind_compat_90993, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2())); } #endif diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 38f1fe57bd..f0f267c246 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -82,11 +82,12 @@ static String _get_device_type_name(const RenderingContextDriver::Device &p_devi } static uint32_t _get_device_type_score(const RenderingContextDriver::Device &p_device) { + static const bool prefer_integrated = OS::get_singleton()->get_user_prefers_integrated_gpu(); switch (p_device.type) { case RenderingContextDriver::DEVICE_TYPE_INTEGRATED_GPU: - return 4; + return prefer_integrated ? 5 : 4; case RenderingContextDriver::DEVICE_TYPE_DISCRETE_GPU: - return 5; + return prefer_integrated ? 4 : 5; case RenderingContextDriver::DEVICE_TYPE_VIRTUAL_GPU: return 3; case RenderingContextDriver::DEVICE_TYPE_CPU: @@ -500,6 +501,8 @@ Error RenderingDevice::buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t Error RenderingDevice::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data) { _THREAD_SAFE_METHOD_ + copy_bytes_count += p_size; + ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER, "Updating buffers is forbidden during creation of a draw list"); ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER, @@ -513,9 +516,23 @@ Error RenderingDevice::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p ERR_FAIL_COND_V_MSG(p_offset + p_size > buffer->size, ERR_INVALID_PARAMETER, "Attempted to write buffer (" + itos((p_offset + p_size) - buffer->size) + " bytes) past the end."); + gpu_copy_count++; + return _buffer_update(buffer, p_buffer, p_offset, (uint8_t *)p_data, p_size, true); } +String RenderingDevice::get_perf_report() const { + return perf_report_text; +} + +void RenderingDevice::update_perf_report() { + perf_report_text = " gpu:" + String::num_int64(gpu_copy_count); + perf_report_text += " bytes:" + String::num_int64(copy_bytes_count); + + gpu_copy_count = 0; + copy_bytes_count = 0; +} + Error RenderingDevice::buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size) { _THREAD_SAFE_METHOD_ @@ -1148,11 +1165,7 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve ERR_FAIL_COND_V_MSG(p_validate_can_update && !(texture->usage_flags & TEXTURE_USAGE_CAN_UPDATE_BIT), ERR_INVALID_PARAMETER, "Texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT` to be set to be updatable."); - uint32_t layer_count = texture->layers; - if (texture->type == TEXTURE_TYPE_CUBE || texture->type == TEXTURE_TYPE_CUBE_ARRAY) { - layer_count *= 6; - } - ERR_FAIL_COND_V(p_layer >= layer_count, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_layer >= texture->layers, ERR_INVALID_PARAMETER); uint32_t width, height; uint32_t tight_mip_size = get_image_format_required_size(texture->format, texture->width, texture->height, texture->depth, texture->mipmaps, &width, &height); @@ -1584,11 +1597,7 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye ERR_FAIL_COND_V_MSG(!(tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), Vector<uint8_t>(), "Texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved."); - uint32_t layer_count = tex->layers; - if (tex->type == TEXTURE_TYPE_CUBE || tex->type == TEXTURE_TYPE_CUBE_ARRAY) { - layer_count *= 6; - } - ERR_FAIL_COND_V(p_layer >= layer_count, Vector<uint8_t>()); + ERR_FAIL_COND_V(p_layer >= tex->layers, Vector<uint8_t>()); if ((tex->usage_flags & TEXTURE_USAGE_CPU_READ_BIT)) { // Does not need anything fancy, map and read. @@ -1606,7 +1615,7 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye driver->texture_get_copyable_layout(tex->driver_id, subres, &mip_layouts[i]); // Assuming layers are tightly packed. If this is not true on some driver, we must modify the copy algorithm. - DEV_ASSERT(mip_layouts[i].layer_pitch == mip_layouts[i].size / layer_count); + DEV_ASSERT(mip_layouts[i].layer_pitch == mip_layouts[i].size / tex->layers); work_buffer_size = STEPIFY(work_buffer_size, work_mip_alignment) + mip_layouts[i].size; } @@ -1617,9 +1626,6 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye thread_local LocalVector<RDD::BufferTextureCopyRegion> command_buffer_texture_copy_regions_vector; command_buffer_texture_copy_regions_vector.clear(); - uint32_t block_w = 0, block_h = 0; - get_compressed_image_format_block_dimensions(tex->format, block_w, block_h); - uint32_t w = tex->width; uint32_t h = tex->height; uint32_t d = tex->depth; @@ -1635,8 +1641,8 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye copy_region.texture_region_size.z = d; command_buffer_texture_copy_regions_vector.push_back(copy_region); - w = MAX(block_w, w >> 1); - h = MAX(block_h, h >> 1); + w = (w >> 1); + h = (h >> 1); d = MAX(1u, d >> 1); } @@ -1653,6 +1659,10 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye const uint8_t *read_ptr = driver->buffer_map(tmp_buffer); ERR_FAIL_NULL_V(read_ptr, Vector<uint8_t>()); + uint32_t block_w = 0; + uint32_t block_h = 0; + get_compressed_image_format_block_dimensions(tex->format, block_w, block_h); + Vector<uint8_t> buffer_data; uint32_t tight_buffer_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps); buffer_data.resize(tight_buffer_size); @@ -1750,18 +1760,14 @@ Error RenderingDevice::texture_copy(RID p_from_texture, RID p_to_texture, const ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_FROM_BIT), ERR_INVALID_PARAMETER, "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to be retrieved."); - uint32_t src_layer_count = src_tex->layers; uint32_t src_width, src_height, src_depth; get_image_format_required_size(src_tex->format, src_tex->width, src_tex->height, src_tex->depth, p_src_mipmap + 1, &src_width, &src_height, &src_depth); - if (src_tex->type == TEXTURE_TYPE_CUBE || src_tex->type == TEXTURE_TYPE_CUBE_ARRAY) { - src_layer_count *= 6; - } ERR_FAIL_COND_V(p_from.x < 0 || p_from.x + p_size.x > src_width, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_from.y < 0 || p_from.y + p_size.y > src_height, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_from.z < 0 || p_from.z + p_size.z > src_depth, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_src_mipmap >= src_tex->mipmaps, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_src_layer >= src_layer_count, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_src_layer >= src_tex->layers, ERR_INVALID_PARAMETER); Texture *dst_tex = texture_owner.get_or_null(p_to_texture); ERR_FAIL_NULL_V(dst_tex, ERR_INVALID_PARAMETER); @@ -1771,18 +1777,14 @@ Error RenderingDevice::texture_copy(RID p_from_texture, RID p_to_texture, const ERR_FAIL_COND_V_MSG(!(dst_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, "Destination texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be retrieved."); - uint32_t dst_layer_count = dst_tex->layers; uint32_t dst_width, dst_height, dst_depth; get_image_format_required_size(dst_tex->format, dst_tex->width, dst_tex->height, dst_tex->depth, p_dst_mipmap + 1, &dst_width, &dst_height, &dst_depth); - if (dst_tex->type == TEXTURE_TYPE_CUBE || dst_tex->type == TEXTURE_TYPE_CUBE_ARRAY) { - dst_layer_count *= 6; - } ERR_FAIL_COND_V(p_to.x < 0 || p_to.x + p_size.x > dst_width, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_to.y < 0 || p_to.y + p_size.y > dst_height, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_to.z < 0 || p_to.z + p_size.z > dst_depth, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_dst_mipmap >= dst_tex->mipmaps, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_dst_layer >= dst_layer_count, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_dst_layer >= dst_tex->layers, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(src_tex->read_aspect_flags != dst_tex->read_aspect_flags, ERR_INVALID_PARAMETER, "Source and destination texture must be of the same type (color or depth)."); @@ -1878,13 +1880,8 @@ Error RenderingDevice::texture_clear(RID p_texture, const Color &p_color, uint32 ERR_FAIL_COND_V_MSG(!(src_tex->usage_flags & TEXTURE_USAGE_CAN_COPY_TO_BIT), ERR_INVALID_PARAMETER, "Source texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT` to be set to be cleared."); - uint32_t src_layer_count = src_tex->layers; - if (src_tex->type == TEXTURE_TYPE_CUBE || src_tex->type == TEXTURE_TYPE_CUBE_ARRAY) { - src_layer_count *= 6; - } - ERR_FAIL_COND_V(p_base_mipmap + p_mipmaps > src_tex->mipmaps, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_base_layer + p_layers > src_layer_count, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_base_layer + p_layers > src_tex->layers, ERR_INVALID_PARAMETER); RDD::TextureSubresourceRange range; range.aspect = src_tex->read_aspect_flags; @@ -2846,6 +2843,7 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p for (int j = 0; j < (int)uniform_count; j++) { if (uniforms[j].binding == set_uniform.binding) { uniform_idx = j; + break; } } ERR_FAIL_COND_V_MSG(uniform_idx == -1, RID(), @@ -3086,7 +3084,7 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p ERR_FAIL_NULL_V_MSG(buffer, RID(), "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); ERR_FAIL_COND_V_MSG(buffer->size < (uint32_t)set_uniform.length, RID(), - "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " is smaller than size of shader uniform: (" + itos(set_uniform.length) + ")."); + "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + ") is smaller than size of shader uniform: (" + itos(set_uniform.length) + ")."); if (buffer->draw_tracker != nullptr) { draw_trackers.push_back(buffer->draw_tracker); @@ -3115,7 +3113,7 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p // If 0, then it's sized on link time. ERR_FAIL_COND_V_MSG(set_uniform.length > 0 && buffer->size != (uint32_t)set_uniform.length, RID(), - "Storage buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " does not match size of shader uniform: (" + itos(set_uniform.length) + ")."); + "Storage buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + ") does not match size of shader uniform: (" + itos(set_uniform.length) + ")."); if (set_uniform.writable && _buffer_make_mutable(buffer, buffer_id)) { // The buffer must be mutable if it's used for writing. @@ -3260,6 +3258,7 @@ RID RenderingDevice::render_pipeline_create(RID p_shader, FramebufferFormatID p_ for (int j = 0; j < vd.vertex_formats.size(); j++) { if (vd.vertex_formats[j].location == i) { found = true; + break; } } @@ -3500,7 +3499,12 @@ Error RenderingDevice::screen_prepare_for_drawing(DisplayServer::WindowID p_scre framebuffer = driver->swap_chain_acquire_framebuffer(main_queue, it->value, resize_required); } - ERR_FAIL_COND_V_MSG(framebuffer.id == 0, FAILED, "Unable to acquire framebuffer."); + if (framebuffer.id == 0) { + // Some drivers like NVIDIA are fast enough to invalidate the swap chain between resizing and acquisition (GH-94104). + // This typically occurs during continuous window resizing operations, especially if done quickly. + // Allow this to fail silently since it has no visual consequences. + return ERR_CANT_CREATE; + } // Store the framebuffer that will be used next to draw to this screen. screen_framebuffers[p_screen] = framebuffer; @@ -3588,7 +3592,7 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin_for_screen(DisplayS clear_value.color = p_clear_color; RDD::RenderPassID render_pass = driver->swap_chain_get_render_pass(sc_it->value); - draw_graph.add_draw_list_begin(render_pass, fb_it->value, viewport, clear_value, true, false); + draw_graph.add_draw_list_begin(render_pass, fb_it->value, viewport, clear_value, true, false, RDD::BreadcrumbMarker::BLIT_PASS); _draw_list_set_viewport(viewport); _draw_list_set_scissor(viewport); @@ -3637,7 +3641,7 @@ Error RenderingDevice::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, return OK; } -Error RenderingDevice::_draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass) { +Error RenderingDevice::_draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass, uint32_t p_breadcrumb) { thread_local LocalVector<RDD::RenderPassClearValue> clear_values; thread_local LocalVector<RDG::ResourceTracker *> resource_trackers; thread_local LocalVector<RDG::ResourceUsage> resource_usages; @@ -3685,7 +3689,7 @@ Error RenderingDevice::_draw_list_render_pass_begin(Framebuffer *p_framebuffer, } } - draw_graph.add_draw_list_begin(p_render_pass, p_framebuffer_driver_id, Rect2i(p_viewport_offset, p_viewport_size), clear_values, uses_color, uses_depth); + draw_graph.add_draw_list_begin(p_render_pass, p_framebuffer_driver_id, Rect2i(p_viewport_offset, p_viewport_size), clear_values, uses_color, uses_depth, p_breadcrumb); draw_graph.add_draw_list_usages(resource_trackers, resource_usages); // Mark textures as bound. @@ -3747,7 +3751,7 @@ void RenderingDevice::_draw_list_insert_clear_region(DrawList *p_draw_list, Fram draw_graph.add_draw_list_clear_attachments(clear_attachments, rect); } -RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region) { +RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, uint32_t p_breadcrumb) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); @@ -3793,7 +3797,7 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer, Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &fb_driver_id, &render_pass, &draw_list_subpass_count); ERR_FAIL_COND_V(err != OK, INVALID_ID); - err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, fb_driver_id, render_pass); + err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, fb_driver_id, render_pass, p_breadcrumb); if (err != OK) { return INVALID_ID; @@ -5080,13 +5084,19 @@ void RenderingDevice::swap_buffers() { void RenderingDevice::submit() { _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_MSG(is_main_instance, "Only local devices can submit and sync."); + ERR_FAIL_COND_MSG(local_device_processing, "device already submitted, call sync to wait until done."); _end_frame(); _execute_frame(false); + local_device_processing = true; } void RenderingDevice::sync() { _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_MSG(is_main_instance, "Only local devices can submit and sync."); + ERR_FAIL_COND_MSG(!local_device_processing, "sync can only be called after a submit"); _begin_frame(); + local_device_processing = false; } void RenderingDevice::_free_pending_resources(int p_frame) { @@ -5202,6 +5212,8 @@ void RenderingDevice::_begin_frame() { frames[frame].draw_fence_signaled = false; } + update_perf_report(); + // Begin recording on the frame's command buffers. driver->begin_segment(frame, frames_drawn++); driver->command_buffer_begin(frames[frame].setup_command_buffer); @@ -5338,7 +5350,7 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ Error err; RenderingContextDriver::SurfaceID main_surface = 0; - const bool main_instance = (singleton == this) && (p_main_window != DisplayServer::INVALID_WINDOW_ID); + is_main_instance = (singleton == this) && (p_main_window != DisplayServer::INVALID_WINDOW_ID); if (p_main_window != DisplayServer::INVALID_WINDOW_ID) { // Retrieve the surface from the main window if it was specified. main_surface = p_context->surface_get_from_window(p_main_window); @@ -5380,13 +5392,13 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ frame = 0; frames.resize(frame_count); - max_timestamp_query_elements = 256; + max_timestamp_query_elements = GLOBAL_GET("debug/settings/profiler/max_timestamp_query_elements"); device = context->device_get(device_index); err = driver->initialize(device_index, frame_count); ERR_FAIL_COND_V_MSG(err != OK, FAILED, "Failed to initialize driver for device."); - if (main_instance) { + if (is_main_instance) { // Only the singleton instance with a display should print this information. String rendering_method; if (OS::get_singleton()->get_current_rendering_method() == "mobile") { @@ -5507,14 +5519,14 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ for (uint32_t i = 0; i < frames.size(); i++) { // Staging was never used, create a block. err = _insert_staging_block(); - ERR_CONTINUE(err != OK); + ERR_FAIL_COND_V(err, FAILED); } draw_list = nullptr; compute_list = nullptr; bool project_pipeline_cache_enable = GLOBAL_GET("rendering/rendering_device/pipeline_cache/enable"); - if (main_instance && project_pipeline_cache_enable) { + if (is_main_instance && project_pipeline_cache_enable) { // Only the instance that is not a local device and is also the singleton is allowed to manage a pipeline cache. pipeline_cache_file_path = vformat("user://vulkan/pipelines.%s.%s", OS::get_singleton()->get_current_rendering_method(), @@ -5631,7 +5643,7 @@ void RenderingDevice::_free_rids(T &p_owner, const char *p_type) { void RenderingDevice::capture_timestamp(const String &p_name) { ERR_FAIL_COND_MSG(draw_list != nullptr && draw_list->state.draw_count > 0, "Capturing timestamps during draw list creation is not allowed. Offending timestamp was: " + p_name); ERR_FAIL_COND_MSG(compute_list != nullptr && compute_list->state.dispatch_count > 0, "Capturing timestamps during compute list creation is not allowed. Offending timestamp was: " + p_name); - ERR_FAIL_COND(frames[frame].timestamp_count >= max_timestamp_query_elements); + ERR_FAIL_COND_MSG(frames[frame].timestamp_count >= max_timestamp_query_elements, vformat("Tried capturing more timestamps than the configured maximum (%d). You can increase this limit in the project settings under 'Debug/Settings' called 'Max Timestamp Query Elements'.", max_timestamp_query_elements)); draw_graph.add_capture_timestamp(frames[frame].timestamp_pool, frames[frame].timestamp_count); @@ -5712,6 +5724,50 @@ uint64_t RenderingDevice::get_driver_resource(DriverResource p_resource, RID p_r return driver->get_resource_native_handle(p_resource, driver_id); } +String RenderingDevice::get_driver_and_device_memory_report() const { + return context->get_driver_and_device_memory_report(); +} + +String RenderingDevice::get_tracked_object_name(uint32_t p_type_index) const { + return context->get_tracked_object_name(p_type_index); +} + +uint64_t RenderingDevice::get_tracked_object_type_count() const { + return context->get_tracked_object_type_count(); +} + +uint64_t RenderingDevice::get_driver_total_memory() const { + return context->get_driver_total_memory(); +} + +uint64_t RenderingDevice::get_driver_allocation_count() const { + return context->get_driver_allocation_count(); +} + +uint64_t RenderingDevice::get_driver_memory_by_object_type(uint32_t p_type) const { + return context->get_driver_memory_by_object_type(p_type); +} + +uint64_t RenderingDevice::get_driver_allocs_by_object_type(uint32_t p_type) const { + return context->get_driver_allocs_by_object_type(p_type); +} + +uint64_t RenderingDevice::get_device_total_memory() const { + return context->get_device_total_memory(); +} + +uint64_t RenderingDevice::get_device_allocation_count() const { + return context->get_device_allocation_count(); +} + +uint64_t RenderingDevice::get_device_memory_by_object_type(uint32_t type) const { + return context->get_device_memory_by_object_type(type); +} + +uint64_t RenderingDevice::get_device_allocs_by_object_type(uint32_t type) const { + return context->get_device_allocs_by_object_type(type); +} + uint32_t RenderingDevice::get_captured_timestamps_count() const { return frames[frame].timestamp_result_count; } @@ -5954,7 +6010,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_list_begin_for_screen", "screen", "clear_color"), &RenderingDevice::draw_list_begin_for_screen, DEFVAL(DisplayServer::MAIN_WINDOW_ID), DEFVAL(Color())); - ClassDB::bind_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region"), &RenderingDevice::draw_list_begin, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2())); + ClassDB::bind_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "breadcrumb"), &RenderingDevice::draw_list_begin, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(0)); #ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("draw_list_begin_split", "framebuffer", "splits", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "storage_textures"), &RenderingDevice::_draw_list_begin_split, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(TypedArray<RID>())); #endif @@ -6024,6 +6080,20 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("get_driver_resource", "resource", "rid", "index"), &RenderingDevice::get_driver_resource); + ClassDB::bind_method(D_METHOD("get_perf_report"), &RenderingDevice::get_perf_report); + + ClassDB::bind_method(D_METHOD("get_driver_and_device_memory_report"), &RenderingDevice::get_driver_and_device_memory_report); + ClassDB::bind_method(D_METHOD("get_tracked_object_name", "type_index"), &RenderingDevice::get_tracked_object_name); + ClassDB::bind_method(D_METHOD("get_tracked_object_type_count"), &RenderingDevice::get_tracked_object_type_count); + ClassDB::bind_method(D_METHOD("get_driver_total_memory"), &RenderingDevice::get_driver_total_memory); + ClassDB::bind_method(D_METHOD("get_driver_allocation_count"), &RenderingDevice::get_driver_allocation_count); + ClassDB::bind_method(D_METHOD("get_driver_memory_by_object_type", "type"), &RenderingDevice::get_driver_memory_by_object_type); + ClassDB::bind_method(D_METHOD("get_driver_allocs_by_object_type", "type"), &RenderingDevice::get_driver_allocs_by_object_type); + ClassDB::bind_method(D_METHOD("get_device_total_memory"), &RenderingDevice::get_device_total_memory); + ClassDB::bind_method(D_METHOD("get_device_allocation_count"), &RenderingDevice::get_device_allocation_count); + ClassDB::bind_method(D_METHOD("get_device_memory_by_object_type", "type"), &RenderingDevice::get_device_memory_by_object_type); + ClassDB::bind_method(D_METHOD("get_device_allocs_by_object_type", "type"), &RenderingDevice::get_device_allocs_by_object_type); + BIND_ENUM_CONSTANT(DEVICE_TYPE_OTHER); BIND_ENUM_CONSTANT(DEVICE_TYPE_INTEGRATED_GPU); BIND_ENUM_CONSTANT(DEVICE_TYPE_DISCRETE_GPU); @@ -6546,6 +6616,20 @@ void RenderingDevice::_bind_methods() { BIND_CONSTANT(INVALID_ID); BIND_CONSTANT(INVALID_FORMAT_ID); + + BIND_ENUM_CONSTANT(NONE); + BIND_ENUM_CONSTANT(REFLECTION_PROBES); + BIND_ENUM_CONSTANT(SKY_PASS); + BIND_ENUM_CONSTANT(LIGHTMAPPER_PASS); + BIND_ENUM_CONSTANT(SHADOW_PASS_DIRECTIONAL); + BIND_ENUM_CONSTANT(SHADOW_PASS_CUBE); + BIND_ENUM_CONSTANT(OPAQUE_PASS); + BIND_ENUM_CONSTANT(ALPHA_PASS); + BIND_ENUM_CONSTANT(TRANSPARENT_PASS); + BIND_ENUM_CONSTANT(POST_PROCESSING_PASS); + BIND_ENUM_CONSTANT(BLIT_PASS); + BIND_ENUM_CONSTANT(UI_PASS); + BIND_ENUM_CONSTANT(DEBUG_PASS); } RenderingDevice::~RenderingDevice() { diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 38ffd5d23d..1405f585b2 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -88,6 +88,9 @@ private: RenderingDeviceDriver *driver = nullptr; RenderingContextDriver::Device device; + bool local_device_processing = false; + bool is_main_instance = false; + protected: static void _bind_methods(); @@ -180,6 +183,12 @@ private: Buffer *_get_buffer_from_owner(RID p_buffer); Error _buffer_update(Buffer *p_buffer, RID p_buffer_id, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_queue = false, uint32_t p_required_align = 32); + void update_perf_report(); + + uint32_t gpu_copy_count = 0; + uint32_t copy_bytes_count = 0; + String perf_report_text; + RID_Owner<Buffer> uniform_buffer_owner; RID_Owner<Buffer> storage_buffer_owner; RID_Owner<Buffer> texture_buffer_owner; @@ -368,7 +377,9 @@ public: // used for the render pipelines. struct AttachmentFormat { - enum { UNUSED_ATTACHMENT = 0xFFFFFFFF }; + enum : uint32_t { + UNUSED_ATTACHMENT = 0xFFFFFFFF + }; DataFormat format; TextureSamples samples; uint32_t usage_flags; @@ -812,6 +823,7 @@ private: void _draw_list_end_bind_compat_81356(BitField<BarrierMask> p_post_barrier); void _compute_list_end_bind_compat_81356(BitField<BarrierMask> p_post_barrier); void _barrier_bind_compat_81356(BitField<BarrierMask> p_from, BitField<BarrierMask> p_to); + void _draw_list_end_bind_compat_84976(BitField<BarrierMask> p_post_barrier); void _compute_list_end_bind_compat_84976(BitField<BarrierMask> p_post_barrier); InitialAction _convert_initial_action_84976(InitialAction p_old_initial_action); @@ -824,7 +836,10 @@ private: Error _texture_copy_bind_compat_84976(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField<BarrierMask> p_post_barrier); Error _texture_clear_bind_compat_84976(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, BitField<BarrierMask> p_post_barrier); Error _texture_resolve_multisample_bind_compat_84976(RID p_from_texture, RID p_to_texture, BitField<BarrierMask> p_post_barrier); + FramebufferFormatID _screen_get_framebuffer_format_bind_compat_87340() const; + + DrawListID _draw_list_begin_bind_compat_90993(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2()); #endif public: @@ -853,6 +868,7 @@ public: /******************/ /**** UNIFORMS ****/ /******************/ + String get_perf_report() const; enum StorageBufferUsage { STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT = 1, @@ -1136,7 +1152,7 @@ private: void _draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, RDD::FramebufferID *r_framebuffer, RDD::RenderPassID *r_render_pass, uint32_t *r_subpass_count); - Error _draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass); + Error _draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass, uint32_t p_breadcrumb); void _draw_list_set_viewport(Rect2i p_rect); void _draw_list_set_scissor(Rect2i p_rect); _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); @@ -1145,7 +1161,7 @@ private: public: DrawListID draw_list_begin_for_screen(DisplayServer::WindowID p_screen = 0, const Color &p_clear_color = Color()); - DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2()); + DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), uint32_t p_breadcrumb = 0); void draw_list_set_blend_constants(DrawListID p_list, const Color &p_color); void draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline); @@ -1403,6 +1419,21 @@ public: uint64_t get_driver_resource(DriverResource p_resource, RID p_rid = RID(), uint64_t p_index = 0); + String get_driver_and_device_memory_report() const; + + String get_tracked_object_name(uint32_t p_type_index) const; + uint64_t get_tracked_object_type_count() const; + + uint64_t get_driver_total_memory() const; + uint64_t get_driver_allocation_count() const; + uint64_t get_driver_memory_by_object_type(uint32_t p_type) const; + uint64_t get_driver_allocs_by_object_type(uint32_t p_type) const; + + uint64_t get_device_total_memory() const; + uint64_t get_device_allocation_count() const; + uint64_t get_device_memory_by_object_type(uint32_t p_type) const; + uint64_t get_device_allocs_by_object_type(uint32_t p_type) const; + static RenderingDevice *get_singleton(); RenderingDevice(); @@ -1475,6 +1506,7 @@ VARIANT_ENUM_CAST(RenderingDevice::FinalAction) VARIANT_ENUM_CAST(RenderingDevice::Limit) VARIANT_ENUM_CAST(RenderingDevice::MemoryType) VARIANT_ENUM_CAST(RenderingDevice::Features) +VARIANT_ENUM_CAST(RenderingDevice::BreadcrumbMarker) #ifndef DISABLE_DEPRECATED VARIANT_BITFIELD_CAST(RenderingDevice::BarrierMask); diff --git a/servers/rendering/rendering_device_binds.cpp b/servers/rendering/rendering_device_binds.cpp index 3678b70254..d9ca286b15 100644 --- a/servers/rendering/rendering_device_binds.cpp +++ b/servers/rendering/rendering_device_binds.cpp @@ -112,7 +112,7 @@ Error RDShaderFile::parse_versions_from_text(const String &p_text, const String } Vector<String> slices = l.get_slice(";", 0).split("="); String version = slices[0].strip_edges(); - if (!version.is_valid_identifier()) { + if (!version.is_valid_ascii_identifier()) { base_error = "Version names must be valid identifiers, found '" + version + "' instead."; break; } @@ -157,9 +157,6 @@ Error RDShaderFile::parse_versions_from_text(const String &p_text, const String } } - Ref<RDShaderFile> shader_file; - shader_file.instantiate(); - if (base_error.is_empty()) { if (stage_found[RD::SHADER_STAGE_COMPUTE] && stages_found > 1) { ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "When writing compute shaders, [compute] mustbe the only stage present."); diff --git a/servers/rendering/rendering_device_commons.h b/servers/rendering/rendering_device_commons.h index 918bf9b834..8c3996bd80 100644 --- a/servers/rendering/rendering_device_commons.h +++ b/servers/rendering/rendering_device_commons.h @@ -271,6 +271,44 @@ public: DATA_FORMAT_MAX, }; + // Breadcrumb markers are useful for debugging GPU crashes (i.e. DEVICE_LOST). Internally + // they're just an uint32_t to "tag" a GPU command. These are only used for debugging and do not + // (or at least shouldn't) alter the execution behavior in any way. + // + // When a GPU crashes and Godot was built in dev or debug mode; Godot will dump what commands + // were being executed and what tag they were marked with. + // This makes narrowing down the cause of a crash easier. Note that a GPU can be executing + // multiple commands at the same time. It is also useful to identify data hazards. + // + // For example if each LIGHTMAPPER_PASS must be executed in sequential order, but dumps + // indicated that pass (LIGHTMAPPER_PASS | 5) was being executed at the same time as + // (LIGHTMAPPER_PASS | 4), that would indicate there is a missing barrier or a render graph bug. + // + // The enums are bitshifted by 16 bits so it's possible to add user data via bitwise operations. + // Using this enum is not mandatory; but it is recommended so that all subsystems agree what each + // ID means when dumping info. + enum BreadcrumbMarker { + NONE = 0, + // Environment + REFLECTION_PROBES = 1u << 16u, + SKY_PASS = 2u << 16u, + // Light mapping + LIGHTMAPPER_PASS = 3u << 16u, + // Shadows + SHADOW_PASS_DIRECTIONAL = 4u << 16u, + SHADOW_PASS_CUBE = 5u << 16u, + // Geometry passes + OPAQUE_PASS = 6u << 16u, + ALPHA_PASS = 7u << 16u, + TRANSPARENT_PASS = 8u << 16u, + // Screen effects + POST_PROCESSING_PASS = 9u << 16u, + BLIT_PASS = 10u << 16u, + UI_PASS = 11u << 16u, + // Other + DEBUG_PASS = 12u << 16u, + }; + enum CompareOperator { COMPARE_OP_NEVER, COMPARE_OP_LESS, diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h index 0b5fc51a1d..97c84c9d05 100644 --- a/servers/rendering/rendering_device_driver.h +++ b/servers/rendering/rendering_device_driver.h @@ -476,6 +476,7 @@ public: // Only meaningful if API_TRAIT_SHADER_CHANGE_INVALIDATION is SHADER_CHANGE_INVALIDATION_ALL_OR_NONE_ACCORDING_TO_LAYOUT_HASH. virtual uint32_t shader_get_layout_hash(ShaderID p_shader) { return 0; } virtual void shader_free(ShaderID p_shader) = 0; + virtual void shader_destroy_modules(ShaderID p_shader) = 0; protected: // An optional service to implementations. @@ -709,6 +710,11 @@ public: virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) = 0; virtual void command_end_label(CommandBufferID p_cmd_buffer) = 0; + /****************/ + /**** DEBUG *****/ + /****************/ + virtual void command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) = 0; + /********************/ /**** SUBMISSION ****/ /********************/ @@ -759,6 +765,7 @@ public: DEVICE_OPENGL, DEVICE_VULKAN, DEVICE_DIRECTX, + DEVICE_METAL, }; struct Capabilities { diff --git a/servers/rendering/rendering_device_graph.cpp b/servers/rendering/rendering_device_graph.cpp index 221ec72e4a..abcb76cd43 100644 --- a/servers/rendering/rendering_device_graph.cpp +++ b/servers/rendering/rendering_device_graph.cpp @@ -823,6 +823,9 @@ void RenderingDeviceGraph::_run_render_commands(int32_t p_level, const RecordedC const RecordedDrawListCommand *draw_list_command = reinterpret_cast<const RecordedDrawListCommand *>(command); const VectorView clear_values(draw_list_command->clear_values(), draw_list_command->clear_values_count); +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) + driver->command_insert_breadcrumb(r_command_buffer, draw_list_command->breadcrumb); +#endif driver->command_begin_render_pass(r_command_buffer, draw_list_command->render_pass, draw_list_command->framebuffer, draw_list_command->command_buffer_type, draw_list_command->region, clear_values); _run_draw_list_command(r_command_buffer, draw_list_command->instruction_data(), draw_list_command->instruction_data_size); driver->command_end_render_pass(r_command_buffer); @@ -1399,8 +1402,9 @@ void RenderingDeviceGraph::add_buffer_update(RDD::BufferID p_dst, ResourceTracke _add_command_to_graph(&p_dst_tracker, &buffer_usage, 1, command_index, command); } -void RenderingDeviceGraph::add_compute_list_begin() { +void RenderingDeviceGraph::add_compute_list_begin(RDD::BreadcrumbMarker p_phase, uint32_t p_breadcrumb_data) { compute_instruction_list.clear(); + compute_instruction_list.breadcrumb = p_breadcrumb_data | (p_phase & ((1 << 16) - 1)); compute_instruction_list.index++; } @@ -1490,12 +1494,13 @@ void RenderingDeviceGraph::add_compute_list_end() { _add_command_to_graph(compute_instruction_list.command_trackers.ptr(), compute_instruction_list.command_tracker_usages.ptr(), compute_instruction_list.command_trackers.size(), command_index, command); } -void RenderingDeviceGraph::add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<RDD::RenderPassClearValue> p_clear_values, bool p_uses_color, bool p_uses_depth) { +void RenderingDeviceGraph::add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<RDD::RenderPassClearValue> p_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb) { draw_instruction_list.clear(); draw_instruction_list.index++; draw_instruction_list.render_pass = p_render_pass; draw_instruction_list.framebuffer = p_framebuffer; draw_instruction_list.region = p_region; + draw_instruction_list.breadcrumb = p_breadcrumb; draw_instruction_list.clear_values.resize(p_clear_values.size()); for (uint32_t i = 0; i < p_clear_values.size(); i++) { draw_instruction_list.clear_values[i] = p_clear_values[i]; @@ -1706,6 +1711,7 @@ void RenderingDeviceGraph::add_draw_list_end() { command->framebuffer = draw_instruction_list.framebuffer; command->command_buffer_type = command_buffer_type; command->region = draw_instruction_list.region; + command->breadcrumb = draw_instruction_list.breadcrumb; command->clear_values_count = draw_instruction_list.clear_values.size(); RDD::RenderPassClearValue *clear_values = command->clear_values(); @@ -1964,6 +1970,7 @@ void RenderingDeviceGraph::end(bool p_reorder_commands, bool p_full_barriers, RD 2, // TYPE_TEXTURE_GET_DATA 2, // TYPE_TEXTURE_RESOLVE 2, // TYPE_TEXTURE_UPDATE + 2, // TYPE_INSERT_BREADCRUMB }; commands_sorted.clear(); diff --git a/servers/rendering/rendering_device_graph.h b/servers/rendering/rendering_device_graph.h index baa15f63f6..e13e3a0429 100644 --- a/servers/rendering/rendering_device_graph.h +++ b/servers/rendering/rendering_device_graph.h @@ -218,13 +218,14 @@ private: }; struct ComputeInstructionList : InstructionList { - // No extra contents. + uint32_t breadcrumb; }; struct DrawInstructionList : InstructionList { RDD::RenderPassID render_pass; RDD::FramebufferID framebuffer; Rect2i region; + uint32_t breadcrumb; LocalVector<RDD::RenderPassClearValue> clear_values; }; @@ -296,6 +297,7 @@ private: struct RecordedComputeListCommand : RecordedCommand { uint32_t instruction_data_size = 0; + uint32_t breadcrumb = 0; _FORCE_INLINE_ uint8_t *instruction_data() { return reinterpret_cast<uint8_t *>(&this[1]); @@ -312,6 +314,7 @@ private: RDD::FramebufferID framebuffer; RDD::CommandBufferType command_buffer_type; Rect2i region; + uint32_t breadcrumb = 0; uint32_t clear_values_count = 0; _FORCE_INLINE_ RDD::RenderPassClearValue *clear_values() { @@ -654,7 +657,7 @@ public: void add_buffer_copy(RDD::BufferID p_src, ResourceTracker *p_src_tracker, RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, RDD::BufferCopyRegion p_region); void add_buffer_get_data(RDD::BufferID p_src, ResourceTracker *p_src_tracker, RDD::BufferID p_dst, RDD::BufferCopyRegion p_region); void add_buffer_update(RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, VectorView<RecordedBufferCopy> p_buffer_copies); - void add_compute_list_begin(); + void add_compute_list_begin(RDD::BreadcrumbMarker p_phase = RDD::BreadcrumbMarker::NONE, uint32_t p_breadcrumb_data = 0); void add_compute_list_bind_pipeline(RDD::PipelineID p_pipeline); void add_compute_list_bind_uniform_set(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index); void add_compute_list_dispatch(uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups); @@ -664,7 +667,7 @@ public: void add_compute_list_usage(ResourceTracker *p_tracker, ResourceUsage p_usage); void add_compute_list_usages(VectorView<ResourceTracker *> p_trackers, VectorView<ResourceUsage> p_usages); void add_compute_list_end(); - void add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<RDD::RenderPassClearValue> p_clear_values, bool p_uses_color, bool p_uses_depth); + void add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<RDD::RenderPassClearValue> p_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb = 0); void add_draw_list_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint32_t p_offset); void add_draw_list_bind_pipeline(RDD::PipelineID p_pipeline, BitField<RDD::PipelineStageBits> p_pipeline_stage_bits); void add_draw_list_bind_uniform_set(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index); diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h index aa5e7d83cc..f6212faf08 100644 --- a/servers/rendering/rendering_method.h +++ b/servers/rendering/rendering_method.h @@ -83,6 +83,8 @@ public: virtual void instance_set_layer_mask(RID p_instance, uint32_t p_mask) = 0; virtual void instance_set_pivot_data(RID p_instance, float p_sorting_offset, bool p_use_aabb_center) = 0; virtual void instance_set_transform(RID p_instance, const Transform3D &p_transform) = 0; + virtual void instance_set_interpolated(RID p_instance, bool p_interpolated) = 0; + virtual void instance_reset_physics_interpolation(RID p_instance) = 0; virtual void instance_attach_object_instance_id(RID p_instance, ObjectID p_id) = 0; virtual void instance_set_blend_shape_weight(RID p_instance, int p_shape, float p_weight) = 0; virtual void instance_set_surface_override_material(RID p_instance, int p_surface, RID p_material) = 0; @@ -347,9 +349,20 @@ public: virtual void decals_set_filter(RS::DecalFilter p_filter) = 0; virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) = 0; + virtual void lightmaps_set_bicubic_filter(bool p_enable) = 0; virtual bool free(RID p_rid) = 0; + /* Physics interpolation */ + + virtual void update_interpolation_tick(bool p_process = true) = 0; + virtual void set_physics_interpolation_enabled(bool p_enabled) = 0; + + /* Event queueing */ + + virtual void tick() = 0; + virtual void pre_draw(bool p_will_draw) = 0; + RenderingMethod(); virtual ~RenderingMethod(); }; diff --git a/servers/rendering/rendering_server_constants.h b/servers/rendering/rendering_server_constants.h new file mode 100644 index 0000000000..6d27a3a022 --- /dev/null +++ b/servers/rendering/rendering_server_constants.h @@ -0,0 +1,48 @@ +/**************************************************************************/ +/* rendering_server_constants.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 RENDERING_SERVER_CONSTANTS_H +#define RENDERING_SERVER_CONSTANTS_H + +// Use for constants etc. that need not be included as often as rendering_server.h +// to reduce dependencies and prevent slow compilation. + +// This is a "cheap" include, and can be used from scene side code as well as servers. + +// N.B. ONLY allow these defined in DEV_ENABLED builds, they will slow +// performance, and are only necessary to use for debugging. +#ifdef DEV_ENABLED + +// Uncomment this define to produce debugging output for physics interpolation. +//#define RENDERING_SERVER_DEBUG_PHYSICS_INTERPOLATION + +#endif // DEV_ENABLED + +#endif // RENDERING_SERVER_CONSTANTS_H diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 51ff009eaf..86efccef9a 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -381,12 +381,9 @@ void RenderingServerDefault::_thread_loop() { /* INTERPOLATION */ -void RenderingServerDefault::tick() { - RSG::canvas->tick(); -} - void RenderingServerDefault::set_physics_interpolation_enabled(bool p_enabled) { RSG::canvas->set_physics_interpolation_enabled(p_enabled); + RSG::scene->set_physics_interpolation_enabled(p_enabled); } /* EVENT QUEUING */ @@ -411,6 +408,15 @@ void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) { } } +void RenderingServerDefault::tick() { + RSG::canvas->tick(); + RSG::scene->tick(); +} + +void RenderingServerDefault::pre_draw(bool p_will_draw) { + RSG::scene->pre_draw(p_will_draw); +} + void RenderingServerDefault::_call_on_render_thread(const Callable &p_callable) { p_callable.call(); } diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 164ec3cc09..60fa546e16 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -344,6 +344,11 @@ public: FUNC2(multimesh_set_buffer, RID, const Vector<float> &) FUNC1RC(Vector<float>, multimesh_get_buffer, RID) + FUNC3(multimesh_set_buffer_interpolated, RID, const Vector<float> &, const Vector<float> &) + FUNC2(multimesh_set_physics_interpolated, RID, bool) + FUNC2(multimesh_set_physics_interpolation_quality, RID, MultimeshPhysicsInterpolationQuality) + FUNC2(multimesh_instance_reset_physics_interpolation, RID, int) + FUNC2(multimesh_set_visible_instances, RID, int) FUNC1RC(int, multimesh_get_visible_instances, RID) @@ -639,6 +644,7 @@ public: FUNC3(viewport_set_canvas_transform, RID, RID, const Transform2D &) FUNC2(viewport_set_transparent_background, RID, bool) FUNC2(viewport_set_use_hdr_2d, RID, bool) + FUNC1RC(bool, viewport_is_using_hdr_2d, RID) FUNC2(viewport_set_snap_2d_transforms_to_pixel, RID, bool) FUNC2(viewport_set_snap_2d_vertices_to_pixel, RID, bool) @@ -761,6 +767,7 @@ public: FUNC1(directional_soft_shadow_filter_set_quality, ShadowQuality); FUNC1(decals_set_filter, RS::DecalFilter); FUNC1(light_projectors_set_filter, RS::LightProjectorFilter); + FUNC1(lightmaps_set_bicubic_filter, bool); /* CAMERA ATTRIBUTES */ @@ -802,6 +809,8 @@ public: FUNC2(instance_set_layer_mask, RID, uint32_t) FUNC3(instance_set_pivot_data, RID, float, bool) FUNC2(instance_set_transform, RID, const Transform3D &) + FUNC2(instance_set_interpolated, RID, bool) + FUNC1(instance_reset_physics_interpolation, RID) FUNC2(instance_attach_object_instance_id, RID, ObjectID) FUNC3(instance_set_blend_shape_weight, RID, int, float) FUNC3(instance_set_surface_override_material, RID, int, RID) @@ -1048,7 +1057,6 @@ public: /* INTERPOLATION */ - virtual void tick() override; virtual void set_physics_interpolation_enabled(bool p_enabled) override; /* EVENT QUEUING */ @@ -1060,6 +1068,8 @@ public: virtual bool has_changed() const override; virtual void init() override; virtual void finish() override; + virtual void tick() override; + virtual void pre_draw(bool p_will_draw) override; virtual bool is_on_render_thread() override { return Thread::get_caller_id() == server_thread; diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index a4ee33ecc0..49e005ca96 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -922,7 +922,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene if (shader->uniforms.has(vnode->name)) { //its a uniform! const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[vnode->name]; - if (u.texture_order >= 0) { + if (u.is_texture()) { StringName name; if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE) { name = "color_buffer"; @@ -1039,7 +1039,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene if (shader->uniforms.has(anode->name)) { //its a uniform! const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[anode->name]; - if (u.texture_order >= 0) { + if (u.is_texture()) { code = _mkid(anode->name); //texture, use as is } else { //a scalar or vector @@ -1286,6 +1286,13 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene break; } if (function->arguments[j].tex_argument_check) { + if (function->arguments[j].tex_hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE) { + is_screen_texture = true; + } else if (function->arguments[j].tex_hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { + is_depth_texture = true; + } else if (function->arguments[j].tex_hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE) { + is_normal_roughness_texture = true; + } sampler_name = _get_sampler_name(function->arguments[j].tex_argument_filter, function->arguments[j].tex_argument_repeat); found = true; break; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 9aa54d0bb7..4eaf7fcb55 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -39,6 +39,8 @@ #define HAS_WARNING(flag) (warning_flags & flag) +int ShaderLanguage::instance_counter = 0; + String ShaderLanguage::get_operator_text(Operator p_op) { static const char *op_names[OP_MAX] = { "==", "!=", @@ -94,6 +96,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "FLOAT_CONSTANT", "INT_CONSTANT", "UINT_CONSTANT", + "STRING_CONSTANT", "TYPE_VOID", "TYPE_BOOL", "TYPE_BVEC2", @@ -210,6 +213,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "HINT_ANISOTROPY_TEXTURE", "HINT_SOURCE_COLOR", "HINT_RANGE", + "HINT_ENUM", "HINT_INSTANCE_INDEX", "HINT_SCREEN_TEXTURE", "HINT_NORMAL_ROUGHNESS_TEXTURE", @@ -363,6 +367,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_HINT_SOURCE_COLOR, "source_color", CF_UNSPECIFIED, {}, {} }, { TK_HINT_RANGE, "hint_range", CF_UNSPECIFIED, {}, {} }, + { TK_HINT_ENUM, "hint_enum", CF_UNSPECIFIED, {}, {} }, { TK_HINT_INSTANCE_INDEX, "instance_index", CF_UNSPECIFIED, {}, {} }, // sampler hints @@ -510,7 +515,54 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { return _make_token(TK_OP_NOT); } break; - //case '"' //string - no strings in shader + case '"': { + String _content = ""; + bool _previous_backslash = false; + + while (true) { + bool _ended = false; + char32_t c = GETCHAR(0); + if (c == 0) { + return _make_token(TK_ERROR, "EOF reached before string termination."); + } + switch (c) { + case '"': { + if (_previous_backslash) { + _content += '"'; + _previous_backslash = false; + } else { + _ended = true; + } + break; + } + case '\\': { + if (_previous_backslash) { + _content += '\\'; + } + _previous_backslash = !_previous_backslash; + break; + } + case '\n': { + return _make_token(TK_ERROR, "Unexpected end of string."); + } + default: { + if (!_previous_backslash) { + _content += c; + } else { + return _make_token(TK_ERROR, "Only \\\" and \\\\ escape characters supported."); + } + break; + } + } + + char_idx++; + if (_ended) { + break; + } + } + + return _make_token(TK_STRING_CONSTANT, _content); + } break; //case '\'' //string - no strings in shader case '{': return _make_token(TK_CURLY_BRACKET_OPEN); @@ -896,6 +948,13 @@ bool ShaderLanguage::_lookup_next(Token &r_tk) { return false; } +ShaderLanguage::Token ShaderLanguage::_peek() { + TkPos pre_pos = _get_tkpos(); + Token tk = _get_token(); + _set_tkpos(pre_pos); + return tk; +} + String ShaderLanguage::token_debug(const String &p_code) { clear(); @@ -1118,6 +1177,9 @@ String ShaderLanguage::get_uniform_hint_name(ShaderNode::Uniform::Hint p_hint) { case ShaderNode::Uniform::HINT_RANGE: { result = "hint_range"; } break; + case ShaderNode::Uniform::HINT_ENUM: { + result = "hint_enum"; + } break; case ShaderNode::Uniform::HINT_SOURCE_COLOR: { result = "source_color"; } break; @@ -1238,6 +1300,7 @@ void ShaderLanguage::clear() { include_positions.push_back(FilePosition()); include_markers_handled.clear(); + calls_info.clear(); #ifdef DEBUG_ENABLED keyword_completion_context = CF_UNSPECIFIED; @@ -1445,8 +1508,12 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea *r_struct_name = shader->constants[p_identifier].struct_name; } if (r_constant_value) { - if (shader->constants[p_identifier].initializer && shader->constants[p_identifier].initializer->values.size() == 1) { - *r_constant_value = shader->constants[p_identifier].initializer->values[0]; + if (shader->constants[p_identifier].initializer && shader->constants[p_identifier].initializer->type == Node::NODE_TYPE_CONSTANT) { + ConstantNode *cnode = static_cast<ConstantNode *>(shader->constants[p_identifier].initializer); + + if (cnode->values.size() == 1) { + *r_constant_value = cnode->values[0]; + } } } if (r_type) { @@ -1542,7 +1609,7 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type } DataType na = p_op->arguments[0]->get_datatype(); - valid = na > TYPE_BOOL && na < TYPE_MAT2; + valid = na > TYPE_BVEC4 && na < TYPE_MAT2; ret_type = na; } break; case OP_ADD: @@ -1562,7 +1629,7 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type } if (na == nb) { - valid = (na > TYPE_BOOL && na <= TYPE_MAT4); + valid = (na > TYPE_BVEC4 && na <= TYPE_MAT4); ret_type = na; } else if (na == TYPE_INT && nb == TYPE_IVEC2) { valid = true; @@ -1771,7 +1838,7 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type DataType nb = p_op->arguments[1]->get_datatype(); if (na == nb) { - valid = (na > TYPE_BOOL && na <= TYPE_MAT4); + valid = (na > TYPE_BVEC4 && na <= TYPE_MAT4); ret_type = na; } else if (na == TYPE_IVEC2 && nb == TYPE_INT) { valid = true; @@ -3085,6 +3152,19 @@ const ShaderLanguage::BuiltinFuncConstArgs ShaderLanguage::builtin_func_const_ar { nullptr, 0, 0, 0 } }; +const ShaderLanguage::BuiltinEntry ShaderLanguage::frag_only_func_defs[] = { + { "dFdx" }, + { "dFdxCoarse" }, + { "dFdxFine" }, + { "dFdy" }, + { "dFdyCoarse" }, + { "dFdyFine" }, + { "fwidth" }, + { "fwidthCoarse" }, + { "fwidthFine" }, + { nullptr } +}; + bool ShaderLanguage::is_const_suffix_lut_initialized = false; bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str, bool *r_is_custom_function) { @@ -3964,12 +4044,9 @@ Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::C } value = Variant(array); } else { - PackedFloat32Array array; + PackedVector4Array array; for (int i = 0; i < array_size; i += 4) { - array.push_back(p_value[i].real); - array.push_back(p_value[i + 1].real); - array.push_back(p_value[i + 2].real); - array.push_back(p_value[i + 3].real); + array.push_back(Vector4(p_value[i].real, p_value[i + 1].real, p_value[i + 2].real, p_value[i + 3].real)); } value = Variant(array); } @@ -4122,6 +4199,11 @@ PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform if (p_uniform.array_size > 0) { pi.type = Variant::PACKED_INT32_ARRAY; // TODO: Handle range and encoding for for unsigned values. + } else if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_ENUM) { + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_ENUM; + String hint_string; + pi.hint_string = String(",").join(p_uniform.hint_enum_names); } else { pi.type = Variant::INT; pi.hint = PROPERTY_HINT_RANGE; @@ -4201,7 +4283,7 @@ PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SOURCE_COLOR) { pi.type = Variant::PACKED_COLOR_ARRAY; } else { - pi.type = Variant::PACKED_FLOAT32_ARRAY; + pi.type = Variant::PACKED_VECTOR4_ARRAY; } } else { if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SOURCE_COLOR) { @@ -4610,6 +4692,67 @@ bool ShaderLanguage::_check_node_constness(const Node *p_node) const { return true; } +bool ShaderLanguage::_check_restricted_func(const StringName &p_name, const StringName &p_current_function) const { + int idx = 0; + + while (frag_only_func_defs[idx].name) { + if (StringName(frag_only_func_defs[idx].name) == p_name) { + if (is_supported_frag_only_funcs) { + if (p_current_function == "vertex" && stages->has(p_current_function)) { + return true; + } + } else { + return true; + } + break; + } + idx++; + } + + return false; +} + +bool ShaderLanguage::_validate_restricted_func(const StringName &p_name, const CallInfo *p_func_info, bool p_is_builtin_hint) { + const bool is_in_restricted_function = p_func_info->name == "vertex"; + + // No need to check up the hierarchy if it's a built-in. + if (!p_is_builtin_hint) { + for (const CallInfo *func_info : p_func_info->calls) { + if (is_in_restricted_function && func_info->name != p_name) { + // Skips check for non-called method. + continue; + } + + if (!_validate_restricted_func(p_name, func_info)) { + return false; + } + } + } + + if (!p_func_info->uses_restricted_items.is_empty()) { + const Pair<StringName, CallInfo::Item> &first_element = p_func_info->uses_restricted_items.get(0); + + if (first_element.second.type == CallInfo::Item::ITEM_TYPE_VARYING) { + const ShaderNode::Varying &varying = shader->varyings[first_element.first]; + + if (varying.stage == ShaderNode::Varying::STAGE_VERTEX) { + return true; + } + } + + _set_tkpos(first_element.second.pos); + + if (is_in_restricted_function) { + _set_error(vformat(RTR("'%s' cannot be used within the '%s' processor function."), first_element.first, "vertex")); + } else { + _set_error(vformat(RTR("'%s' cannot be used here, because '%s' is called by the '%s' processor function (which is not allowed)."), first_element.first, p_func_info->name, "vertex")); + } + return false; + } + + return true; +} + bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message) { if (p_node->type == Node::NODE_TYPE_OPERATOR) { OperatorNode *op = static_cast<OperatorNode *>(p_node); @@ -4683,7 +4826,16 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_functi return false; } -bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(const StringName &p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat) { +ShaderLanguage::ShaderNode::Uniform::Hint ShaderLanguage::_sanitize_hint(ShaderNode::Uniform::Hint p_hint) { + if (p_hint == ShaderNode::Uniform::HINT_SCREEN_TEXTURE || + p_hint == ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE || + p_hint == ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { + return p_hint; + } + return ShaderNode::Uniform::HINT_NONE; +} + +bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(const StringName &p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat, ShaderNode::Uniform::Hint p_hint) { for (int i = 0; i < shader->vfunctions.size(); i++) { if (shader->vfunctions[i].name == p_name) { ERR_FAIL_INDEX_V(p_argument, shader->vfunctions[i].function->arguments.size(), false); @@ -4692,20 +4844,21 @@ bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(const Str _set_error(vformat(RTR("Sampler argument %d of function '%s' called more than once using both built-ins and uniform textures, this is not supported (use either one or the other)."), p_argument, String(p_name))); return false; } else if (arg->tex_argument_check) { - //was checked, verify that filter and repeat are the same - if (arg->tex_argument_filter == p_filter && arg->tex_argument_repeat == p_repeat) { + // Was checked, verify that filter, repeat, and hint are the same. + if (arg->tex_argument_filter == p_filter && arg->tex_argument_repeat == p_repeat && arg->tex_hint == _sanitize_hint(p_hint)) { return true; } else { - _set_error(vformat(RTR("Sampler argument %d of function '%s' called more than once using textures that differ in either filter or repeat setting."), p_argument, String(p_name))); + _set_error(vformat(RTR("Sampler argument %d of function '%s' called more than once using textures that differ in either filter, repeat, or texture hint setting."), p_argument, String(p_name))); return false; } } else { arg->tex_argument_check = true; arg->tex_argument_filter = p_filter; arg->tex_argument_repeat = p_repeat; + arg->tex_hint = _sanitize_hint(p_hint); for (KeyValue<StringName, HashSet<int>> &E : arg->tex_argument_connect) { for (const int &F : E.value) { - if (!_propagate_function_call_sampler_uniform_settings(E.key, F, p_filter, p_repeat)) { + if (!_propagate_function_call_sampler_uniform_settings(E.key, F, p_filter, p_repeat, p_hint)) { return false; } } @@ -5043,7 +5196,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc return an; } -ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info) { +ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info, const ExpressionInfo *p_previous_expression_info) { Vector<Expression> expression; //Vector<TokenType> operators; @@ -5266,6 +5419,36 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons const StringName &name = identifier; + if (name != current_function) { // Recursion is not allowed. + // Register call. + if (calls_info.has(name)) { + calls_info[current_function].calls.push_back(&calls_info[name]); + } + + int idx = 0; + bool is_builtin = false; + + while (frag_only_func_defs[idx].name) { + if (frag_only_func_defs[idx].name == name) { + // If a built-in function not found for the current shader type, then it shouldn't be parsed further. + if (!is_supported_frag_only_funcs) { + _set_error(vformat(RTR("Built-in function '%s' is not supported for the '%s' shader type."), name, shader_type_identifier)); + return nullptr; + } + // Register usage of the restricted function. + calls_info[current_function].uses_restricted_items.push_back(Pair<StringName, CallInfo::Item>(name, CallInfo::Item(CallInfo::Item::ITEM_TYPE_BUILTIN, _get_tkpos()))); + is_builtin = true; + break; + } + idx++; + } + + // Recursively checks for the restricted function call. + if (is_supported_frag_only_funcs && current_function == "vertex" && stages->has(current_function) && !_validate_restricted_func(name, &calls_info[current_function], is_builtin)) { + return nullptr; + } + } + OperatorNode *func = alloc_node<OperatorNode>(); func->op = OP_CALL; VariableNode *funcname = alloc_node<VariableNode>(); @@ -5370,10 +5553,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (shader->varyings.has(varname)) { switch (shader->varyings[varname].stage) { - case ShaderNode::Varying::STAGE_UNKNOWN: { - _set_error(vformat(RTR("Varying '%s' must be assigned in the 'vertex' or 'fragment' function first."), varname)); - return nullptr; - } + case ShaderNode::Varying::STAGE_UNKNOWN: + if (is_out_arg) { + error = true; + } + break; case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT: [[fallthrough]]; case ShaderNode::Varying::STAGE_VERTEX: @@ -5449,10 +5633,16 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } } if (is_sampler_type(call_function->arguments[i].type)) { - //let's see where our argument comes from - ERR_CONTINUE(n->type != Node::NODE_TYPE_VARIABLE); //bug? this should always be a variable - VariableNode *vn = static_cast<VariableNode *>(n); - StringName varname = vn->name; + // Let's see where our argument comes from. + StringName varname; + if (n->type == Node::NODE_TYPE_VARIABLE) { + VariableNode *vn = static_cast<VariableNode *>(n); + varname = vn->name; + } else if (n->type == Node::NODE_TYPE_ARRAY) { + ArrayNode *an = static_cast<ArrayNode *>(n); + varname = an->name; + } + if (shader->uniforms.has(varname)) { //being sampler, this either comes from a uniform ShaderNode::Uniform *u = &shader->uniforms[varname]; @@ -5468,7 +5658,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } //propagate - if (!_propagate_function_call_sampler_uniform_settings(name, i, u->filter, u->repeat)) { + if (!_propagate_function_call_sampler_uniform_settings(name, i, u->filter, u->repeat, u->hint)) { return nullptr; } } else if (p_function_info.built_ins.has(varname)) { @@ -5567,6 +5757,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons _set_tkpos(prev_pos); ShaderNode::Varying &var = shader->varyings[identifier]; + calls_info[current_function].uses_restricted_items.push_back(Pair<StringName, CallInfo::Item>(identifier, CallInfo::Item(CallInfo::Item::ITEM_TYPE_VARYING, prev_pos))); + String error; if (is_token_operator_assign(next_token.type)) { if (!_validate_varying_assign(shader->varyings[identifier], &error)) { @@ -6359,6 +6551,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons pos = _get_tkpos(); tk = _get_token(); + if (p_previous_expression_info != nullptr && tk.type == p_previous_expression_info->tt_break && !p_previous_expression_info->is_last_expr) { + break; + } + if (is_token_operator(tk.type)) { Expression o; o.is_op = true; @@ -6465,6 +6661,31 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expression.push_back(o); + if (o.op == OP_SELECT_IF) { + ExpressionInfo info; + info.expression = &expression; + info.tt_break = TK_COLON; + + expr = _parse_and_reduce_expression(p_block, p_function_info, &info); + if (!expr) { + return nullptr; + } + + expression.push_back({ true, { OP_SELECT_ELSE } }); + + if (p_previous_expression_info != nullptr) { + info.is_last_expr = p_previous_expression_info->is_last_expr; + } else { + info.is_last_expr = true; + } + + expr = _parse_and_reduce_expression(p_block, p_function_info, &info); + if (!expr) { + return nullptr; + } + + break; + } } else { _set_tkpos(pos); //something else, so rollback and end break; @@ -6777,6 +6998,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } } + if (p_previous_expression_info != nullptr) { + p_previous_expression_info->expression->push_back(expression[0]); + } + return expression[0].node; } @@ -6889,8 +7114,8 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha return p_node; } -ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_block, const FunctionInfo &p_function_info) { - ShaderLanguage::Node *expr = _parse_expression(p_block, p_function_info); +ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_block, const FunctionInfo &p_function_info, const ExpressionInfo *p_previous_expression_info) { + ShaderLanguage::Node *expr = _parse_expression(p_block, p_function_info, p_previous_expression_info); if (!expr) { //errored return nullptr; } @@ -7294,6 +7519,9 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun return ERR_PARSE_ERROR; } tk = _get_token(); + } else { + _set_expected_error("("); + return ERR_PARSE_ERROR; } } } else { @@ -7931,7 +8159,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun if (!expr) { return ERR_PARSE_ERROR; } - is_condition = expr->type == Node::NODE_TYPE_OPERATOR && expr->get_datatype() == TYPE_BOOL; + is_condition = expr->get_datatype() == TYPE_BOOL; if (expr->type == Node::NODE_TYPE_OPERATOR) { OperatorNode *op = static_cast<OperatorNode *>(expr); @@ -7947,7 +8175,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION) { if (tk.type == TK_COMMA) { if (!is_condition) { - _set_error(RTR("The middle expression is expected to be a boolean operator.")); + _set_error(RTR("The middle expression is expected to have a boolean data type.")); + return ERR_PARSE_ERROR; + } + tk = _peek(); + if (tk.type == TK_SEMICOLON) { + _set_error(vformat(RTR("Expected expression, found: '%s'."), get_token_text(tk))); return ERR_PARSE_ERROR; } continue; @@ -7958,6 +8191,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun } } else if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_EXPRESSION) { if (tk.type == TK_COMMA) { + tk = _peek(); + if (tk.type == TK_PARENTHESIS_CLOSE) { + _set_error(vformat(RTR("Expected expression, found: '%s'."), get_token_text(tk))); + return ERR_PARSE_ERROR; + } continue; } if (tk.type != TK_PARENTHESIS_CLOSE) { @@ -7976,7 +8214,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun return ERR_PARSE_ERROR; } if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION && !is_condition) { - _set_error(RTR("The middle expression is expected to be a boolean operator.")); + _set_error(RTR("The middle expression is expected to have a boolean data type.")); return ERR_PARSE_ERROR; } } @@ -8081,6 +8319,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f int texture_binding = 0; int uniforms = 0; int instance_index = 0; + int prop_index = 0; #ifdef DEBUG_ENABLED uint64_t uniform_buffer_size = 0; uint64_t max_uniform_buffer_size = 0; @@ -8099,6 +8338,8 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f ShaderNode::Uniform::Scope uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL; stages = &p_functions; + is_supported_frag_only_funcs = shader_type_identifier == "canvas_item" || shader_type_identifier == "spatial" || shader_type_identifier == "sky"; + const FunctionInfo &constants = p_functions.has("constants") ? p_functions["constants"] : FunctionInfo(); HashMap<String, String> defined_modes; @@ -8633,6 +8874,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f ++texture_binding; } uniform.order = -1; + uniform.prop_order = prop_index++; } else { if (uniform_scope == ShaderNode::Uniform::SCOPE_INSTANCE && (type == TYPE_MAT2 || type == TYPE_MAT3 || type == TYPE_MAT4)) { _set_error(vformat(RTR("The '%s' qualifier is not supported for matrix types."), "SCOPE_INSTANCE")); @@ -8641,6 +8883,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f uniform.texture_order = -1; if (uniform_scope != ShaderNode::Uniform::SCOPE_INSTANCE) { uniform.order = uniforms++; + uniform.prop_order = prop_index++; #ifdef DEBUG_ENABLED if (check_device_limit_warnings) { if (uniform.array_size > 0) { @@ -8836,6 +9079,40 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f new_hint = ShaderNode::Uniform::HINT_RANGE; } break; + case TK_HINT_ENUM: { + if (type != TYPE_INT) { + _set_error(vformat(RTR("Enum hint is for '%s' only."), "int")); + return ERR_PARSE_ERROR; + } + + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_expected_after_error("(", "hint_enum"); + return ERR_PARSE_ERROR; + } + + while (true) { + tk = _get_token(); + + if (tk.type != TK_STRING_CONSTANT) { + _set_error(RTR("Expected a string constant.")); + return ERR_PARSE_ERROR; + } + + uniform.hint_enum_names.push_back(tk.text); + + tk = _get_token(); + + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } else if (tk.type != TK_COMMA) { + _set_error(RTR("Expected ',' or ')' after string constant.")); + return ERR_PARSE_ERROR; + } + } + + new_hint = ShaderNode::Uniform::HINT_ENUM; + } break; case TK_HINT_INSTANCE_INDEX: { if (custom_instance_index != -1) { _set_error(vformat(RTR("Can only specify '%s' once."), "instance_index")); @@ -8930,7 +9207,9 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f default: break; } - if (((new_filter != FILTER_DEFAULT || new_repeat != REPEAT_DEFAULT) || (new_hint != ShaderNode::Uniform::HINT_NONE && new_hint != ShaderNode::Uniform::HINT_SOURCE_COLOR && new_hint != ShaderNode::Uniform::HINT_RANGE)) && !is_sampler_type(type)) { + + bool is_sampler_hint = new_hint != ShaderNode::Uniform::HINT_NONE && new_hint != ShaderNode::Uniform::HINT_SOURCE_COLOR && new_hint != ShaderNode::Uniform::HINT_RANGE && new_hint != ShaderNode::Uniform::HINT_ENUM; + if (((new_filter != FILTER_DEFAULT || new_repeat != REPEAT_DEFAULT) || is_sampler_hint) && !is_sampler_type(type)) { _set_error(RTR("This hint is only for sampler types.")); return ERR_PARSE_ERROR; } @@ -9085,6 +9364,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f tk = _get_token(); if (tk.type == TK_IDENTIFIER) { current_uniform_group_name = tk.text; + current_uniform_subgroup_name = ""; tk = _get_token(); if (tk.type == TK_PERIOD) { tk = _get_token(); @@ -9401,6 +9681,9 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f _set_error(RTR("Array size mismatch.")); return ERR_PARSE_ERROR; } + } else { + _set_expected_error("("); + return ERR_PARSE_ERROR; } array_size = constant.array_size; @@ -9443,7 +9726,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f } } - constant.initializer = static_cast<ConstantNode *>(expr); + constant.initializer = expr; if (!_compare_datatypes(type, struct_name, 0, expr->get_datatype(), expr->get_datatype_name(), expr->get_array_size())) { return ERR_PARSE_ERROR; @@ -9505,6 +9788,11 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f break; } + if (is_constant) { + _set_error(vformat(RTR("'%s' qualifier cannot be used with a function return type."), "const")); + return ERR_PARSE_ERROR; + } + FunctionInfo builtins; if (p_functions.has(name)) { builtins = p_functions[name]; @@ -9541,6 +9829,11 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f shader->functions.insert(name, function); shader->vfunctions.push_back(function); + CallInfo call_info; + call_info.name = name; + + calls_info.insert(name, call_info); + func_node->name = name; func_node->return_type = type; func_node->return_struct_name = struct_name; @@ -10325,10 +10618,11 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ } while (builtin_func_defs[idx].name) { - if (low_end && builtin_func_defs[idx].high_end) { + if ((low_end && builtin_func_defs[idx].high_end) || _check_restricted_func(builtin_func_defs[idx].name, skip_function)) { idx++; continue; } + matches.insert(String(builtin_func_defs[idx].name), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); idx++; } @@ -10490,7 +10784,7 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ } while (builtin_func_defs[idx].name) { - if (low_end && builtin_func_defs[idx].high_end) { + if ((low_end && builtin_func_defs[idx].high_end) || _check_restricted_func(builtin_func_defs[idx].name, block_function)) { idx++; continue; } @@ -10623,15 +10917,21 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_ } } else if ((completion_base == DataType::TYPE_INT || completion_base == DataType::TYPE_FLOAT) && !completion_base_array) { if (current_uniform_hint == ShaderNode::Uniform::HINT_NONE) { - ScriptLanguage::CodeCompletionOption option("hint_range", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + Vector<String> options; if (completion_base == DataType::TYPE_INT) { - option.insert_text = "hint_range(0, 100, 1)"; + options.push_back("hint_range(0, 100, 1)"); + options.push_back("hint_enum(\"Zero\", \"One\", \"Two\")"); } else { - option.insert_text = "hint_range(0.0, 1.0, 0.1)"; + options.push_back("hint_range(0.0, 1.0, 0.1)"); } - r_options->push_back(option); + for (const String &option_text : options) { + String hint_name = option_text.substr(0, option_text.find_char(char32_t('('))); + ScriptLanguage::CodeCompletionOption option(hint_name, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + option.insert_text = option_text; + r_options->push_back(option); + } } } else if ((int(completion_base) > int(TYPE_MAT4) && int(completion_base) < int(TYPE_STRUCT))) { Vector<String> options; @@ -10707,17 +11007,16 @@ ShaderLanguage::ShaderLanguage() { nodes = nullptr; completion_class = TAG_GLOBAL; - int idx = 0; - while (builtin_func_defs[idx].name) { - if (builtin_func_defs[idx].tag == SubClassTag::TAG_GLOBAL) { - const StringName &name = StringName(builtin_func_defs[idx].name); - - if (!global_func_set.has(name)) { - global_func_set.insert(name); + if (instance_counter == 0) { + int idx = 0; + while (builtin_func_defs[idx].name) { + if (builtin_func_defs[idx].tag == SubClassTag::TAG_GLOBAL) { + global_func_set.insert(builtin_func_defs[idx].name); } + idx++; } - idx++; } + instance_counter++; #ifdef DEBUG_ENABLED warnings_check_map.insert(ShaderWarning::UNUSED_CONSTANT, &used_constants); @@ -10732,4 +11031,8 @@ ShaderLanguage::ShaderLanguage() { ShaderLanguage::~ShaderLanguage() { clear(); + instance_counter--; + if (instance_counter == 0) { + global_func_set.clear(); + } } diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 5615d7f457..63dca99654 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -59,6 +59,7 @@ public: TK_FLOAT_CONSTANT, TK_INT_CONSTANT, TK_UINT_CONSTANT, + TK_STRING_CONSTANT, TK_TYPE_VOID, TK_TYPE_BOOL, TK_TYPE_BVEC2, @@ -175,6 +176,7 @@ public: TK_HINT_ANISOTROPY_TEXTURE, TK_HINT_SOURCE_COLOR, TK_HINT_RANGE, + TK_HINT_ENUM, TK_HINT_INSTANCE_INDEX, TK_HINT_SCREEN_TEXTURE, TK_HINT_NORMAL_ROUGHNESS_TEXTURE, @@ -578,49 +580,13 @@ public: Node(NODE_TYPE_STRUCT) {} }; - struct FunctionNode : public Node { - struct Argument { - ArgumentQualifier qualifier; - StringName name; - DataType type; - StringName struct_name; - DataPrecision precision; - //for passing textures as arguments - bool tex_argument_check; - TextureFilter tex_argument_filter; - TextureRepeat tex_argument_repeat; - bool tex_builtin_check; - StringName tex_builtin; - bool is_const; - int array_size; - - HashMap<StringName, HashSet<int>> tex_argument_connect; - }; - - StringName name; - DataType return_type = TYPE_VOID; - StringName return_struct_name; - DataPrecision return_precision = PRECISION_DEFAULT; - int return_array_size = 0; - Vector<Argument> arguments; - BlockNode *body = nullptr; - bool can_discard = false; - - virtual DataType get_datatype() const override { return return_type; } - virtual String get_datatype_name() const override { return String(return_struct_name); } - virtual int get_array_size() const override { return return_array_size; } - - FunctionNode() : - Node(NODE_TYPE_FUNCTION) {} - }; - struct ShaderNode : public Node { struct Constant { StringName name; DataType type; StringName struct_name; DataPrecision precision; - ConstantNode *initializer = nullptr; + Node *initializer = nullptr; int array_size; }; @@ -659,6 +625,7 @@ public: enum Hint { HINT_NONE, HINT_RANGE, + HINT_ENUM, HINT_SOURCE_COLOR, HINT_NORMAL, HINT_ROUGHNESS_NORMAL, @@ -684,6 +651,7 @@ public: }; int order = 0; + int prop_order = 0; int texture_order = 0; int texture_binding = 0; DataType type = TYPE_VOID; @@ -696,10 +664,16 @@ public: TextureFilter filter = FILTER_DEFAULT; TextureRepeat repeat = REPEAT_DEFAULT; float hint_range[3]; + PackedStringArray hint_enum_names; int instance_index = 0; String group; String subgroup; + _FORCE_INLINE_ bool is_texture() const { + // Order is assigned to -1 for texture uniforms. + return order < 0; + } + Uniform() { hint_range[0] = 0.0f; hint_range[1] = 1.0f; @@ -722,6 +696,43 @@ public: Node(NODE_TYPE_SHADER) {} }; + struct FunctionNode : public Node { + struct Argument { + ArgumentQualifier qualifier; + StringName name; + DataType type; + StringName struct_name; + DataPrecision precision; + //for passing textures as arguments + bool tex_argument_check; + TextureFilter tex_argument_filter; + TextureRepeat tex_argument_repeat; + bool tex_builtin_check; + StringName tex_builtin; + ShaderNode::Uniform::Hint tex_hint; + bool is_const; + int array_size; + + HashMap<StringName, HashSet<int>> tex_argument_connect; + }; + + StringName name; + DataType return_type = TYPE_VOID; + StringName return_struct_name; + DataPrecision return_precision = PRECISION_DEFAULT; + int return_array_size = 0; + Vector<Argument> arguments; + BlockNode *body = nullptr; + bool can_discard = false; + + virtual DataType get_datatype() const override { return return_type; } + virtual String get_datatype_name() const override { return String(return_struct_name); } + virtual int get_array_size() const override { return return_array_size; } + + FunctionNode() : + Node(NODE_TYPE_FUNCTION) {} + }; + struct UniformOrderComparator { _FORCE_INLINE_ bool operator()(const Pair<StringName, int> &A, const Pair<StringName, int> &B) const { return A.second < B.second; @@ -736,6 +747,12 @@ public: }; }; + struct ExpressionInfo { + Vector<Expression> *expression = nullptr; + TokenType tt_break = TK_EMPTY; + bool is_last_expr = false; + }; + struct VarInfo { StringName name; DataType type; @@ -800,6 +817,8 @@ public: static bool is_control_flow_keyword(String p_keyword); static void get_builtin_funcs(List<String> *r_keywords); + static int instance_counter; + struct BuiltInInfo { DataType type = TYPE_VOID; bool constant = false; @@ -913,6 +932,28 @@ private: Vector<FilePosition> include_positions; HashSet<String> include_markers_handled; + // Additional function information (eg. call hierarchy). No need to expose it to compiler. + struct CallInfo { + struct Item { + enum ItemType { + ITEM_TYPE_BUILTIN, + ITEM_TYPE_VARYING, + } type; + + TkPos pos; + + Item() {} + Item(ItemType p_type, TkPos p_pos) : + type(p_type), pos(p_pos) {} + }; + + StringName name; + List<Pair<StringName, Item>> uses_restricted_items; + List<CallInfo *> calls; + }; + + RBMap<StringName, CallInfo> calls_info; + #ifdef DEBUG_ENABLED struct Usage { int decl_line; @@ -1012,6 +1053,7 @@ private: Token _make_token(TokenType p_type, const StringName &p_text = StringName()); Token _get_token(); bool _lookup_next(Token &r_tk); + Token _peek(); ShaderNode *shader = nullptr; @@ -1036,6 +1078,10 @@ private: 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, int *r_ret_size = nullptr); + struct BuiltinEntry { + const char *name; + }; + struct BuiltinFuncDef { enum { MAX_ARGS = 5 }; const char *name; @@ -1078,11 +1124,13 @@ private: #endif // DEBUG_ENABLED const HashMap<StringName, FunctionInfo> *stages = nullptr; + bool is_supported_frag_only_funcs = false; bool _get_completable_identifier(BlockNode *p_block, CompletionType p_type, StringName &identifier); static const BuiltinFuncDef builtin_func_defs[]; static const BuiltinFuncOutArgs builtin_func_out_args[]; static const BuiltinFuncConstArgs builtin_func_const_args[]; + static const BuiltinEntry frag_only_func_defs[]; static bool is_const_suffix_lut_initialized; @@ -1092,18 +1140,22 @@ private: bool _validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str, bool *r_is_custom_function = nullptr); bool _parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg = nullptr); - bool _propagate_function_call_sampler_uniform_settings(const StringName &p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat); + ShaderNode::Uniform::Hint _sanitize_hint(ShaderNode::Uniform::Hint p_hint); + bool _propagate_function_call_sampler_uniform_settings(const StringName &p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat, ShaderNode::Uniform::Hint p_hint); bool _propagate_function_call_sampler_builtin_reference(const StringName &p_name, int p_argument, const StringName &p_builtin); bool _validate_varying_assign(ShaderNode::Varying &p_varying, String *r_message); bool _check_node_constness(const Node *p_node) const; - Node *_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info); + bool _check_restricted_func(const StringName &p_name, const StringName &p_current_function) const; + bool _validate_restricted_func(const StringName &p_call_name, const CallInfo *p_func_info, bool p_is_builtin_hint = false); + + Node *_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info, const ExpressionInfo *p_previous_expression_info = nullptr); Error _parse_array_size(BlockNode *p_block, const FunctionInfo &p_function_info, bool p_forbid_unknown_size, Node **r_size_expression, int *r_array_size, bool *r_unknown_size); Node *_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info); Node *_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info, DataType p_type, const StringName &p_struct_name, int p_array_size); ShaderLanguage::Node *_reduce_expression(BlockNode *p_block, ShaderLanguage::Node *p_node); - Node *_parse_and_reduce_expression(BlockNode *p_block, const FunctionInfo &p_function_info); + Node *_parse_and_reduce_expression(BlockNode *p_block, const FunctionInfo &p_function_info, const ExpressionInfo *p_previous_expression_info = nullptr); Error _parse_block(BlockNode *p_block, const FunctionInfo &p_function_info, bool p_just_one = false, bool p_can_break = false, bool p_can_continue = false); String _get_shader_type_list(const HashSet<String> &p_shader_types) const; String _get_qualifier_str(ArgumentQualifier p_qualifier) const; diff --git a/servers/rendering/shader_preprocessor.cpp b/servers/rendering/shader_preprocessor.cpp index dbd1374941..27e39551ba 100644 --- a/servers/rendering/shader_preprocessor.cpp +++ b/servers/rendering/shader_preprocessor.cpp @@ -173,7 +173,7 @@ String ShaderPreprocessor::Tokenizer::get_identifier(bool *r_is_cursor, bool p_s } String id = vector_to_string(text); - if (!id.is_valid_identifier()) { + if (!id.is_valid_ascii_identifier()) { return ""; } diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index ab9cf666ec..f498c0bf93 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -77,12 +77,12 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["INSTANCE_CUSTOM"] = constt(ShaderLanguage::TYPE_VEC4); shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VERTEX_ID"] = constt(ShaderLanguage::TYPE_INT); shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["ROUGHNESS"] = ShaderLanguage::TYPE_FLOAT; - shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["BONE_INDICES"] = ShaderLanguage::TYPE_UVEC4; - shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["BONE_WEIGHTS"] = ShaderLanguage::TYPE_VEC4; - shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CUSTOM0"] = ShaderLanguage::TYPE_VEC4; - shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CUSTOM1"] = ShaderLanguage::TYPE_VEC4; - shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CUSTOM2"] = ShaderLanguage::TYPE_VEC4; - shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CUSTOM3"] = ShaderLanguage::TYPE_VEC4; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["BONE_INDICES"] = constt(ShaderLanguage::TYPE_UVEC4); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["BONE_WEIGHTS"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CUSTOM0"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CUSTOM1"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CUSTOM2"] = constt(ShaderLanguage::TYPE_VEC4); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CUSTOM3"] = constt(ShaderLanguage::TYPE_VEC4); shader_modes[RS::SHADER_SPATIAL].functions["vertex"].can_discard = false; shader_modes[RS::SHADER_SPATIAL].functions["vertex"].main_function = true; @@ -97,13 +97,14 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["MODELVIEW_NORMAL_MATRIX"] = ShaderLanguage::TYPE_MAT3; shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEWPORT_SIZE"] = constt(ShaderLanguage::TYPE_VEC2); shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CLIP_SPACE_FAR"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["MAIN_CAM_INV_VIEW_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); - shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["NODE_POSITION_WORLD"] = ShaderLanguage::TYPE_VEC3; - shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CAMERA_POSITION_WORLD"] = ShaderLanguage::TYPE_VEC3; - shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CAMERA_DIRECTION_WORLD"] = ShaderLanguage::TYPE_VEC3; - shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CAMERA_VISIBLE_LAYERS"] = ShaderLanguage::TYPE_UINT; - shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["NODE_POSITION_VIEW"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["NODE_POSITION_WORLD"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CAMERA_POSITION_WORLD"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CAMERA_DIRECTION_WORLD"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CAMERA_VISIBLE_LAYERS"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["NODE_POSITION_VIEW"] = constt(ShaderLanguage::TYPE_VEC3); shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEW_INDEX"] = constt(ShaderLanguage::TYPE_INT); shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEW_MONO_LEFT"] = constt(ShaderLanguage::TYPE_INT); @@ -147,11 +148,11 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_UV"] = constt(ShaderLanguage::TYPE_VEC2); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["POINT_COORD"] = constt(ShaderLanguage::TYPE_VEC2); - shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NODE_POSITION_WORLD"] = ShaderLanguage::TYPE_VEC3; - shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["CAMERA_POSITION_WORLD"] = ShaderLanguage::TYPE_VEC3; - shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["CAMERA_DIRECTION_WORLD"] = ShaderLanguage::TYPE_VEC3; - shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["CAMERA_VISIBLE_LAYERS"] = ShaderLanguage::TYPE_UINT; - shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NODE_POSITION_VIEW"] = ShaderLanguage::TYPE_VEC3; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NODE_POSITION_WORLD"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["CAMERA_POSITION_WORLD"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["CAMERA_DIRECTION_WORLD"] = constt(ShaderLanguage::TYPE_VEC3); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["CAMERA_VISIBLE_LAYERS"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NODE_POSITION_VIEW"] = constt(ShaderLanguage::TYPE_VEC3); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEW_INDEX"] = constt(ShaderLanguage::TYPE_INT); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEW_MONO_LEFT"] = constt(ShaderLanguage::TYPE_INT); @@ -159,6 +160,7 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["EYE_OFFSET"] = constt(ShaderLanguage::TYPE_VEC3); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["CLIP_SPACE_FAR"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["MODEL_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["MODEL_NORMAL_MATRIX"] = constt(ShaderLanguage::TYPE_MAT3); @@ -202,6 +204,7 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["DIFFUSE_LIGHT"] = ShaderLanguage::TYPE_VEC3; shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["SPECULAR_LIGHT"] = ShaderLanguage::TYPE_VEC3; shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); + shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["CLIP_SPACE_FAR"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["ALPHA"] = ShaderLanguage::TYPE_FLOAT; shader_modes[RS::SHADER_SPATIAL].functions["light"].can_discard = true; diff --git a/servers/rendering/storage/mesh_storage.cpp b/servers/rendering/storage/mesh_storage.cpp new file mode 100644 index 0000000000..221ebaa277 --- /dev/null +++ b/servers/rendering/storage/mesh_storage.cpp @@ -0,0 +1,485 @@ +/**************************************************************************/ +/* mesh_storage.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "mesh_storage.h" + +#include "core/math/transform_interpolator.h" + +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) +#include "core/config/project_settings.h" +#endif + +RID RendererMeshStorage::multimesh_allocate() { + return _multimesh_allocate(); +} + +void RendererMeshStorage::multimesh_initialize(RID p_rid) { + _multimesh_initialize(p_rid); +} + +void RendererMeshStorage::multimesh_free(RID p_rid) { + _multimesh_free(p_rid); +} + +void RendererMeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi) { + mmi->_transform_format = p_transform_format; + mmi->_use_colors = p_use_colors; + mmi->_use_custom_data = p_use_custom_data; + mmi->_num_instances = p_instances; + + mmi->_vf_size_xform = p_transform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12; + mmi->_vf_size_color = p_use_colors ? 4 : 0; + mmi->_vf_size_data = p_use_custom_data ? 4 : 0; + + mmi->_stride = mmi->_vf_size_xform + mmi->_vf_size_color + mmi->_vf_size_data; + + int size_in_floats = p_instances * mmi->_stride; + mmi->_data_curr.resize_zeroed(size_in_floats); + mmi->_data_prev.resize_zeroed(size_in_floats); + mmi->_data_interpolated.resize_zeroed(size_in_floats); + } + + _multimesh_allocate_data(p_multimesh, p_instances, p_transform_format, p_use_colors, p_use_custom_data); +} + +int RendererMeshStorage::multimesh_get_instance_count(RID p_multimesh) const { + return _multimesh_get_instance_count(p_multimesh); +} + +void RendererMeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { + _multimesh_set_mesh(p_multimesh, p_mesh); +} + +void RendererMeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi && mmi->interpolated) { + ERR_FAIL_COND(p_index >= mmi->_num_instances); + ERR_FAIL_COND(mmi->_vf_size_xform != 12); + + int start = p_index * mmi->_stride; + float *ptr = mmi->_data_curr.ptrw(); + ptr += start; + + const Transform3D &t = p_transform; + ptr[0] = t.basis.rows[0][0]; + ptr[1] = t.basis.rows[0][1]; + ptr[2] = t.basis.rows[0][2]; + ptr[3] = t.origin.x; + ptr[4] = t.basis.rows[1][0]; + ptr[5] = t.basis.rows[1][1]; + ptr[6] = t.basis.rows[1][2]; + ptr[7] = t.origin.y; + ptr[8] = t.basis.rows[2][0]; + ptr[9] = t.basis.rows[2][1]; + ptr[10] = t.basis.rows[2][2]; + ptr[11] = t.origin.z; + + _multimesh_add_to_interpolation_lists(p_multimesh, *mmi); + +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (!Engine::get_singleton()->is_in_physics_frame()) { + PHYSICS_INTERPOLATION_WARNING("MultiMesh interpolation is being triggered from outside physics process, this might lead to issues"); + } +#endif + + return; + } + + _multimesh_instance_set_transform(p_multimesh, p_index, p_transform); +} + +void RendererMeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { + _multimesh_instance_set_transform_2d(p_multimesh, p_index, p_transform); +} + +void RendererMeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi && mmi->interpolated) { + ERR_FAIL_COND(p_index >= mmi->_num_instances); + ERR_FAIL_COND(mmi->_vf_size_color == 0); + + int start = (p_index * mmi->_stride) + mmi->_vf_size_xform; + float *ptr = mmi->_data_curr.ptrw(); + ptr += start; + + if (mmi->_vf_size_color == 4) { + for (int n = 0; n < 4; n++) { + ptr[n] = p_color.components[n]; + } + } else { +#ifdef DEV_ENABLED + // The options are currently 4 or zero, but just in case this changes in future... + ERR_FAIL_COND(mmi->_vf_size_color != 0); +#endif + } + _multimesh_add_to_interpolation_lists(p_multimesh, *mmi); + return; + } + + _multimesh_instance_set_color(p_multimesh, p_index, p_color); +} + +void RendererMeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi && mmi->interpolated) { + ERR_FAIL_COND(p_index >= mmi->_num_instances); + ERR_FAIL_COND(mmi->_vf_size_data == 0); + + int start = (p_index * mmi->_stride) + mmi->_vf_size_xform + mmi->_vf_size_color; + float *ptr = mmi->_data_curr.ptrw(); + ptr += start; + + if (mmi->_vf_size_data == 4) { + for (int n = 0; n < 4; n++) { + ptr[n] = p_color.components[n]; + } + } else { +#ifdef DEV_ENABLED + // The options are currently 4 or zero, but just in case this changes in future... + ERR_FAIL_COND(mmi->_vf_size_data != 0); +#endif + } + _multimesh_add_to_interpolation_lists(p_multimesh, *mmi); + return; + } + + _multimesh_instance_set_custom_data(p_multimesh, p_index, p_color); +} + +void RendererMeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) { + _multimesh_set_custom_aabb(p_multimesh, p_aabb); +} + +AABB RendererMeshStorage::multimesh_get_custom_aabb(RID p_multimesh) const { + return _multimesh_get_custom_aabb(p_multimesh); +} + +RID RendererMeshStorage::multimesh_get_mesh(RID p_multimesh) const { + return _multimesh_get_mesh(p_multimesh); +} + +Transform3D RendererMeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { + return _multimesh_instance_get_transform(p_multimesh, p_index); +} + +Transform2D RendererMeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { + return _multimesh_instance_get_transform_2d(p_multimesh, p_index); +} + +Color RendererMeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) const { + return _multimesh_instance_get_color(p_multimesh, p_index); +} + +Color RendererMeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { + return _multimesh_instance_get_custom_data(p_multimesh, p_index); +} + +void RendererMeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi && mmi->interpolated) { + ERR_FAIL_COND_MSG(p_buffer.size() != mmi->_data_curr.size(), vformat("Buffer should have %d elements, got %d instead.", mmi->_data_curr.size(), p_buffer.size())); + + mmi->_data_curr = p_buffer; + _multimesh_add_to_interpolation_lists(p_multimesh, *mmi); + +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (!Engine::get_singleton()->is_in_physics_frame()) { + PHYSICS_INTERPOLATION_WARNING("MultiMesh interpolation is being triggered from outside physics process, this might lead to issues"); + } +#endif + + return; + } + + _multimesh_set_buffer(p_multimesh, p_buffer); +} + +Vector<float> RendererMeshStorage::multimesh_get_buffer(RID p_multimesh) const { + return _multimesh_get_buffer(p_multimesh); +} + +void RendererMeshStorage::multimesh_set_buffer_interpolated(RID p_multimesh, const Vector<float> &p_buffer, const Vector<float> &p_buffer_prev) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi) { + ERR_FAIL_COND_MSG(p_buffer.size() != mmi->_data_curr.size(), vformat("Buffer for current frame should have %d elements, got %d instead.", mmi->_data_curr.size(), p_buffer.size())); + ERR_FAIL_COND_MSG(p_buffer_prev.size() != mmi->_data_prev.size(), vformat("Buffer for previous frame should have %d elements, got %d instead.", mmi->_data_prev.size(), p_buffer_prev.size())); + + // We are assuming that mmi->interpolated is the case. (Can possibly assert this?) + // Even if this flag hasn't been set - just calling this function suggests interpolation is desired. + mmi->_data_prev = p_buffer_prev; + mmi->_data_curr = p_buffer; + _multimesh_add_to_interpolation_lists(p_multimesh, *mmi); + +#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) + if (!Engine::get_singleton()->is_in_physics_frame()) { + PHYSICS_INTERPOLATION_WARNING("MultiMesh interpolation is being triggered from outside physics process, this might lead to issues"); + } +#endif + } +} + +void RendererMeshStorage::multimesh_set_physics_interpolated(RID p_multimesh, bool p_interpolated) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi) { + mmi->interpolated = p_interpolated; + } +} + +void RendererMeshStorage::multimesh_set_physics_interpolation_quality(RID p_multimesh, RS::MultimeshPhysicsInterpolationQuality p_quality) { + ERR_FAIL_COND((p_quality < 0) || (p_quality > 1)); + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi) { + mmi->quality = (int)p_quality; + } +} + +void RendererMeshStorage::multimesh_instance_reset_physics_interpolation(RID p_multimesh, int p_index) { + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(p_multimesh); + if (mmi) { + ERR_FAIL_INDEX(p_index, mmi->_num_instances); + + float *w = mmi->_data_prev.ptrw(); + const float *r = mmi->_data_curr.ptr(); + int start = p_index * mmi->_stride; + + for (int n = 0; n < mmi->_stride; n++) { + w[start + n] = r[start + n]; + } + } +} + +void RendererMeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { + return _multimesh_set_visible_instances(p_multimesh, p_visible); +} + +int RendererMeshStorage::multimesh_get_visible_instances(RID p_multimesh) const { + return _multimesh_get_visible_instances(p_multimesh); +} + +AABB RendererMeshStorage::multimesh_get_aabb(RID p_multimesh) const { + return _multimesh_get_aabb(p_multimesh); +} + +void RendererMeshStorage::_multimesh_add_to_interpolation_lists(RID p_multimesh, MultiMeshInterpolator &r_mmi) { + if (!r_mmi.on_interpolate_update_list) { + r_mmi.on_interpolate_update_list = true; + _interpolation_data.multimesh_interpolate_update_list.push_back(p_multimesh); + } + + if (!r_mmi.on_transform_update_list) { + r_mmi.on_transform_update_list = true; + _interpolation_data.multimesh_transform_update_list_curr->push_back(p_multimesh); + } +} + +void RendererMeshStorage::InterpolationData::notify_free_multimesh(RID p_rid) { + // If the instance was on any of the lists, remove. + multimesh_interpolate_update_list.erase_multiple_unordered(p_rid); + multimesh_transform_update_lists[0].erase_multiple_unordered(p_rid); + multimesh_transform_update_lists[1].erase_multiple_unordered(p_rid); +} + +void RendererMeshStorage::update_interpolation_tick(bool p_process) { + // Detect any that were on the previous transform list that are no longer active, + // we should remove them from the interpolate list. + + for (unsigned int n = 0; n < _interpolation_data.multimesh_transform_update_list_prev->size(); n++) { + const RID &rid = (*_interpolation_data.multimesh_transform_update_list_prev)[n]; + + bool active = true; + + // No longer active? (Either the instance deleted or no longer being transformed.) + + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(rid); + if (mmi && !mmi->on_transform_update_list) { + active = false; + mmi->on_interpolate_update_list = false; + + // Make sure the most recent transform is set... + mmi->_data_interpolated = mmi->_data_curr; // TODO: Copy data rather than use Packed = function? + + // ... and that both prev and current are the same, just in case of any interpolations. + mmi->_data_prev = mmi->_data_curr; + } + + if (!mmi) { + active = false; + } + + if (!active) { + _interpolation_data.multimesh_interpolate_update_list.erase(rid); + } + } + + if (p_process) { + for (unsigned int i = 0; i < _interpolation_data.multimesh_transform_update_list_curr->size(); i++) { + const RID &rid = (*_interpolation_data.multimesh_transform_update_list_curr)[i]; + + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(rid); + if (mmi) { + // Reset for next tick. + mmi->on_transform_update_list = false; + mmi->_data_prev = mmi->_data_curr; + } + } + } + + // If any have left the transform list, remove from the interpolate list. + + // We maintain a mirror list for the transform updates, so we can detect when an instance + // is no longer being transformed, and remove it from the interpolate list. + SWAP(_interpolation_data.multimesh_transform_update_list_curr, _interpolation_data.multimesh_transform_update_list_prev); + + // Prepare for the next iteration. + _interpolation_data.multimesh_transform_update_list_curr->clear(); +} + +void RendererMeshStorage::update_interpolation_frame(bool p_process) { + if (p_process) { + // Only need 32 bits for interpolation, don't use real_t. + float f = Engine::get_singleton()->get_physics_interpolation_fraction(); + + for (unsigned int c = 0; c < _interpolation_data.multimesh_interpolate_update_list.size(); c++) { + const RID &rid = _interpolation_data.multimesh_interpolate_update_list[c]; + + // We could use the TransformInterpolator here to slerp transforms, but that might be too expensive, + // so just using a Basis lerp for now. + MultiMeshInterpolator *mmi = _multimesh_get_interpolator(rid); + if (mmi) { + // Make sure arrays are the correct size. + DEV_ASSERT(mmi->_data_prev.size() == mmi->_data_curr.size()); + + if (mmi->_data_interpolated.size() < mmi->_data_curr.size()) { + mmi->_data_interpolated.resize(mmi->_data_curr.size()); + } + DEV_ASSERT(mmi->_data_interpolated.size() >= mmi->_data_curr.size()); + + DEV_ASSERT((mmi->_data_curr.size() % mmi->_stride) == 0); + int num = mmi->_data_curr.size() / mmi->_stride; + + const float *pf_prev = mmi->_data_prev.ptr(); + const float *pf_curr = mmi->_data_curr.ptr(); + float *pf_int = mmi->_data_interpolated.ptrw(); + + bool use_lerp = mmi->quality == 0; + + // Temporary transform (needed for swizzling). + Transform3D tp, tc, tr; // (transform prev, curr and result) + + // Test for cache friendliness versus doing branchless. + for (int n = 0; n < num; n++) { + // Transform. + if (use_lerp) { + for (int i = 0; i < mmi->_vf_size_xform; i++) { + pf_int[i] = Math::lerp(pf_prev[i], pf_curr[i], f); + } + } else { + // Silly swizzling, this will slow things down. + // No idea why it is using this format... + // ... maybe due to the shader. + tp.basis.rows[0][0] = pf_prev[0]; + tp.basis.rows[0][1] = pf_prev[1]; + tp.basis.rows[0][2] = pf_prev[2]; + tp.basis.rows[1][0] = pf_prev[4]; + tp.basis.rows[1][1] = pf_prev[5]; + tp.basis.rows[1][2] = pf_prev[6]; + tp.basis.rows[2][0] = pf_prev[8]; + tp.basis.rows[2][1] = pf_prev[9]; + tp.basis.rows[2][2] = pf_prev[10]; + tp.origin.x = pf_prev[3]; + tp.origin.y = pf_prev[7]; + tp.origin.z = pf_prev[11]; + + tc.basis.rows[0][0] = pf_curr[0]; + tc.basis.rows[0][1] = pf_curr[1]; + tc.basis.rows[0][2] = pf_curr[2]; + tc.basis.rows[1][0] = pf_curr[4]; + tc.basis.rows[1][1] = pf_curr[5]; + tc.basis.rows[1][2] = pf_curr[6]; + tc.basis.rows[2][0] = pf_curr[8]; + tc.basis.rows[2][1] = pf_curr[9]; + tc.basis.rows[2][2] = pf_curr[10]; + tc.origin.x = pf_curr[3]; + tc.origin.y = pf_curr[7]; + tc.origin.z = pf_curr[11]; + + TransformInterpolator::interpolate_transform_3d(tp, tc, tr, f); + + pf_int[0] = tr.basis.rows[0][0]; + pf_int[1] = tr.basis.rows[0][1]; + pf_int[2] = tr.basis.rows[0][2]; + pf_int[4] = tr.basis.rows[1][0]; + pf_int[5] = tr.basis.rows[1][1]; + pf_int[6] = tr.basis.rows[1][2]; + pf_int[8] = tr.basis.rows[2][0]; + pf_int[9] = tr.basis.rows[2][1]; + pf_int[10] = tr.basis.rows[2][2]; + pf_int[3] = tr.origin.x; + pf_int[7] = tr.origin.y; + pf_int[11] = tr.origin.z; + } + + pf_prev += mmi->_vf_size_xform; + pf_curr += mmi->_vf_size_xform; + pf_int += mmi->_vf_size_xform; + + // Color. + if (mmi->_vf_size_color == 4) { + for (int i = 0; i < 4; i++) { + pf_int[i] = Math::lerp(pf_prev[i], pf_curr[i], f); + } + + pf_prev += 4; + pf_curr += 4; + pf_int += 4; + } + + // Custom data. + if (mmi->_vf_size_data == 4) { + for (int i = 0; i < 4; i++) { + pf_int[i] = Math::lerp(pf_prev[i], pf_curr[i], f); + } + + pf_prev += 4; + pf_curr += 4; + pf_int += 4; + } + } + + _multimesh_set_buffer(rid, mmi->_data_interpolated); + + // TODO: Make sure AABBs are constantly up to date through the interpolation? + // NYI. + } + } + } +} diff --git a/servers/rendering/storage/mesh_storage.h b/servers/rendering/storage/mesh_storage.h index 39fd4f393d..ecd2a967d0 100644 --- a/servers/rendering/storage/mesh_storage.h +++ b/servers/rendering/storage/mesh_storage.h @@ -89,39 +89,110 @@ public: virtual void update_mesh_instances() = 0; /* MULTIMESH API */ + struct MultiMeshInterpolator { + RS::MultimeshTransformFormat _transform_format = RS::MULTIMESH_TRANSFORM_3D; + bool _use_colors = false; + bool _use_custom_data = false; - virtual RID multimesh_allocate() = 0; - virtual void multimesh_initialize(RID p_rid) = 0; - virtual void multimesh_free(RID p_rid) = 0; + // The stride of the buffer in floats. + int _stride = 0; - virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) = 0; + // Vertex format sizes in floats. + int _vf_size_xform = 0; + int _vf_size_color = 0; + int _vf_size_data = 0; - virtual int multimesh_get_instance_count(RID p_multimesh) const = 0; + // Set by allocate, can be used to prevent indexing out of range. + int _num_instances = 0; - virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) = 0; - virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) = 0; - virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) = 0; - virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) = 0; - virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) = 0; + // Quality determines whether to use lerp or slerp etc. + int quality = 0; + bool interpolated = false; + bool on_interpolate_update_list = false; + bool on_transform_update_list = false; - virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) = 0; - virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const = 0; + Vector<float> _data_prev; + Vector<float> _data_curr; + Vector<float> _data_interpolated; + }; - virtual RID multimesh_get_mesh(RID p_multimesh) const = 0; + virtual RID multimesh_allocate(); + virtual void multimesh_initialize(RID p_rid); + virtual void multimesh_free(RID p_rid); - virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const = 0; - virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const = 0; - virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const = 0; - virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const = 0; + virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false); - virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) = 0; - virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const = 0; + virtual int multimesh_get_instance_count(RID p_multimesh) const; - virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) = 0; - virtual int multimesh_get_visible_instances(RID p_multimesh) const = 0; + virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh); + virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform); + virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform); + virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color); + virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color); - virtual AABB multimesh_get_aabb(RID p_multimesh) const = 0; + virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb); + virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const; + virtual RID multimesh_get_mesh(RID p_multimesh) const; + + virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const; + virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const; + virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const; + virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const; + + virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer); + virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const; + + virtual void multimesh_set_buffer_interpolated(RID p_multimesh, const Vector<float> &p_buffer, const Vector<float> &p_buffer_prev); + virtual void multimesh_set_physics_interpolated(RID p_multimesh, bool p_interpolated); + virtual void multimesh_set_physics_interpolation_quality(RID p_multimesh, RS::MultimeshPhysicsInterpolationQuality p_quality); + virtual void multimesh_instance_reset_physics_interpolation(RID p_multimesh, int p_index); + + virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible); + virtual int multimesh_get_visible_instances(RID p_multimesh) const; + + virtual AABB multimesh_get_aabb(RID p_multimesh) const; + + virtual RID _multimesh_allocate() = 0; + virtual void _multimesh_initialize(RID p_rid) = 0; + virtual void _multimesh_free(RID p_rid) = 0; + + virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) = 0; + + virtual int _multimesh_get_instance_count(RID p_multimesh) const = 0; + + virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) = 0; + virtual void _multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) = 0; + virtual void _multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) = 0; + virtual void _multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) = 0; + virtual void _multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) = 0; + + virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) = 0; + virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const = 0; + + virtual RID _multimesh_get_mesh(RID p_multimesh) const = 0; + + virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const = 0; + virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const = 0; + virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const = 0; + virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const = 0; + + virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) = 0; + virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const = 0; + + virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) = 0; + virtual int _multimesh_get_visible_instances(RID p_multimesh) const = 0; + + virtual AABB _multimesh_get_aabb(RID p_multimesh) const = 0; + + // Multimesh is responsible for allocating / destroying a MultiMeshInterpolator object. + // This allows shared functionality for interpolation across backends. + virtual MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const = 0; + +private: + void _multimesh_add_to_interpolation_lists(RID p_multimesh, MultiMeshInterpolator &r_mmi); + +public: /* SKELETON API */ virtual RID skeleton_allocate() = 0; @@ -137,6 +208,19 @@ public: virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0; virtual void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) = 0; + + /* INTERPOLATION */ + + struct InterpolationData { + void notify_free_multimesh(RID p_rid); + LocalVector<RID> multimesh_interpolate_update_list; + LocalVector<RID> multimesh_transform_update_lists[2]; + LocalVector<RID> *multimesh_transform_update_list_curr = &multimesh_transform_update_lists[0]; + LocalVector<RID> *multimesh_transform_update_list_prev = &multimesh_transform_update_lists[1]; + } _interpolation_data; + + void update_interpolation_tick(bool p_process = true); + void update_interpolation_frame(bool p_process = true); }; #endif // MESH_STORAGE_H |