summaryrefslogtreecommitdiffstats
path: root/servers
diff options
context:
space:
mode:
Diffstat (limited to 'servers')
-rw-r--r--servers/audio_server.cpp2
-rw-r--r--servers/display_server.cpp19
-rw-r--r--servers/display_server.h12
-rw-r--r--servers/rendering/dummy/rasterizer_canvas_dummy.h1
-rw-r--r--servers/rendering/dummy/rasterizer_dummy.h1
-rw-r--r--servers/rendering/dummy/rasterizer_scene_dummy.h5
-rw-r--r--servers/rendering/dummy/storage/texture_storage.h2
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp139
-rw-r--r--servers/rendering/renderer_canvas_cull.h18
-rw-r--r--servers/rendering/renderer_canvas_render.h1
-rw-r--r--servers/rendering/renderer_compositor.h1
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp881
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h92
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp519
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h146
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp641
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h95
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp407
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h130
-rw-r--r--servers/rendering/renderer_rd/pipeline_cache_rd.cpp3
-rw-r--r--servers/rendering/renderer_rd/pipeline_cache_rd.h8
-rw-r--r--servers/rendering/renderer_rd/pipeline_hash_map_rd.h218
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp523
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h126
-rw-r--r--servers/rendering/renderer_rd/renderer_compositor_rd.h1
-rw-r--r--servers/rendering/renderer_rd/shader_rd.cpp114
-rw-r--r--servers/rendering/renderer_rd/shader_rd.h19
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas.glsl183
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl38
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl55
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl87
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl97
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl119
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl54
-rw-r--r--servers/rendering/renderer_rd/storage_rd/light_storage.cpp67
-rw-r--r--servers/rendering/renderer_rd/storage_rd/light_storage.h18
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.cpp89
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.h14
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp202
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.h9
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp140
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h25
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.cpp44
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.h5
-rw-r--r--servers/rendering/renderer_scene_cull.cpp11
-rw-r--r--servers/rendering/renderer_scene_cull.h3
-rw-r--r--servers/rendering/renderer_scene_render.h5
-rw-r--r--servers/rendering/rendering_device.cpp1300
-rw-r--r--servers/rendering/rendering_device.h130
-rw-r--r--servers/rendering/rendering_device_commons.cpp2
-rw-r--r--servers/rendering/rendering_device_commons.h2
-rw-r--r--servers/rendering/rendering_device_driver.cpp2
-rw-r--r--servers/rendering/rendering_device_driver.h6
-rw-r--r--servers/rendering/rendering_method.h5
-rw-r--r--servers/rendering/rendering_server_default.cpp10
-rw-r--r--servers/rendering/rendering_server_default.h93
-rw-r--r--servers/rendering/shader_compiler.cpp10
-rw-r--r--servers/rendering/shader_language.cpp368
-rw-r--r--servers/rendering/shader_language.h4
-rw-r--r--servers/rendering/storage/texture_storage.h1
-rw-r--r--servers/rendering_server.cpp12
-rw-r--r--servers/rendering_server.h19
62 files changed, 5066 insertions, 2287 deletions
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 5835ecfed0..70ef88e36d 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -1248,6 +1248,8 @@ void AudioServer::stop_playback_stream(Ref<AudioStreamPlayback> p_playback) {
return;
}
+ p_playback->stop();
+
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return;
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 86b4016da8..ce0d6cb996 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -634,6 +634,10 @@ int DisplayServer::virtual_keyboard_get_height() const {
ERR_FAIL_V_MSG(0, "Virtual keyboard not supported by this display server.");
}
+bool DisplayServer::has_hardware_keyboard() const {
+ return true;
+}
+
void DisplayServer::cursor_set_shape(CursorShape p_shape) {
WARN_PRINT("Cursor shape not supported by this display server.");
}
@@ -976,6 +980,8 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("virtual_keyboard_get_height"), &DisplayServer::virtual_keyboard_get_height);
+ ClassDB::bind_method(D_METHOD("has_hardware_keyboard"), &DisplayServer::has_hardware_keyboard);
+
ClassDB::bind_method(D_METHOD("cursor_set_shape", "shape"), &DisplayServer::cursor_set_shape);
ClassDB::bind_method(D_METHOD("cursor_get_shape"), &DisplayServer::cursor_get_shape);
ClassDB::bind_method(D_METHOD("cursor_set_custom_image", "cursor", "shape", "hotspot"), &DisplayServer::cursor_set_custom_image, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2()));
@@ -1229,6 +1235,12 @@ bool DisplayServer::can_create_rendering_device() {
return true;
}
+ if (created_rendering_device == RenderingDeviceCreationStatus::SUCCESS) {
+ return true;
+ } else if (created_rendering_device == RenderingDeviceCreationStatus::FAILURE) {
+ return false;
+ }
+
Error err;
RenderingContextDriver *rcd = nullptr;
@@ -1258,7 +1270,14 @@ bool DisplayServer::can_create_rendering_device() {
memdelete(rd);
rd = nullptr;
if (err == OK) {
+ // Creating a RenderingDevice is quite slow.
+ // Cache the result for future usage, so that it's much faster on subsequent calls.
+ created_rendering_device = RenderingDeviceCreationStatus::SUCCESS;
+ memdelete(rcd);
+ rcd = nullptr;
return true;
+ } else {
+ created_rendering_device = RenderingDeviceCreationStatus::FAILURE;
}
}
diff --git a/servers/display_server.h b/servers/display_server.h
index 04f4b0c03d..36798bd011 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -507,6 +507,8 @@ public:
// returns height of the currently shown virtual keyboard (0 if keyboard is hidden)
virtual int virtual_keyboard_get_height() const;
+ virtual bool has_hardware_keyboard() const;
+
enum CursorShape {
CURSOR_ARROW,
CURSOR_IBEAM,
@@ -594,6 +596,16 @@ public:
static Vector<String> get_create_function_rendering_drivers(int p_index);
static DisplayServer *create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
+ enum RenderingDeviceCreationStatus {
+ UNKNOWN,
+ SUCCESS,
+ FAILURE,
+ };
+
+ // Used to cache the result of `can_create_rendering_device()` when RenderingDevice isn't currently being used.
+ // This is done as creating a RenderingDevice is quite slow.
+ static inline RenderingDeviceCreationStatus created_rendering_device = RenderingDeviceCreationStatus::UNKNOWN;
+
static bool can_create_rendering_device();
DisplayServer();
diff --git a/servers/rendering/dummy/rasterizer_canvas_dummy.h b/servers/rendering/dummy/rasterizer_canvas_dummy.h
index a450b2a21d..d61ee1bdb6 100644
--- a/servers/rendering/dummy/rasterizer_canvas_dummy.h
+++ b/servers/rendering/dummy/rasterizer_canvas_dummy.h
@@ -56,6 +56,7 @@ public:
void update() override {}
virtual void set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) override {}
+ virtual uint32_t get_pipeline_compilations(RS::PipelineSource p_source) override { return 0; }
RasterizerCanvasDummy() {}
~RasterizerCanvasDummy() {}
diff --git a/servers/rendering/dummy/rasterizer_dummy.h b/servers/rendering/dummy/rasterizer_dummy.h
index c3f5f3102d..7640afc711 100644
--- a/servers/rendering/dummy/rasterizer_dummy.h
+++ b/servers/rendering/dummy/rasterizer_dummy.h
@@ -110,6 +110,7 @@ public:
uint64_t get_frame_number() const override { return frame; }
double get_frame_delta_time() const override { return delta; }
double get_total_time() const override { return time; }
+ bool can_create_resources_async() const override { return false; }
RasterizerDummy() {}
~RasterizerDummy() {}
diff --git a/servers/rendering/dummy/rasterizer_scene_dummy.h b/servers/rendering/dummy/rasterizer_scene_dummy.h
index a699a58b1f..f129c86746 100644
--- a/servers/rendering/dummy/rasterizer_scene_dummy.h
+++ b/servers/rendering/dummy/rasterizer_scene_dummy.h
@@ -94,6 +94,11 @@ public:
uint32_t geometry_instance_get_pair_mask() override { return 0; }
+ /* PIPELINES */
+
+ virtual void mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) override {}
+ virtual uint32_t get_pipeline_compilations(RS::PipelineSource p_source) override { return 0; }
+
/* SDFGI UPDATE */
void sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {}
diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h
index 6da7446e69..6735f6bcda 100644
--- a/servers/rendering/dummy/storage/texture_storage.h
+++ b/servers/rendering/dummy/storage/texture_storage.h
@@ -51,8 +51,6 @@ public:
TextureStorage();
~TextureStorage();
- virtual bool can_create_resources_async() const override { return false; }
-
/* Canvas Texture API */
virtual RID canvas_texture_allocate() override { return RID(); };
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index 0ec161d8cf..701b4da8f8 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, Point2(), 1, nullptr);
+ _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, false, p_canvas_cull_mask, Point2(), 1, nullptr);
}
RendererCanvasRender::Item *list = nullptr;
@@ -79,45 +79,71 @@ void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas
}
}
-void _collect_ysort_children(RendererCanvasCull::Item *p_canvas_item, const Transform2D &p_transform, RendererCanvasCull::Item *p_material_owner, const Color &p_modulate, RendererCanvasCull::Item **r_items, int &r_index, int p_z) {
+void RendererCanvasCull::_collect_ysort_children(RendererCanvasCull::Item *p_canvas_item, RendererCanvasCull::Item *p_material_owner, const Color &p_modulate, RendererCanvasCull::Item **r_items, int &r_index, int p_z) {
int child_item_count = p_canvas_item->child_items.size();
RendererCanvasCull::Item **child_items = p_canvas_item->child_items.ptrw();
for (int i = 0; i < child_item_count; i++) {
- int abs_z = 0;
if (child_items[i]->visible) {
- if (r_items) {
- r_items[r_index] = child_items[i];
- child_items[i]->ysort_xform = p_transform;
- child_items[i]->ysort_pos = p_transform.xform(child_items[i]->xform_curr.columns[2]);
- child_items[i]->material_owner = child_items[i]->use_parent_material ? p_material_owner : nullptr;
- child_items[i]->ysort_modulate = p_modulate;
- child_items[i]->ysort_index = r_index;
- child_items[i]->ysort_parent_abs_z_index = p_z;
-
- 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;
- }
+ // To y-sort according to the item's final position, physics interpolation
+ // and transform snapping need to be applied before y-sorting.
+ Transform2D child_xform;
+ if (!_interpolation_data.interpolation_enabled || !child_items[i]->interpolated) {
+ child_xform = child_items[i]->xform_curr;
+ } else {
+ real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();
+ TransformInterpolator::interpolate_transform_2d(child_items[i]->xform_prev, child_items[i]->xform_curr, child_xform, f);
+ }
- // Y sorted canvas items are flattened into r_items. Calculate their absolute z index to use when rendering r_items.
- if (child_items[i]->z_relative) {
- abs_z = CLAMP(p_z + child_items[i]->z_index, RS::CANVAS_ITEM_Z_MIN, RS::CANVAS_ITEM_Z_MAX);
- } else {
- abs_z = child_items[i]->z_index;
- }
+ if (snapping_2d_transforms_to_pixel) {
+ child_xform.columns[2] = (child_xform.columns[2] + Point2(0.5, 0.5)).floor();
+ }
+
+ r_items[r_index] = child_items[i];
+ child_items[i]->ysort_xform = p_canvas_item->ysort_xform * child_xform;
+ child_items[i]->material_owner = child_items[i]->use_parent_material ? p_material_owner : nullptr;
+ child_items[i]->ysort_modulate = p_modulate;
+ child_items[i]->ysort_index = r_index;
+ child_items[i]->ysort_parent_abs_z_index = p_z;
+
+ 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.
+ int abs_z = 0;
+ if (child_items[i]->z_relative) {
+ abs_z = CLAMP(p_z + child_items[i]->z_index, RS::CANVAS_ITEM_Z_MIN, RS::CANVAS_ITEM_Z_MAX);
+ } else {
+ abs_z = child_items[i]->z_index;
}
r_index++;
if (child_items[i]->sort_y) {
- _collect_ysort_children(child_items[i], p_transform * child_items[i]->xform_curr, child_items[i]->use_parent_material ? p_material_owner : child_items[i], p_modulate * child_items[i]->modulate, r_items, r_index, abs_z);
+ _collect_ysort_children(child_items[i], child_items[i]->use_parent_material ? p_material_owner : child_items[i], p_modulate * child_items[i]->modulate, r_items, r_index, abs_z);
}
}
}
}
-void _mark_ysort_dirty(RendererCanvasCull::Item *ysort_owner, RID_Owner<RendererCanvasCull::Item, true> &canvas_item_owner) {
+int RendererCanvasCull::_count_ysort_children(RendererCanvasCull::Item *p_canvas_item) {
+ int ysort_children_count = 0;
+ int child_item_count = p_canvas_item->child_items.size();
+ RendererCanvasCull::Item *const *child_items = p_canvas_item->child_items.ptr();
+ for (int i = 0; i < child_item_count; i++) {
+ if (child_items[i]->visible) {
+ ysort_children_count++;
+ if (child_items[i]->sort_y) {
+ ysort_children_count += _count_ysort_children(child_items[i]);
+ }
+ }
+ }
+ return ysort_children_count;
+}
+
+void RendererCanvasCull::_mark_ysort_dirty(RendererCanvasCull::Item *ysort_owner) {
do {
ysort_owner->ysort_children_count = -1;
ysort_owner = canvas_item_owner.owns(ysort_owner->parent) ? canvas_item_owner.get_or_null(ysort_owner->parent) : nullptr;
@@ -236,7 +262,7 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item *
}
}
-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) {
+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_is_already_y_sorted, 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) {
@@ -260,15 +286,29 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
}
}
+ Transform2D self_xform;
Transform2D final_xform;
- if (!_interpolation_data.interpolation_enabled || !ci->interpolated) {
- final_xform = ci->xform_curr;
+ if (p_is_already_y_sorted) {
+ // Y-sorted item's final transform is calculated before y-sorting,
+ // and is passed as `p_parent_xform` afterwards. No need to recalculate.
+ final_xform = p_parent_xform;
} else {
- real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();
- TransformInterpolator::interpolate_transform_2d(ci->xform_prev, ci->xform_curr, final_xform, f);
- }
+ if (!_interpolation_data.interpolation_enabled || !ci->interpolated) {
+ self_xform = ci->xform_curr;
+ } else {
+ real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();
+ TransformInterpolator::interpolate_transform_2d(ci->xform_prev, ci->xform_curr, self_xform, f);
+ }
- Transform2D parent_xform = p_parent_xform;
+ Transform2D parent_xform = p_parent_xform;
+
+ if (snapping_2d_transforms_to_pixel) {
+ self_xform.columns[2] = (self_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 * self_xform;
+ }
Point2 repeat_size = p_repeat_size;
int repeat_times = p_repeat_times;
@@ -284,13 +324,6 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
ci->repeat_source_item = repeat_source_item;
}
- if (snapping_2d_transforms_to_pixel) {
- 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.
@@ -355,29 +388,27 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
}
if (ci->sort_y) {
- if (p_allow_y_sort) {
+ if (!p_is_already_y_sorted) {
if (ci->ysort_children_count == -1) {
- ci->ysort_children_count = 0;
- _collect_ysort_children(ci, Transform2D(), p_material_owner, Color(1, 1, 1, 1), nullptr, ci->ysort_children_count, p_z);
+ ci->ysort_children_count = _count_ysort_children(ci);
}
child_item_count = ci->ysort_children_count + 1;
child_items = (Item **)alloca(child_item_count * sizeof(Item *));
- ci->ysort_xform = ci->xform_curr.affine_inverse();
- ci->ysort_pos = Vector2();
+ ci->ysort_xform = Transform2D();
ci->ysort_modulate = Color(1, 1, 1, 1);
ci->ysort_index = 0;
ci->ysort_parent_abs_z_index = parent_z;
child_items[0] = ci;
int i = 1;
- _collect_ysort_children(ci, Transform2D(), p_material_owner, Color(1, 1, 1, 1), child_items, i, p_z);
+ _collect_ysort_children(ci, p_material_owner, Color(1, 1, 1, 1), child_items, i, p_z);
- SortArray<Item *, ItemPtrSort> sorter;
+ SortArray<Item *, ItemYSort> sorter;
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, child_items[i]->repeat_source_item);
+ _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, true, 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;
@@ -401,14 +432,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, repeat_source_item);
+ _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, false, 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, repeat_source_item);
+ _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, false, p_canvas_cull_mask, repeat_size, repeat_times, repeat_source_item);
}
}
}
@@ -509,7 +540,7 @@ void RendererCanvasCull::canvas_item_set_parent(RID p_item, RID p_parent) {
item_owner->child_items.erase(canvas_item);
if (item_owner->sort_y) {
- _mark_ysort_dirty(item_owner, canvas_item_owner);
+ _mark_ysort_dirty(item_owner);
}
}
@@ -529,7 +560,7 @@ void RendererCanvasCull::canvas_item_set_parent(RID p_item, RID p_parent) {
item_owner->children_order_dirty = true;
if (item_owner->sort_y) {
- _mark_ysort_dirty(item_owner, canvas_item_owner);
+ _mark_ysort_dirty(item_owner);
}
} else {
@@ -546,7 +577,7 @@ void RendererCanvasCull::canvas_item_set_visible(RID p_item, bool p_visible) {
canvas_item->visible = p_visible;
- _mark_ysort_dirty(canvas_item, canvas_item_owner);
+ _mark_ysort_dirty(canvas_item);
}
void RendererCanvasCull::canvas_item_set_light_mask(RID p_item, int p_mask) {
@@ -1742,7 +1773,7 @@ void RendererCanvasCull::canvas_item_set_sort_children_by_y(RID p_item, bool p_e
canvas_item->sort_y = p_enable;
- _mark_ysort_dirty(canvas_item, canvas_item_owner);
+ _mark_ysort_dirty(canvas_item);
}
void RendererCanvasCull::canvas_item_set_z_index(RID p_item, int p_z) {
@@ -2423,7 +2454,7 @@ bool RendererCanvasCull::free(RID p_rid) {
item_owner->child_items.erase(canvas_item);
if (item_owner->sort_y) {
- _mark_ysort_dirty(item_owner, canvas_item_owner);
+ _mark_ysort_dirty(item_owner);
}
}
}
diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h
index 91c03054f7..9a088d94ed 100644
--- a/servers/rendering/renderer_canvas_cull.h
+++ b/servers/rendering/renderer_canvas_cull.h
@@ -50,8 +50,7 @@ public:
bool children_order_dirty;
int ysort_children_count;
Color ysort_modulate;
- Transform2D ysort_xform;
- Vector2 ysort_pos;
+ Transform2D ysort_xform; // Relative to y-sorted subtree's root item (identity for such root). Its `origin.y` is used for sorting.
int ysort_index;
int ysort_parent_abs_z_index; // Absolute Z index of parent. Only populated and used when y-sorting.
uint32_t visibility_layer = 0xffffffff;
@@ -84,7 +83,6 @@ public:
index = 0;
ysort_children_count = -1;
ysort_xform = Transform2D();
- ysort_pos = Vector2();
ysort_index = 0;
ysort_parent_abs_z_index = 0;
}
@@ -96,13 +94,15 @@ public:
}
};
- struct ItemPtrSort {
+ struct ItemYSort {
_FORCE_INLINE_ bool operator()(const Item *p_left, const Item *p_right) const {
- if (Math::is_equal_approx(p_left->ysort_pos.y, p_right->ysort_pos.y)) {
+ const real_t left_y = p_left->ysort_xform.columns[2].y;
+ const real_t right_y = p_right->ysort_xform.columns[2].y;
+ if (Math::is_equal_approx(left_y, right_y)) {
return p_left->ysort_index < p_right->ysort_index;
}
- return p_left->ysort_pos.y < p_right->ysort_pos.y;
+ return left_y < right_y;
}
};
@@ -187,7 +187,11 @@ 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, RendererCanvasRender::Item *p_repeat_source_item);
+ 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_is_already_y_sorted, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times, RendererCanvasRender::Item *p_repeat_source_item);
+
+ void _collect_ysort_children(RendererCanvasCull::Item *p_canvas_item, RendererCanvasCull::Item *p_material_owner, const Color &p_modulate, RendererCanvasCull::Item **r_items, int &r_index, int p_z);
+ int _count_ysort_children(RendererCanvasCull::Item *p_canvas_item);
+ void _mark_ysort_dirty(RendererCanvasCull::Item *ysort_owner);
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 c57abee165..328fe32ea6 100644
--- a/servers/rendering/renderer_canvas_render.h
+++ b/servers/rendering/renderer_canvas_render.h
@@ -545,6 +545,7 @@ public:
virtual void update() = 0;
virtual void set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) = 0;
+ virtual uint32_t get_pipeline_compilations(RS::PipelineSource p_source) = 0;
RendererCanvasRender() {
ERR_FAIL_COND_MSG(singleton != nullptr, "A RendererCanvasRender singleton already exists.");
diff --git a/servers/rendering/renderer_compositor.h b/servers/rendering/renderer_compositor.h
index 933b9e457f..a585c9430b 100644
--- a/servers/rendering/renderer_compositor.h
+++ b/servers/rendering/renderer_compositor.h
@@ -104,6 +104,7 @@ public:
virtual uint64_t get_frame_number() const = 0;
virtual double get_frame_delta_time() const = 0;
virtual double get_total_time() const = 0;
+ virtual bool can_create_resources_async() const = 0;
static bool is_low_end() { return low_end; };
virtual bool is_xr_enabled() const;
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 307898232d..76d8972ad9 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -31,6 +31,7 @@
#include "render_forward_clustered.h"
#include "core/config/project_settings.h"
#include "core/object/worker_thread_pool.h"
+#include "scene/resources/material.h"
#include "servers/rendering/renderer_rd/framebuffer_cache_rd.h"
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#include "servers/rendering/renderer_rd/storage_rd/light_storage.h"
@@ -43,23 +44,18 @@
using namespace RendererSceneRenderImplementation;
+#define PRELOAD_PIPELINES_ON_SURFACE_CACHE_CONSTRUCTION 1
+
+#define FADE_ALPHA_PASS_THRESHOLD 0.999
+
void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_specular() {
ERR_FAIL_NULL(render_buffers);
if (!render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR)) {
- RD::DataFormat format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
- uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
- if (render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
- usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
- } else {
- usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
- }
-
- render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR, format, usage_bits);
-
- if (render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
- usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
- render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR_MSAA, format, usage_bits, render_buffers->get_texture_samples());
+ bool msaa = render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED;
+ render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR, get_specular_format(), get_specular_usage_bits(msaa, false, render_buffers->get_can_be_storage()));
+ if (msaa) {
+ render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR_MSAA, get_specular_format(), get_specular_usage_bits(false, msaa, render_buffers->get_can_be_storage()), render_buffers->get_texture_samples());
}
}
}
@@ -68,20 +64,10 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_normal_rou
ERR_FAIL_NULL(render_buffers);
if (!render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_NORMAL_ROUGHNESS)) {
- RD::DataFormat format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
- uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
-
- if (render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
- usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
- } else {
- usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
- }
-
- render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_NORMAL_ROUGHNESS, format, usage_bits);
-
- if (render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
- usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
- render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_NORMAL_ROUGHNESS_MSAA, format, usage_bits, render_buffers->get_texture_samples());
+ bool msaa = render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED;
+ render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_NORMAL_ROUGHNESS, get_normal_roughness_format(), get_normal_roughness_usage_bits(msaa, false, render_buffers->get_can_be_storage()));
+ if (msaa) {
+ render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_NORMAL_ROUGHNESS_MSAA, get_normal_roughness_format(), get_normal_roughness_usage_bits(false, msaa, render_buffers->get_can_be_storage()), render_buffers->get_texture_samples());
}
}
}
@@ -90,17 +76,10 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_voxelgi()
ERR_FAIL_NULL(render_buffers);
if (!render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI)) {
- RD::DataFormat format = RD::DATA_FORMAT_R8G8_UINT;
- uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
- if (render_buffers->get_msaa_3d() == RS::VIEWPORT_MSAA_DISABLED) {
- usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
- }
-
- render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI, format, usage_bits);
-
- if (render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
- usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
- render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI_MSAA, format, usage_bits, render_buffers->get_texture_samples());
+ bool msaa = render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED;
+ render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI, get_voxelgi_format(), get_voxelgi_usage_bits(msaa, false, render_buffers->get_can_be_storage()));
+ if (msaa) {
+ render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI_MSAA, get_voxelgi_format(), get_voxelgi_usage_bits(false, msaa, render_buffers->get_can_be_storage()), render_buffers->get_texture_samples());
}
}
}
@@ -249,6 +228,30 @@ RID RenderForwardClustered::RenderBufferDataForwardClustered::get_velocity_only_
return FramebufferCacheRD::get_singleton()->get_cache_multiview(render_buffers->get_view_count(), velocity);
}
+RD::DataFormat RenderForwardClustered::RenderBufferDataForwardClustered::get_specular_format() {
+ return RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+}
+
+uint32_t RenderForwardClustered::RenderBufferDataForwardClustered::get_specular_usage_bits(bool p_resolve, bool p_msaa, bool p_storage) {
+ return RenderSceneBuffersRD::get_color_usage_bits(p_resolve, p_msaa, p_storage);
+}
+
+RD::DataFormat RenderForwardClustered::RenderBufferDataForwardClustered::get_normal_roughness_format() {
+ return RD::DATA_FORMAT_R8G8B8A8_UNORM;
+}
+
+uint32_t RenderForwardClustered::RenderBufferDataForwardClustered::get_normal_roughness_usage_bits(bool p_resolve, bool p_msaa, bool p_storage) {
+ return RenderSceneBuffersRD::get_color_usage_bits(p_resolve, p_msaa, p_storage);
+}
+
+RD::DataFormat RenderForwardClustered::RenderBufferDataForwardClustered::get_voxelgi_format() {
+ return RD::DATA_FORMAT_R8G8_UINT;
+}
+
+uint32_t RenderForwardClustered::RenderBufferDataForwardClustered::get_voxelgi_usage_bits(bool p_resolve, bool p_msaa, bool p_storage) {
+ return RenderSceneBuffersRD::get_color_usage_bits(p_resolve, p_msaa, p_storage);
+}
+
void RenderForwardClustered::setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_render_buffers) {
Ref<RenderBufferDataForwardClustered> data;
data.instantiate();
@@ -266,6 +269,12 @@ bool RenderForwardClustered::free(RID p_rid) {
return false;
}
+void RenderForwardClustered::update() {
+ RendererSceneRenderRD::update();
+ _update_global_pipeline_data_requirements_from_project();
+ _update_global_pipeline_data_requirements_from_light_storage();
+}
+
/// RENDERING ///
template <RenderForwardClustered::PassMode p_pass_mode, uint32_t p_color_pass_flags>
@@ -284,9 +293,14 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p
RID prev_vertex_array_rd;
RID prev_index_array_rd;
- RID prev_pipeline_rd;
RID prev_xforms_uniform_set;
+ SceneShaderForwardClustered::ShaderData *shader = nullptr;
+ SceneShaderForwardClustered::ShaderData *prev_shader = nullptr;
+ SceneShaderForwardClustered::ShaderData::PipelineKey pipeline_key;
+ uint32_t pipeline_hash = 0;
+ uint32_t prev_pipeline_hash = 0;
+
bool shadow_pass = (p_pass_mode == PASS_MODE_SHADOW) || (p_pass_mode == PASS_MODE_SHADOW_DP);
SceneState::PushConstant push_constant;
@@ -317,7 +331,6 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p
push_constant.base_index = i + p_params->element_offset;
RID material_uniform_set;
- SceneShaderForwardClustered::ShaderData *shader;
void *mesh_surface;
if (shadow_pass || p_pass_mode == PASS_MODE_DEPTH) { //regular depth pass can use these too
@@ -356,166 +369,211 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p
should_request_redraw = true;
}
- //find cull variant
- SceneShaderForwardClustered::ShaderData::CullVariant cull_variant;
-
- if (p_pass_mode == PASS_MODE_DEPTH_MATERIAL || p_pass_mode == PASS_MODE_SDF || ((p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_SHADOW_DP) && surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_DOUBLE_SIDED_SHADOWS)) {
+ // Determine the cull variant.
+ SceneShaderForwardClustered::ShaderData::CullVariant cull_variant = SceneShaderForwardClustered::ShaderData::CULL_VARIANT_MAX;
+ if constexpr (p_pass_mode == PASS_MODE_DEPTH_MATERIAL || p_pass_mode == PASS_MODE_SDF) {
cull_variant = SceneShaderForwardClustered::ShaderData::CULL_VARIANT_DOUBLE_SIDED;
} else {
- bool mirror = surf->owner->mirror;
- if (p_params->reverse_cull) {
- mirror = !mirror;
+ if constexpr (p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_SHADOW_DP) {
+ if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_DOUBLE_SIDED_SHADOWS) {
+ cull_variant = SceneShaderForwardClustered::ShaderData::CULL_VARIANT_DOUBLE_SIDED;
+ }
+ }
+
+ if (cull_variant == SceneShaderForwardClustered::ShaderData::CULL_VARIANT_MAX) {
+ bool mirror = surf->owner->mirror;
+ if (p_params->reverse_cull) {
+ mirror = !mirror;
+ }
+
+ cull_variant = mirror ? SceneShaderForwardClustered::ShaderData::CULL_VARIANT_REVERSED : SceneShaderForwardClustered::ShaderData::CULL_VARIANT_NORMAL;
}
- cull_variant = mirror ? SceneShaderForwardClustered::ShaderData::CULL_VARIANT_REVERSED : SceneShaderForwardClustered::ShaderData::CULL_VARIANT_NORMAL;
}
- RS::PrimitiveType primitive = surf->primitive;
+ pipeline_key.primitive_type = surf->primitive;
+
RID xforms_uniform_set = surf->owner->transforms_uniform_set;
- SceneShaderForwardClustered::PipelineVersion pipeline_version = SceneShaderForwardClustered::PIPELINE_VERSION_MAX; // Assigned to silence wrong -Wmaybe-initialized.
- uint32_t pipeline_color_pass_flags = 0;
- uint32_t pipeline_specialization = p_params->spec_constant_base_flags;
+ SceneShaderForwardClustered::ShaderSpecialization pipeline_specialization = p_params->base_specialization;
if constexpr (p_pass_mode == PASS_MODE_COLOR) {
- if (element_info.uses_softshadow) {
- pipeline_specialization |= SceneShaderForwardClustered::SHADER_SPECIALIZATION_SOFT_SHADOWS;
- }
- if (element_info.uses_projector) {
- pipeline_specialization |= SceneShaderForwardClustered::SHADER_SPECIALIZATION_PROJECTOR;
- }
-
- if (p_params->use_directional_soft_shadow) {
- pipeline_specialization |= SceneShaderForwardClustered::SHADER_SPECIALIZATION_DIRECTIONAL_SOFT_SHADOWS;
- }
+ pipeline_specialization.use_light_soft_shadows = element_info.uses_softshadow;
+ pipeline_specialization.use_light_projector = element_info.uses_projector;
+ pipeline_specialization.use_directional_soft_shadows = p_params->use_directional_soft_shadow;
}
+ pipeline_key.color_pass_flags = 0;
+
switch (p_pass_mode) {
case PASS_MODE_COLOR: {
if (element_info.uses_lightmap) {
- pipeline_color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_LIGHTMAP;
+ pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_LIGHTMAP;
} else {
- if (element_info.uses_forward_gi) {
- pipeline_specialization |= SceneShaderForwardClustered::SHADER_SPECIALIZATION_FORWARD_GI;
- }
+ pipeline_specialization.use_forward_gi = element_info.uses_forward_gi;
}
if constexpr ((p_color_pass_flags & COLOR_PASS_FLAG_SEPARATE_SPECULAR) != 0) {
- pipeline_color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR;
+ pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR;
}
if constexpr ((p_color_pass_flags & COLOR_PASS_FLAG_MOTION_VECTORS) != 0) {
- pipeline_color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS;
+ pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS;
}
if constexpr ((p_color_pass_flags & COLOR_PASS_FLAG_TRANSPARENT) != 0) {
- pipeline_color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_TRANSPARENT;
+ pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_TRANSPARENT;
}
if constexpr ((p_color_pass_flags & COLOR_PASS_FLAG_MULTIVIEW) != 0) {
- pipeline_color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MULTIVIEW;
+ pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MULTIVIEW;
}
- pipeline_version = SceneShaderForwardClustered::PIPELINE_VERSION_COLOR_PASS;
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_COLOR_PASS;
} break;
case PASS_MODE_SHADOW:
case PASS_MODE_DEPTH: {
- pipeline_version = p_params->view_count > 1 ? SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_MULTIVIEW : SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS;
+ pipeline_key.version = p_params->view_count > 1 ? SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_MULTIVIEW : SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS;
} break;
case PASS_MODE_SHADOW_DP: {
ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for shadow DP pass");
- pipeline_version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_DP;
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_DP;
} break;
case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: {
- pipeline_version = p_params->view_count > 1 ? SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW : SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS;
+ pipeline_key.version = p_params->view_count > 1 ? SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW : SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS;
} break;
case PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI: {
- pipeline_version = p_params->view_count > 1 ? SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW : SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI;
+ pipeline_key.version = p_params->view_count > 1 ? SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW : SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI;
} break;
case PASS_MODE_DEPTH_MATERIAL: {
ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for material pass");
- pipeline_version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_MATERIAL;
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_MATERIAL;
} break;
case PASS_MODE_SDF: {
// Note, SDF is prepared in world space, this shouldn't be a multiview buffer even when stereoscopic rendering is used.
ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for SDF pass");
- pipeline_version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_SDF;
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_SDF;
} break;
}
- PipelineCacheRD *pipeline = nullptr;
+ pipeline_key.framebuffer_format_id = framebuffer_format;
+ pipeline_key.wireframe = p_params->force_wireframe;
+ pipeline_key.ubershader = 0;
- if constexpr (p_pass_mode == PASS_MODE_COLOR) {
- pipeline = &shader->color_pipelines[cull_variant][primitive][pipeline_color_pass_flags];
- } else {
- pipeline = &shader->pipelines[cull_variant][primitive][pipeline_version];
- }
-
- RD::VertexFormatID vertex_format = -1;
+ const RD::PolygonCullMode cull_mode = shader->get_cull_mode_from_cull_variant(cull_variant);
RID vertex_array_rd;
RID index_array_rd;
+ RID pipeline_rd;
+ uint32_t ubershader_iterations = 2;
+ if constexpr (p_pass_mode == PASS_MODE_DEPTH_MATERIAL || p_pass_mode == PASS_MODE_SDF) {
+ ubershader_iterations = 1;
+ }
+
+ bool pipeline_valid = false;
+ while (pipeline_key.ubershader < ubershader_iterations) {
+ // Skeleton and blend shape.
+ RD::VertexFormatID vertex_format = -1;
+ bool pipeline_motion_vectors = pipeline_key.color_pass_flags & SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS;
+ uint64_t input_mask = shader->get_vertex_input_mask(pipeline_key.version, pipeline_key.color_pass_flags, pipeline_key.ubershader);
+ if (surf->owner->mesh_instance.is_valid()) {
+ mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, input_mask, pipeline_motion_vectors, vertex_array_rd, vertex_format);
+ } else {
+ mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, input_mask, pipeline_motion_vectors, vertex_array_rd, vertex_format);
+ }
- //skeleton and blend shape
- bool pipeline_motion_vectors = pipeline_color_pass_flags & SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS;
- if (surf->owner->mesh_instance.is_valid()) {
- mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, pipeline->get_vertex_input_mask(), pipeline_motion_vectors, vertex_array_rd, vertex_format);
- } else {
- mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, pipeline->get_vertex_input_mask(), pipeline_motion_vectors, vertex_array_rd, vertex_format);
- }
+ pipeline_key.vertex_format_id = vertex_format;
- index_array_rd = mesh_storage->mesh_surface_get_index_array(mesh_surface, element_info.lod_index);
+ if (pipeline_key.ubershader) {
+ pipeline_key.shader_specialization = {};
+ pipeline_key.cull_mode = RD::POLYGON_CULL_DISABLED;
+ } else {
+ pipeline_key.shader_specialization = pipeline_specialization;
+ pipeline_key.cull_mode = cull_mode;
+ }
- if (prev_vertex_array_rd != vertex_array_rd) {
- RD::get_singleton()->draw_list_bind_vertex_array(draw_list, vertex_array_rd);
- prev_vertex_array_rd = vertex_array_rd;
- }
+ pipeline_hash = pipeline_key.hash();
+
+ if (shader != prev_shader || pipeline_hash != prev_pipeline_hash) {
+ bool wait_for_compilation = (ubershader_iterations == 1) || pipeline_key.ubershader;
+ RS::PipelineSource pipeline_source = wait_for_compilation ? RS::PIPELINE_SOURCE_DRAW : RS::PIPELINE_SOURCE_SPECIALIZATION;
+ pipeline_rd = shader->pipeline_hash_map.get_pipeline(pipeline_key, pipeline_hash, wait_for_compilation, pipeline_source);
- if (prev_index_array_rd != index_array_rd) {
- if (index_array_rd.is_valid()) {
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array_rd);
+ if (pipeline_rd.is_valid()) {
+ pipeline_valid = true;
+ prev_shader = shader;
+ prev_pipeline_hash = pipeline_hash;
+ break;
+ } else {
+ pipeline_key.ubershader++;
+ }
+ } else {
+ // The same pipeline is bound already.
+ pipeline_valid = true;
+ break;
}
- prev_index_array_rd = index_array_rd;
}
- RID pipeline_rd = pipeline->get_render_pipeline(vertex_format, framebuffer_format, p_params->force_wireframe, 0, pipeline_specialization);
+ if (pipeline_valid) {
+ index_array_rd = mesh_storage->mesh_surface_get_index_array(mesh_surface, element_info.lod_index);
- 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;
- }
+ if (prev_vertex_array_rd != vertex_array_rd) {
+ RD::get_singleton()->draw_list_bind_vertex_array(draw_list, vertex_array_rd);
+ prev_vertex_array_rd = vertex_array_rd;
+ }
- if (xforms_uniform_set.is_valid() && prev_xforms_uniform_set != xforms_uniform_set) {
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, xforms_uniform_set, TRANSFORMS_UNIFORM_SET);
- prev_xforms_uniform_set = xforms_uniform_set;
- }
+ if (prev_index_array_rd != index_array_rd) {
+ if (index_array_rd.is_valid()) {
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array_rd);
+ }
+ prev_index_array_rd = index_array_rd;
+ }
- if (material_uniform_set != prev_material_uniform_set) {
- // Update uniform set.
- if (material_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set.
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_uniform_set, MATERIAL_UNIFORM_SET);
+ if (!pipeline_rd.is_null()) {
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline_rd);
}
- prev_material_uniform_set = material_uniform_set;
- }
+ if (xforms_uniform_set.is_valid() && prev_xforms_uniform_set != xforms_uniform_set) {
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, xforms_uniform_set, TRANSFORMS_UNIFORM_SET);
+ prev_xforms_uniform_set = xforms_uniform_set;
+ }
- if (surf->owner->base_flags & INSTANCE_DATA_FLAG_PARTICLES) {
- particles_storage->particles_get_instance_buffer_motion_vectors_offsets(surf->owner->data->base, push_constant.multimesh_motion_vectors_current_offset, push_constant.multimesh_motion_vectors_previous_offset);
- } else if (surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH) {
- mesh_storage->_multimesh_get_motion_vectors_offsets(surf->owner->data->base, push_constant.multimesh_motion_vectors_current_offset, push_constant.multimesh_motion_vectors_previous_offset);
- } else {
- push_constant.multimesh_motion_vectors_current_offset = 0;
- push_constant.multimesh_motion_vectors_previous_offset = 0;
- }
+ if (material_uniform_set != prev_material_uniform_set) {
+ // Update uniform set.
+ if (material_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set.
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_uniform_set, MATERIAL_UNIFORM_SET);
+ }
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(SceneState::PushConstant));
+ prev_material_uniform_set = material_uniform_set;
+ }
+
+ if (surf->owner->base_flags & INSTANCE_DATA_FLAG_PARTICLES) {
+ particles_storage->particles_get_instance_buffer_motion_vectors_offsets(surf->owner->data->base, push_constant.multimesh_motion_vectors_current_offset, push_constant.multimesh_motion_vectors_previous_offset);
+ } else if (surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH) {
+ mesh_storage->_multimesh_get_motion_vectors_offsets(surf->owner->data->base, push_constant.multimesh_motion_vectors_current_offset, push_constant.multimesh_motion_vectors_previous_offset);
+ } else {
+ push_constant.multimesh_motion_vectors_current_offset = 0;
+ push_constant.multimesh_motion_vectors_previous_offset = 0;
+ }
+
+ size_t push_constant_size = 0;
+ if (pipeline_key.ubershader) {
+ push_constant_size = sizeof(SceneState::PushConstant);
+ push_constant.ubershader.specialization = pipeline_specialization;
+ push_constant.ubershader.constants = {};
+ push_constant.ubershader.constants.cull_mode = cull_mode;
+ } else {
+ push_constant_size = sizeof(SceneState::PushConstant) - sizeof(SceneState::PushConstantUbershader);
+ }
- uint32_t instance_count = surf->owner->instance_count > 1 ? surf->owner->instance_count : element_info.repeat;
- if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_PARTICLE_TRAILS) {
- instance_count /= surf->owner->trail_steps;
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, push_constant_size);
+
+ uint32_t instance_count = surf->owner->instance_count > 1 ? surf->owner->instance_count : element_info.repeat;
+ if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_PARTICLE_TRAILS) {
+ instance_count /= surf->owner->trail_steps;
+ }
+
+ RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count);
}
- RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count);
i += element_info.repeat - 1; //skip equal elements
}
@@ -575,6 +633,9 @@ void RenderForwardClustered::_render_list(RenderingDevice::DrawListID p_draw_lis
case PASS_MODE_SDF: {
_render_list_template<PASS_MODE_SDF>(p_draw_list, p_framebuffer_Format, p_params, p_from_element, p_to_element);
} break;
+ default: {
+ // Unknown pass mode.
+ } break;
}
}
@@ -1013,7 +1074,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
bool force_alpha = false;
#endif
- if (fade_alpha < 0.999) {
+ if (fade_alpha < FADE_ALPHA_PASS_THRESHOLD) {
force_alpha = true;
}
@@ -1691,12 +1752,18 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
reverse_cull = true; // for some reason our views are inverted
samplers = RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default();
+
+ // Indicate pipelines for reflection probes are required.
+ global_pipeline_data_required.use_reflection_probes = true;
} else {
screen_size = rb->get_internal_size();
if (p_render_data->scene_data->calculate_motion_vectors) {
color_pass_flags |= COLOR_PASS_FLAG_MOTION_VECTORS;
scene_shader.enable_advanced_shader_group();
+
+ // Indicate pipelines for motion vectors are required.
+ global_pipeline_data_required.use_motion_vectors = true;
}
if (p_render_data->voxel_gi_instances->size() > 0) {
@@ -1718,6 +1785,9 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
color_pass_flags |= COLOR_PASS_FLAG_MULTIVIEW;
// Try enabling here in case is_xr_enabled() returns false.
scene_shader.shader.enable_group(SceneShaderForwardClustered::SHADER_GROUP_MULTIVIEW);
+
+ // Indicate pipelines for multiview are required.
+ global_pipeline_data_required.use_multiview = true;
}
color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags);
@@ -1795,6 +1865,26 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
scene_shader.enable_advanced_shader_group(p_render_data->scene_data->view_count > 1);
}
+ // Update the global pipeline requirements with all the features found to be in use in this scene.
+ if (depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS) {
+ global_pipeline_data_required.use_normal_and_roughness = true;
+ }
+
+ if (scene_state.used_lightmap) {
+ global_pipeline_data_required.use_lightmaps = true;
+ }
+
+ if (using_voxelgi) {
+ global_pipeline_data_required.use_voxelgi = true;
+ }
+
+ if (using_separate_specular) {
+ global_pipeline_data_required.use_separate_specular = true;
+ }
+
+ // Update the compiled pipelines if any of the requirements have changed.
+ _update_dirty_geometry_pipelines();
+
RID radiance_texture;
bool draw_sky = false;
bool draw_sky_fog_only = false;
@@ -1911,12 +2001,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
bool debug_sdfgi_probes = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SDFGI_PROBES;
bool depth_pre_pass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable")) && depth_framebuffer.is_valid();
- uint32_t spec_constant_base_flags = 0;
- {
- if (p_render_data->environment.is_valid() && environment_get_fog_mode(p_render_data->environment) == RS::EnvironmentFogMode::ENV_FOG_MODE_DEPTH) {
- spec_constant_base_flags |= 1 << SPEC_CONSTANT_USE_DEPTH_FOG;
- }
- }
+ SceneShaderForwardClustered::ShaderSpecialization base_specialization = scene_shader.default_specialization;
+ base_specialization.use_depth_fog = p_render_data->environment.is_valid() && environment_get_fog_mode(p_render_data->environment) == RS::EnvironmentFogMode::ENV_FOG_MODE_DEPTH;
bool using_ssao = depth_pre_pass && !is_reflection_probe && p_render_data->environment.is_valid() && environment_get_ssao_enabled(p_render_data->environment);
@@ -1940,7 +2026,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, nullptr, RID(), samplers);
bool finish_depth = using_ssao || using_ssil || using_sdfgi || using_voxelgi || ce_pre_opaque_resolved_depth || ce_post_opaque_resolved_depth;
- RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, depth_pass_mode, 0, 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_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, depth_pass_mode, 0, 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, base_specialization);
_render_list_with_draw_list(&render_list_params, depth_framebuffer, needs_pre_resolve ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, needs_pre_resolve ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, needs_pre_resolve ? Vector<Color>() : depth_pass_clear);
RD::get_singleton()->draw_command_end_label();
@@ -2019,7 +2105,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
uint32_t opaque_color_pass_flags = using_motion_pass ? (color_pass_flags & ~COLOR_PASS_FLAG_MOTION_VECTORS) : color_pass_flags;
RID opaque_framebuffer = using_motion_pass ? rb_data->get_color_pass_fb(opaque_color_pass_flags) : color_framebuffer;
- RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, opaque_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_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, opaque_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, base_specialization);
_render_list_with_draw_list(&render_list_params, opaque_framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, depth_pre_pass ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 0.0, 0);
}
@@ -2039,7 +2125,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_MOTION, p_render_data, radiance_texture, samplers, true);
- RenderListParameters render_list_params(render_list[RENDER_LIST_MOTION].elements.ptr(), render_list[RENDER_LIST_MOTION].element_info.ptr(), render_list[RENDER_LIST_MOTION].elements.size(), reverse_cull, PASS_MODE_COLOR, 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_MOTION].elements.ptr(), render_list[RENDER_LIST_MOTION].element_info.ptr(), render_list[RENDER_LIST_MOTION].elements.size(), reverse_cull, PASS_MODE_COLOR, 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, base_specialization);
_render_list_with_draw_list(&render_list_params, color_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE);
RD::get_singleton()->draw_command_end_label();
@@ -2218,7 +2304,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(), 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);
+ 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, base_specialization);
_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);
}
@@ -2841,6 +2927,9 @@ void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_bu
_update_render_base_uniform_set();
+ // Indicate pipelines for SDFGI are required.
+ global_pipeline_data_required.use_sdfgi = true;
+
PassMode pass_mode = PASS_MODE_SDF;
_fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode);
render_list[RENDER_LIST_SECONDARY].sort_by_key();
@@ -3734,14 +3823,23 @@ void RenderForwardClustered::GeometryInstanceForwardClustered::_mark_dirty() {
RenderForwardClustered::get_singleton()->geometry_instance_dirty_list.add(&dirty_list_element);
}
-void RenderForwardClustered::_geometry_instance_add_surface_with_material(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, SceneShaderForwardClustered::MaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh) {
- RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
+void RenderForwardClustered::_update_global_pipeline_data_requirements_from_project() {
+ const int msaa_3d_mode = GLOBAL_GET("rendering/anti_aliasing/quality/msaa_3d");
+ const bool directional_shadow_16_bits = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/16_bits");
+ const bool positional_shadow_16_bits = GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/atlas_16_bits");
+ global_pipeline_data_required.use_16_bit_shadows = directional_shadow_16_bits || positional_shadow_16_bits;
+ global_pipeline_data_required.use_32_bit_shadows = !directional_shadow_16_bits || !positional_shadow_16_bits;
+ global_pipeline_data_required.texture_samples = RenderSceneBuffersRD::msaa_to_samples(RS::ViewportMSAA(msaa_3d_mode));
+}
- bool has_read_screen_alpha = p_material->shader_data->uses_screen_texture || p_material->shader_data->uses_depth_texture || p_material->shader_data->uses_normal_texture;
- bool has_base_alpha = (p_material->shader_data->uses_alpha && (!p_material->shader_data->uses_alpha_clip || p_material->shader_data->uses_alpha_antialiasing)) || has_read_screen_alpha;
- bool has_blend_alpha = p_material->shader_data->uses_blend_alpha;
- bool has_alpha = has_base_alpha || has_blend_alpha;
+void RenderForwardClustered::_update_global_pipeline_data_requirements_from_light_storage() {
+ RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
+ global_pipeline_data_required.use_shadow_cubemaps = light_storage->get_shadow_cubemaps_used();
+ global_pipeline_data_required.use_shadow_dual_paraboloid = light_storage->get_shadow_dual_paraboloid_used();
+}
+void RenderForwardClustered::_geometry_instance_add_surface_with_material(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, SceneShaderForwardClustered::MaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh) {
+ RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
uint32_t flags = 0;
if (p_material->shader_data->uses_sss) {
@@ -3764,10 +3862,9 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet
flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_DOUBLE_SIDED_SHADOWS;
}
- if (has_alpha || has_read_screen_alpha || p_material->shader_data->depth_draw == SceneShaderForwardClustered::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardClustered::ShaderData::DEPTH_TEST_DISABLED) {
- //material is only meant for alpha pass
+ if (p_material->shader_data->uses_alpha_pass()) {
flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA;
- if ((p_material->shader_data->uses_depth_prepass_alpha || p_material->shader_data->uses_alpha_antialiasing) && !(p_material->shader_data->depth_draw == SceneShaderForwardClustered::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardClustered::ShaderData::DEPTH_TEST_DISABLED)) {
+ if (p_material->shader_data->uses_depth_in_alpha_pass()) {
flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH;
flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW;
}
@@ -3787,16 +3884,14 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet
SceneShaderForwardClustered::MaterialData *material_shadow = nullptr;
void *surface_shadow = nullptr;
- if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_position && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_alpha_antialiasing && p_material->shader_data->cull_mode == SceneShaderForwardClustered::ShaderData::CULL_BACK && !p_material->shader_data->uses_point_size && !p_material->shader_data->uses_world_coordinates) {
+ if (p_material->shader_data->uses_shared_shadow_material()) {
flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_SHARED_SHADOW_MATERIAL;
material_shadow = static_cast<SceneShaderForwardClustered::MaterialData *>(RendererRD::MaterialStorage::get_singleton()->material_get_data(scene_shader.default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
RID shadow_mesh = mesh_storage->mesh_get_shadow_mesh(p_mesh);
-
if (shadow_mesh.is_valid()) {
surface_shadow = mesh_storage->mesh_get_surface(shadow_mesh, p_surface);
}
-
} else {
material_shadow = p_material;
}
@@ -3848,6 +3943,16 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet
String mesh_path = mesh_storage->mesh_get_path(p_mesh).is_empty() ? "" : "(" + mesh_storage->mesh_get_path(p_mesh) + ")";
WARN_PRINT_ED(vformat("Attempting to use a shader %s that requires tangents with a mesh %s that doesn't contain tangents. Ensure that meshes are imported with the 'ensure_tangents' option. If creating your own meshes, add an `ARRAY_TANGENT` array (when using ArrayMesh) or call `generate_tangents()` (when using SurfaceTool).", shader_path, mesh_path));
}
+
+#if PRELOAD_PIPELINES_ON_SURFACE_CACHE_CONSTRUCTION
+ if (!sdcache->compilation_dirty_element.in_list()) {
+ geometry_surface_compilation_dirty_list.add(&sdcache->compilation_dirty_element);
+ }
+
+ if (!sdcache->compilation_all_element.in_list()) {
+ geometry_surface_compilation_all_list.add(&sdcache->compilation_all_element);
+ }
+#endif
}
void RenderForwardClustered::_geometry_instance_add_surface_with_material_chain(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, SceneShaderForwardClustered::MaterialData *p_material, RID p_mat_src, RID p_mesh) {
@@ -3859,7 +3964,7 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material_chain(
while (material->next_pass.is_valid()) {
RID next_pass = material->next_pass;
material = static_cast<SceneShaderForwardClustered::MaterialData *>(material_storage->material_get_data(next_pass, RendererRD::MaterialStorage::SHADER_TYPE_3D));
- if (!material || !material->shader_data->valid) {
+ if (!material || !material->shader_data->is_valid()) {
break;
}
if (ginstance->data->dirty_dependencies) {
@@ -3879,7 +3984,7 @@ void RenderForwardClustered::_geometry_instance_add_surface(GeometryInstanceForw
if (m_src.is_valid()) {
material = static_cast<SceneShaderForwardClustered::MaterialData *>(material_storage->material_get_data(m_src, RendererRD::MaterialStorage::SHADER_TYPE_3D));
- if (!material || !material->shader_data->valid) {
+ if (!material || !material->shader_data->is_valid()) {
material = nullptr;
}
}
@@ -3901,7 +4006,7 @@ void RenderForwardClustered::_geometry_instance_add_surface(GeometryInstanceForw
m_src = ginstance->data->material_overlay;
material = static_cast<SceneShaderForwardClustered::MaterialData *>(material_storage->material_get_data(m_src, RendererRD::MaterialStorage::SHADER_TYPE_3D));
- if (material && material->shader_data->valid) {
+ if (material && material->shader_data->is_valid()) {
if (ginstance->data->dirty_dependencies) {
material_storage->material_update_dependency(m_src, &ginstance->data->dependency_tracker);
}
@@ -4068,10 +4173,361 @@ void RenderForwardClustered::_geometry_instance_update(RenderGeometryInstance *p
ginstance->dirty_list_element.remove_from_list();
}
+static RD::FramebufferFormatID _get_color_framebuffer_format_for_pipeline(RD::DataFormat p_color_format, bool p_can_be_storage, RD::TextureSamples p_samples, bool p_specular, bool p_velocity, uint32_t p_view_count) {
+ const bool multisampling = p_samples > RD::TEXTURE_SAMPLES_1;
+ RD::AttachmentFormat attachment;
+ attachment.samples = p_samples;
+
+ RD::AttachmentFormat unused_attachment;
+ unused_attachment.usage_flags = RD::AttachmentFormat::UNUSED_ATTACHMENT;
+
+ thread_local Vector<RD::AttachmentFormat> attachments;
+ attachments.clear();
+
+ // Color attachment.
+ attachment.format = p_color_format;
+ attachment.usage_flags = RenderSceneBuffersRD::get_color_usage_bits(false, multisampling, p_can_be_storage);
+ attachments.push_back(attachment);
+
+ if (p_specular) {
+ attachment.format = RenderForwardClustered::RenderBufferDataForwardClustered::get_specular_format();
+ attachment.usage_flags = RenderForwardClustered::RenderBufferDataForwardClustered::get_specular_usage_bits(false, multisampling, p_can_be_storage);
+ attachments.push_back(attachment);
+ } else {
+ attachments.push_back(unused_attachment);
+ }
+
+ if (p_velocity) {
+ attachment.format = RenderSceneBuffersRD::get_velocity_format();
+ attachment.usage_flags = RenderSceneBuffersRD::get_velocity_usage_bits(false, multisampling, p_can_be_storage);
+ attachments.push_back(attachment);
+ } else {
+ attachments.push_back(unused_attachment);
+ }
+
+ // Depth attachment.
+ attachment.format = RenderSceneBuffersRD::get_depth_format(false, multisampling, p_can_be_storage);
+ attachment.usage_flags = RenderSceneBuffersRD::get_depth_usage_bits(false, multisampling, p_can_be_storage);
+ attachments.push_back(attachment);
+
+ thread_local Vector<RD::FramebufferPass> passes;
+ passes.resize(1);
+ passes.ptrw()[0].color_attachments.resize(attachments.size() - 1);
+
+ int *color_attachments = passes.ptrw()[0].color_attachments.ptrw();
+ for (int64_t i = 0; i < attachments.size() - 1; i++) {
+ color_attachments[i] = (attachments[i].usage_flags == RD::AttachmentFormat::UNUSED_ATTACHMENT) ? RD::ATTACHMENT_UNUSED : i;
+ }
+
+ passes.ptrw()[0].depth_attachment = attachments.size() - 1;
+
+ return RD::get_singleton()->framebuffer_format_create_multipass(attachments, passes, p_view_count);
+}
+
+static RD::FramebufferFormatID _get_reflection_probe_color_framebuffer_format_for_pipeline() {
+ RD::AttachmentFormat attachment;
+ thread_local Vector<RD::AttachmentFormat> attachments;
+ attachments.clear();
+
+ attachment.format = RendererRD::LightStorage::get_reflection_probe_color_format();
+ attachment.usage_flags = RendererRD::LightStorage::get_reflection_probe_color_usage_bits();
+ attachments.push_back(attachment);
+
+ attachment.format = RendererRD::LightStorage::get_reflection_probe_depth_format();
+ attachment.usage_flags = RendererRD::LightStorage::get_reflection_probe_depth_usage_bits();
+ attachments.push_back(attachment);
+
+ return RD::get_singleton()->framebuffer_format_create(attachments);
+}
+
+static RD::FramebufferFormatID _get_depth_framebuffer_format_for_pipeline(bool p_can_be_storage, RD::TextureSamples p_samples, bool p_normal_roughness, bool p_voxelgi) {
+ const bool multisampling = p_samples > RD::TEXTURE_SAMPLES_1;
+ RD::AttachmentFormat attachment;
+ attachment.samples = p_samples;
+
+ thread_local LocalVector<RD::AttachmentFormat> attachments;
+ attachments.clear();
+
+ attachment.format = RenderSceneBuffersRD::get_depth_format(false, multisampling, p_can_be_storage);
+ attachment.usage_flags = RenderSceneBuffersRD::get_depth_usage_bits(false, multisampling, p_can_be_storage);
+ attachments.push_back(attachment);
+
+ if (p_normal_roughness) {
+ attachment.format = RenderForwardClustered::RenderBufferDataForwardClustered::get_normal_roughness_format();
+ attachment.usage_flags = RenderForwardClustered::RenderBufferDataForwardClustered::get_normal_roughness_usage_bits(false, multisampling, p_can_be_storage);
+ attachments.push_back(attachment);
+ }
+
+ if (p_voxelgi) {
+ attachment.format = RenderForwardClustered::RenderBufferDataForwardClustered::get_voxelgi_format();
+ attachment.usage_flags = RenderForwardClustered::RenderBufferDataForwardClustered::get_voxelgi_usage_bits(false, multisampling, p_can_be_storage);
+ attachments.push_back(attachment);
+ }
+
+ thread_local Vector<RD::FramebufferPass> passes;
+ passes.resize(1);
+ passes.ptrw()[0].color_attachments.resize(attachments.size() - 1);
+
+ int *color_attachments = passes.ptrw()[0].color_attachments.ptrw();
+ for (int64_t i = 1; i < attachments.size(); i++) {
+ color_attachments[i - 1] = (attachments[i].usage_flags == RD::AttachmentFormat::UNUSED_ATTACHMENT) ? RD::ATTACHMENT_UNUSED : i;
+ }
+
+ passes.ptrw()[0].depth_attachment = 0;
+
+ return RD::get_singleton()->framebuffer_format_create_multipass(attachments, passes);
+}
+
+static RD::FramebufferFormatID _get_shadow_cubemap_framebuffer_format_for_pipeline() {
+ thread_local LocalVector<RD::AttachmentFormat> attachments;
+ attachments.clear();
+
+ RD::AttachmentFormat attachment;
+ attachment.format = RendererRD::LightStorage::get_cubemap_depth_format();
+ attachment.usage_flags = RendererRD::LightStorage::get_cubemap_depth_usage_bits();
+ attachments.push_back(attachment);
+
+ return RD::get_singleton()->framebuffer_format_create(attachments);
+}
+
+static RD::FramebufferFormatID _get_shadow_atlas_framebuffer_format_for_pipeline(bool p_use_16_bits) {
+ thread_local LocalVector<RD::AttachmentFormat> attachments;
+ attachments.clear();
+
+ RD::AttachmentFormat attachment;
+ attachment.format = RendererRD::LightStorage::get_shadow_atlas_depth_format(p_use_16_bits);
+ attachment.usage_flags = RendererRD::LightStorage::get_shadow_atlas_depth_usage_bits();
+ attachments.push_back(attachment);
+
+ return RD::get_singleton()->framebuffer_format_create(attachments);
+}
+
+static RD::FramebufferFormatID _get_reflection_probe_depth_framebuffer_format_for_pipeline() {
+ thread_local LocalVector<RD::AttachmentFormat> attachments;
+ attachments.clear();
+
+ RD::AttachmentFormat attachment;
+ attachment.format = RendererRD::LightStorage::get_reflection_probe_depth_format();
+ attachment.usage_flags = RendererRD::LightStorage::get_reflection_probe_depth_usage_bits();
+ attachments.push_back(attachment);
+
+ return RD::get_singleton()->framebuffer_format_create(attachments);
+}
+
+void RenderForwardClustered::_mesh_compile_pipeline_for_surface(SceneShaderForwardClustered::ShaderData *p_shader, void *p_mesh_surface, bool p_ubershader, bool p_instanced_surface, RS::PipelineSource p_source, SceneShaderForwardClustered::ShaderData::PipelineKey &r_pipeline_key, Vector<ShaderPipelinePair> *r_pipeline_pairs) {
+ RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
+ uint64_t input_mask = p_shader->get_vertex_input_mask(r_pipeline_key.version, r_pipeline_key.color_pass_flags, p_ubershader);
+ bool pipeline_motion_vectors = r_pipeline_key.color_pass_flags & SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS;
+ r_pipeline_key.vertex_format_id = mesh_storage->mesh_surface_get_vertex_format(p_mesh_surface, input_mask, p_instanced_surface, pipeline_motion_vectors);
+ r_pipeline_key.ubershader = p_ubershader;
+
+ p_shader->pipeline_hash_map.compile_pipeline(r_pipeline_key, r_pipeline_key.hash(), p_source);
+
+ if (r_pipeline_pairs != nullptr) {
+ r_pipeline_pairs->push_back({ p_shader, r_pipeline_key });
+ }
+}
+
+void RenderForwardClustered::_mesh_compile_pipelines_for_surface(const SurfacePipelineData &p_surface, const GlobalPipelineData &p_global, RS::PipelineSource p_source, Vector<ShaderPipelinePair> *r_pipeline_pairs) {
+ RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
+
+ // Retrieve from the scene shader which groups are currently enabled.
+ const bool multiview_enabled = p_global.use_multiview && scene_shader.is_multiview_shader_group_enabled();
+ const RD::DataFormat buffers_color_format = _render_buffers_get_color_format();
+ const bool buffers_can_be_storage = _render_buffers_can_be_storage();
+
+ // Set the attributes common to all pipelines.
+ SceneShaderForwardClustered::ShaderData::PipelineKey pipeline_key;
+ pipeline_key.cull_mode = RD::POLYGON_CULL_DISABLED;
+ pipeline_key.primitive_type = mesh_storage->mesh_surface_get_primitive(p_surface.mesh_surface);
+ pipeline_key.wireframe = false;
+
+ // Grab the shader and surface used for most passes.
+ const uint32_t multiview_iterations = multiview_enabled ? 2 : 1;
+ const uint32_t lightmap_iterations = p_global.use_lightmaps && p_surface.can_use_lightmap ? 2 : 1;
+ const uint32_t alpha_iterations = p_surface.uses_transparent ? 2 : 1;
+ for (uint32_t multiview = 0; multiview < multiview_iterations; multiview++) {
+ for (uint32_t lightmap = 0; lightmap < lightmap_iterations; lightmap++) {
+ for (uint32_t alpha = p_surface.uses_opaque ? 0 : 1; alpha < alpha_iterations; alpha++) {
+ // Generate all the possible variants used during the color pass.
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_COLOR_PASS;
+ pipeline_key.color_pass_flags = 0;
+
+ if (lightmap) {
+ pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_LIGHTMAP;
+ }
+
+ if (alpha) {
+ pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_TRANSPARENT;
+ }
+
+ if (multiview) {
+ pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MULTIVIEW;
+ } else if (p_global.use_reflection_probes) {
+ // Reflection probe can't be rendered in multiview.
+ pipeline_key.framebuffer_format_id = _get_reflection_probe_color_framebuffer_format_for_pipeline();
+ _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+
+ // View count is assumed to be 2 as the configuration is dependent on the viewport. It's likely a safe assumption for stereo rendering.
+ uint32_t view_count = multiview ? 2 : 1;
+ pipeline_key.framebuffer_format_id = _get_color_framebuffer_format_for_pipeline(buffers_color_format, buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), false, false, view_count);
+ _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+
+ // Generate all the possible variants used during the advanced color passes.
+ const uint32_t separate_specular_iterations = p_global.use_separate_specular ? 2 : 1;
+ const uint32_t motion_vectors_iterations = p_global.use_motion_vectors ? 2 : 1;
+ uint32_t base_color_pass_flags = pipeline_key.color_pass_flags;
+ for (uint32_t separate_specular = 0; separate_specular < separate_specular_iterations; separate_specular++) {
+ for (uint32_t motion_vectors = 0; motion_vectors < motion_vectors_iterations; motion_vectors++) {
+ if (!separate_specular && !motion_vectors) {
+ // This case was already generated.
+ continue;
+ }
+
+ pipeline_key.color_pass_flags = base_color_pass_flags;
+
+ if (separate_specular) {
+ pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR;
+ }
+
+ if (motion_vectors) {
+ pipeline_key.color_pass_flags |= SceneShaderForwardClustered::PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS;
+ }
+
+ pipeline_key.framebuffer_format_id = _get_color_framebuffer_format_for_pipeline(buffers_color_format, buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), separate_specular, motion_vectors, view_count);
+ _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+ }
+ }
+ }
+ }
+
+ if (!p_surface.uses_depth) {
+ return;
+ }
+
+ // Generate the depth pipelines if the material supports depth or it must be part of the shadow pass.
+ pipeline_key.color_pass_flags = 0;
+
+ if (p_global.use_normal_and_roughness) {
+ // A lot of different effects rely on normal and roughness being written to during the depth pass.
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS;
+ pipeline_key.framebuffer_format_id = _get_depth_framebuffer_format_for_pipeline(buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), true, false);
+ _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+
+ if (p_global.use_voxelgi) {
+ // Depth pass with VoxelGI support.
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI;
+ pipeline_key.framebuffer_format_id = _get_depth_framebuffer_format_for_pipeline(buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), true, true);
+ _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+
+ if (p_global.use_sdfgi) {
+ // Depth pass with SDFGI support.
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_SDF;
+ pipeline_key.framebuffer_format_id = _get_depth_framebuffer_format_for_pipeline(buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), false, false);
+ _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, false, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+
+ // Depth pass with SDFGI support for an empty framebuffer.
+ pipeline_key.framebuffer_format_id = RD::get_singleton()->framebuffer_format_create_empty();
+ _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, false, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+
+ // The dedicated depth passes use a different version of the surface and the shader.
+ pipeline_key.primitive_type = mesh_storage->mesh_surface_get_primitive(p_surface.mesh_surface_shadow);
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS;
+ pipeline_key.framebuffer_format_id = _get_depth_framebuffer_format_for_pipeline(buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), false, false);
+ _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+
+ if (p_global.use_shadow_dual_paraboloid) {
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_DP;
+ _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+
+ if (p_global.use_shadow_cubemaps) {
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS;
+ pipeline_key.framebuffer_format_id = _get_shadow_cubemap_framebuffer_format_for_pipeline();
+ _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+
+ // Atlas shadowmaps (omni lights) can be in both 16-bit and 32-bit versions.
+ const uint32_t use_16_bits_start = p_global.use_32_bit_shadows ? 0 : 1;
+ const uint32_t use_16_bits_iterations = p_global.use_16_bit_shadows ? 2 : 1;
+ for (uint32_t use_16_bits = use_16_bits_start; use_16_bits < use_16_bits_iterations; use_16_bits++) {
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS;
+ pipeline_key.framebuffer_format_id = _get_shadow_atlas_framebuffer_format_for_pipeline(use_16_bits);
+ _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+
+ if (p_global.use_shadow_dual_paraboloid) {
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_DP;
+ _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+ }
+
+ if (p_global.use_reflection_probes) {
+ // Depth pass for reflection probes. Normally this will be redundant as the format is the exact same as the shadow cubemap.
+ pipeline_key.version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS;
+ pipeline_key.framebuffer_format_id = _get_reflection_probe_depth_framebuffer_format_for_pipeline();
+ _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, true, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+}
+
+void RenderForwardClustered::_mesh_generate_all_pipelines_for_surface_cache(GeometryInstanceSurfaceDataCache *p_surface_cache, const GlobalPipelineData &p_global) {
+ bool uses_alpha_pass = (p_surface_cache->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA) != 0;
+ float multiplied_fade_alpha = p_surface_cache->owner->force_alpha * p_surface_cache->owner->parent_fade_alpha;
+ bool uses_fade = (multiplied_fade_alpha < FADE_ALPHA_PASS_THRESHOLD) || p_surface_cache->owner->fade_near || p_surface_cache->owner->fade_far;
+ SurfacePipelineData surface;
+ surface.mesh_surface = p_surface_cache->surface;
+ surface.mesh_surface_shadow = p_surface_cache->surface_shadow;
+ surface.shader = p_surface_cache->shader;
+ surface.shader_shadow = p_surface_cache->shader_shadow;
+ surface.instanced = p_surface_cache->owner->mesh_instance.is_valid();
+ surface.uses_opaque = !uses_alpha_pass;
+ surface.uses_transparent = uses_alpha_pass || uses_fade;
+ surface.uses_depth = (p_surface_cache->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE | GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW)) != 0;
+ surface.can_use_lightmap = p_surface_cache->owner->lightmap_instance.is_valid() || p_surface_cache->owner->lightmap_sh;
+ _mesh_compile_pipelines_for_surface(surface, p_global, RS::PIPELINE_SOURCE_SURFACE);
+}
+
void RenderForwardClustered::_update_dirty_geometry_instances() {
while (geometry_instance_dirty_list.first()) {
_geometry_instance_update(geometry_instance_dirty_list.first()->self());
}
+
+ _update_dirty_geometry_pipelines();
+}
+
+void RenderForwardClustered::_update_dirty_geometry_pipelines() {
+ if (global_pipeline_data_required.key != global_pipeline_data_compiled.key) {
+ // Go through the entire list of surfaces and compile pipelines for everything again.
+ SelfList<GeometryInstanceSurfaceDataCache> *list = geometry_surface_compilation_all_list.first();
+ while (list != nullptr) {
+ GeometryInstanceSurfaceDataCache *surface_cache = list->self();
+ _mesh_generate_all_pipelines_for_surface_cache(surface_cache, global_pipeline_data_required);
+
+ if (surface_cache->compilation_dirty_element.in_list()) {
+ // Remove any elements from the dirty list as they don't need to be processed again.
+ geometry_surface_compilation_dirty_list.remove(&surface_cache->compilation_dirty_element);
+ }
+
+ list = list->next();
+ }
+
+ global_pipeline_data_compiled.key = global_pipeline_data_required.key;
+ } else {
+ // Compile pipelines only for the dirty list.
+ if (!geometry_surface_compilation_dirty_list.first()) {
+ return;
+ }
+
+ while (geometry_surface_compilation_dirty_list.first() != nullptr) {
+ GeometryInstanceSurfaceDataCache *surface_cache = geometry_surface_compilation_dirty_list.first()->self();
+ _mesh_generate_all_pipelines_for_surface_cache(surface_cache, global_pipeline_data_compiled);
+ surface_cache->compilation_dirty_element.remove_from_list();
+ }
+ }
}
void RenderForwardClustered::_geometry_instance_dependency_changed(Dependency::DependencyChangedNotification p_notification, DependencyTracker *p_tracker) {
@@ -4174,6 +4630,66 @@ uint32_t RenderForwardClustered::geometry_instance_get_pair_mask() {
return (1 << RS::INSTANCE_VOXEL_GI);
}
+void RenderForwardClustered::mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) {
+ RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
+ RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
+ RID shadow_mesh = mesh_storage->mesh_get_shadow_mesh(p_mesh);
+ uint32_t surface_count = 0;
+ const RID *materials = mesh_storage->mesh_get_surface_count_and_materials(p_mesh, surface_count);
+ Vector<ShaderPipelinePair> pipeline_pairs;
+ for (uint32_t i = 0; i < surface_count; i++) {
+ if (materials[i].is_null()) {
+ continue;
+ }
+
+ void *mesh_surface = mesh_storage->mesh_get_surface(p_mesh, i);
+ void *mesh_surface_shadow = mesh_surface;
+ SceneShaderForwardClustered::MaterialData *material = static_cast<SceneShaderForwardClustered::MaterialData *>(material_storage->material_get_data(materials[i], RendererRD::MaterialStorage::SHADER_TYPE_3D));
+ if (material == nullptr) {
+ continue;
+ }
+
+ SceneShaderForwardClustered::ShaderData *shader = material->shader_data;
+ SceneShaderForwardClustered::ShaderData *shader_shadow = shader;
+ if (material->shader_data->uses_shared_shadow_material()) {
+ SceneShaderForwardClustered::MaterialData *material_shadow = static_cast<SceneShaderForwardClustered::MaterialData *>(material_storage->material_get_data(scene_shader.default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
+ if (material_shadow != nullptr) {
+ shader_shadow = material_shadow->shader_data;
+ if (shadow_mesh.is_valid()) {
+ mesh_surface_shadow = mesh_storage->mesh_get_surface(shadow_mesh, i);
+ }
+ }
+ }
+
+ if (!shader->is_valid()) {
+ continue;
+ }
+
+ SurfacePipelineData surface;
+ surface.mesh_surface = mesh_surface;
+ surface.mesh_surface_shadow = mesh_surface_shadow;
+ surface.shader = shader;
+ surface.shader_shadow = shader_shadow;
+ surface.instanced = mesh_storage->mesh_needs_instance(p_mesh, true);
+ surface.uses_opaque = !material->shader_data->uses_alpha_pass();
+ surface.uses_transparent = material->shader_data->uses_alpha_pass();
+ surface.uses_depth = surface.uses_opaque || (surface.uses_transparent && material->shader_data->uses_depth_in_alpha_pass());
+ surface.can_use_lightmap = mesh_storage->mesh_surface_get_format(mesh_surface) & RS::ARRAY_FORMAT_TEX_UV2;
+ _mesh_compile_pipelines_for_surface(surface, global_pipeline_data_required, RS::PIPELINE_SOURCE_MESH, &pipeline_pairs);
+ }
+
+ // Try to retrieve all the pipeline pairs that were compiled. This will force the loader to wait on all ubershader pipelines to be ready.
+ if (!p_background_compilation && !pipeline_pairs.is_empty()) {
+ for (ShaderPipelinePair pair : pipeline_pairs) {
+ pair.first->pipeline_hash_map.get_pipeline(pair.second, pair.second.hash(), true, RS::PIPELINE_SOURCE_MESH);
+ }
+ }
+}
+
+uint32_t RenderForwardClustered::get_pipeline_compilations(RS::PipelineSource p_source) {
+ return scene_shader.get_pipeline_compilations(p_source);
+}
+
void RenderForwardClustered::GeometryInstanceForwardClustered::pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) {
if (p_voxel_gi_instance_count > 0) {
voxel_gi_instances[0] = p_voxel_gi_instances[0];
@@ -4195,54 +4711,23 @@ void RenderForwardClustered::GeometryInstanceForwardClustered::set_softshadow_pr
}
void RenderForwardClustered::_update_shader_quality_settings() {
- Vector<RD::PipelineSpecializationConstant> spec_constants;
-
- RD::PipelineSpecializationConstant sc;
- sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT;
-
- sc.constant_id = SPEC_CONSTANT_SOFT_SHADOW_SAMPLES;
- sc.int_value = soft_shadow_samples_get();
-
- spec_constants.push_back(sc);
-
- sc.constant_id = SPEC_CONSTANT_PENUMBRA_SHADOW_SAMPLES;
- sc.int_value = penumbra_shadow_samples_get();
-
- spec_constants.push_back(sc);
-
- sc.constant_id = SPEC_CONSTANT_DIRECTIONAL_SOFT_SHADOW_SAMPLES;
- sc.int_value = directional_soft_shadow_samples_get();
-
- spec_constants.push_back(sc);
-
- sc.constant_id = SPEC_CONSTANT_DIRECTIONAL_PENUMBRA_SHADOW_SAMPLES;
- sc.int_value = directional_penumbra_shadow_samples_get();
-
- spec_constants.push_back(sc);
-
- sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL;
- sc.constant_id = SPEC_CONSTANT_DECAL_FILTER;
- sc.bool_value = decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS ||
+ SceneShaderForwardClustered::ShaderSpecialization specialization = {};
+ specialization.decal_use_mipmaps = decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS ||
decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS ||
decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC ||
decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC;
-
- spec_constants.push_back(sc);
-
- sc.constant_id = SPEC_CONSTANT_PROJECTOR_FILTER;
- sc.bool_value = light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS ||
+ ;
+ specialization.projector_use_mipmaps = light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS ||
light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS ||
light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC ||
light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC;
- 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);
+ specialization.soft_shadow_samples = soft_shadow_samples_get();
+ specialization.penumbra_shadow_samples = penumbra_shadow_samples_get();
+ specialization.directional_soft_shadow_samples = directional_soft_shadow_samples_get();
+ specialization.directional_penumbra_shadow_samples = directional_penumbra_shadow_samples_get();
+ specialization.use_lightmap_bicubic_filter = lightmap_filter_bicubic_get();
+ scene_shader.set_default_specialization(specialization);
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 5d14653db6..ebc291e803 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
@@ -66,17 +66,6 @@ class RenderForwardClustered : public RendererSceneRenderRD {
};
enum {
- SPEC_CONSTANT_SOFT_SHADOW_SAMPLES = 6,
- SPEC_CONSTANT_PENUMBRA_SHADOW_SAMPLES = 7,
- SPEC_CONSTANT_DIRECTIONAL_SOFT_SHADOW_SAMPLES = 8,
- SPEC_CONSTANT_DIRECTIONAL_PENUMBRA_SHADOW_SAMPLES = 9,
- SPEC_CONSTANT_DECAL_FILTER = 10,
- SPEC_CONSTANT_PROJECTOR_FILTER = 11,
- SPEC_CONSTANT_USE_DEPTH_FOG = 12,
- SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER = 13,
- };
-
- enum {
SDFGI_MAX_CASCADES = 8,
MAX_VOXEL_GI_INSTANCESS = 8,
MAX_LIGHTMAPS = 8,
@@ -96,6 +85,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
SceneShaderForwardClustered scene_shader;
+public:
/* Framebuffer */
class RenderBufferDataForwardClustered : public RenderBufferCustomDataRD {
@@ -155,8 +145,16 @@ class RenderForwardClustered : public RendererSceneRenderRD {
virtual void configure(RenderSceneBuffersRD *p_render_buffers) override;
virtual void free_data() override;
+
+ static RD::DataFormat get_specular_format();
+ static uint32_t get_specular_usage_bits(bool p_resolve, bool p_msaa, bool p_storage);
+ static RD::DataFormat get_normal_roughness_format();
+ static uint32_t get_normal_roughness_usage_bits(bool p_resolve, bool p_msaa, bool p_storage);
+ static RD::DataFormat get_voxelgi_format();
+ static uint32_t get_voxelgi_usage_bits(bool p_resolve, bool p_msaa, bool p_storage);
};
+private:
virtual void setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_render_buffers) override;
RID render_base_uniform_set;
@@ -183,6 +181,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI,
PASS_MODE_DEPTH_MATERIAL,
PASS_MODE_SDF,
+ PASS_MODE_MAX
};
enum ColorPassFlags {
@@ -212,9 +211,9 @@ class RenderForwardClustered : public RendererSceneRenderRD {
RD::FramebufferFormatID framebuffer_format = 0;
uint32_t element_offset = 0;
bool use_directional_soft_shadow = false;
- uint32_t spec_constant_base_flags = 0;
+ SceneShaderForwardClustered::ShaderSpecialization base_specialization = {};
- RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, uint32_t p_color_pass_flags, bool p_no_gi, bool p_use_directional_soft_shadows, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_spec_constant_base_flags = 0) {
+ RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, uint32_t p_color_pass_flags, bool p_no_gi, bool p_use_directional_soft_shadows, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, SceneShaderForwardClustered::ShaderSpecialization p_base_specialization = {}) {
elements = p_elements;
element_info = p_element_info;
element_count = p_element_count;
@@ -230,7 +229,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
screen_mesh_lod_threshold = p_screen_mesh_lod_threshold;
element_offset = p_element_offset;
use_directional_soft_shadow = p_use_directional_soft_shadows;
- spec_constant_base_flags = p_spec_constant_base_flags;
+ base_specialization = p_base_specialization;
}
};
@@ -293,11 +292,17 @@ class RenderForwardClustered : public RendererSceneRenderRD {
uint32_t volumetric_fog_pad;
};
+ struct PushConstantUbershader {
+ SceneShaderForwardClustered::ShaderSpecialization specialization;
+ SceneShaderForwardClustered::UbershaderConstants constants;
+ };
+
struct PushConstant {
uint32_t base_index; //
uint32_t uv_offset; //packed
uint32_t multimesh_motion_vectors_current_offset;
uint32_t multimesh_motion_vectors_previous_offset;
+ PushConstantUbershader ubershader;
};
struct InstanceData {
@@ -450,6 +455,11 @@ class RenderForwardClustered : public RendererSceneRenderRD {
GeometryInstanceSurfaceDataCache *next = nullptr;
GeometryInstanceForwardClustered *owner = nullptr;
+ SelfList<GeometryInstanceSurfaceDataCache> compilation_dirty_element;
+ SelfList<GeometryInstanceSurfaceDataCache> compilation_all_element;
+
+ GeometryInstanceSurfaceDataCache() :
+ compilation_dirty_element(this), compilation_all_element(this) {}
};
class GeometryInstanceForwardClustered : public RenderGeometryInstanceBase {
@@ -500,16 +510,63 @@ class RenderForwardClustered : public RendererSceneRenderRD {
static void _geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker);
SelfList<GeometryInstanceForwardClustered>::List geometry_instance_dirty_list;
+ SelfList<GeometryInstanceSurfaceDataCache>::List geometry_surface_compilation_dirty_list;
+ SelfList<GeometryInstanceSurfaceDataCache>::List geometry_surface_compilation_all_list;
PagedAllocator<GeometryInstanceForwardClustered> geometry_instance_alloc;
PagedAllocator<GeometryInstanceSurfaceDataCache> geometry_instance_surface_alloc;
PagedAllocator<GeometryInstanceLightmapSH> geometry_instance_lightmap_sh;
+ struct SurfacePipelineData {
+ void *mesh_surface = nullptr;
+ void *mesh_surface_shadow = nullptr;
+ SceneShaderForwardClustered::ShaderData *shader = nullptr;
+ SceneShaderForwardClustered::ShaderData *shader_shadow = nullptr;
+ bool instanced = false;
+ bool uses_opaque = false;
+ bool uses_transparent = false;
+ bool uses_depth = false;
+ bool can_use_lightmap = false;
+ };
+
+ struct GlobalPipelineData {
+ union {
+ struct {
+ uint32_t texture_samples : 3;
+ uint32_t use_reflection_probes : 1;
+ uint32_t use_separate_specular : 1;
+ uint32_t use_motion_vectors : 1;
+ uint32_t use_normal_and_roughness : 1;
+ uint32_t use_lightmaps : 1;
+ uint32_t use_voxelgi : 1;
+ uint32_t use_sdfgi : 1;
+ uint32_t use_multiview : 1;
+ uint32_t use_16_bit_shadows : 1;
+ uint32_t use_32_bit_shadows : 1;
+ uint32_t use_shadow_cubemaps : 1;
+ uint32_t use_shadow_dual_paraboloid : 1;
+ };
+
+ uint32_t key;
+ };
+ };
+
+ GlobalPipelineData global_pipeline_data_compiled = {};
+ GlobalPipelineData global_pipeline_data_required = {};
+
+ typedef Pair<SceneShaderForwardClustered::ShaderData *, SceneShaderForwardClustered::ShaderData::PipelineKey> ShaderPipelinePair;
+
+ void _update_global_pipeline_data_requirements_from_project();
+ void _update_global_pipeline_data_requirements_from_light_storage();
void _geometry_instance_add_surface_with_material(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, SceneShaderForwardClustered::MaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh);
void _geometry_instance_add_surface_with_material_chain(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, SceneShaderForwardClustered::MaterialData *p_material, RID p_mat_src, RID p_mesh);
void _geometry_instance_add_surface(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, RID p_material, RID p_mesh);
void _geometry_instance_update(RenderGeometryInstance *p_geometry_instance);
+ void _mesh_compile_pipeline_for_surface(SceneShaderForwardClustered::ShaderData *p_shader, void *p_mesh_surface, bool p_ubershader, bool p_instanced_surface, RS::PipelineSource p_source, SceneShaderForwardClustered::ShaderData::PipelineKey &r_pipeline_key, Vector<ShaderPipelinePair> *r_pipeline_pairs = nullptr);
+ void _mesh_compile_pipelines_for_surface(const SurfacePipelineData &p_surface, const GlobalPipelineData &p_global, RS::PipelineSource p_source, Vector<ShaderPipelinePair> *r_pipeline_pairs = nullptr);
+ void _mesh_generate_all_pipelines_for_surface_cache(GeometryInstanceSurfaceDataCache *p_surface_cache, const GlobalPipelineData &p_global);
void _update_dirty_geometry_instances();
+ void _update_dirty_geometry_pipelines();
/* Render List */
@@ -663,8 +720,15 @@ public:
virtual uint32_t geometry_instance_get_pair_mask() override;
+ /* PIPELINES */
+
+ virtual void mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) override;
+ virtual uint32_t get_pipeline_compilations(RS::PipelineSource p_source) override;
+
virtual bool free(RID p_rid) override;
+ virtual void update() override;
+
RenderForwardClustered();
~RenderForwardClustered();
};
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 ee5b5ef1d8..53982af590 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
@@ -41,9 +41,9 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
//compile
code = p_code;
- valid = false;
ubo_size = 0;
uniforms.clear();
+ _clear_vertex_input_mask_cache();
if (code.is_empty()) {
return; //just invalid, but no error
@@ -51,9 +51,9 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
ShaderCompiler::GeneratedCode gen_code;
- int blend_mode = BLEND_MODE_MIX;
- int depth_testi = DEPTH_TEST_ENABLED;
- int alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
+ blend_mode = BLEND_MODE_MIX;
+ depth_testi = DEPTH_TEST_ENABLED;
+ alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
int cull_modei = CULL_BACK;
uses_point_size = false;
@@ -66,8 +66,8 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
uses_roughness = false;
uses_normal = false;
uses_tangent = false;
- bool uses_normal_map = false;
- bool wireframe = false;
+ uses_normal_map = false;
+ wireframe = false;
unshaded = false;
uses_vertex = false;
@@ -90,7 +90,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MIX);
actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_mode, BLEND_MODE_SUB);
actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MUL);
- actions.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&blend_mode, BLEND_MODE_PREMULT_ALPHA);
+ actions.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&blend_mode, BLEND_MODE_PREMULTIPLIED_ALPHA);
actions.render_mode_values["alpha_to_coverage"] = Pair<int *, int>(&alpha_antialiasing_mode, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE);
actions.render_mode_values["alpha_to_coverage_and_one"] = Pair<int *, int>(&alpha_antialiasing_mode, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE);
@@ -141,12 +141,12 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
actions.uniforms = &uniforms;
- SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton;
- Error err = shader_singleton->compiler.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code);
+ MutexLock lock(SceneShaderForwardClustered::singleton_mutex);
+ Error err = SceneShaderForwardClustered::singleton->compiler.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code);
ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
if (version.is_null()) {
- version = shader_singleton->shader.version_create();
+ version = SceneShaderForwardClustered::singleton->shader.version_create();
}
depth_draw = DepthDraw(depth_drawi);
@@ -178,95 +178,119 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
print_line("\n**vertex_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX]);
print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]);
#endif
- shader_singleton->shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines);
- ERR_FAIL_COND(!shader_singleton->shader.version_is_valid(version));
+ SceneShaderForwardClustered::singleton->shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines);
ubo_size = gen_code.uniform_total_size;
ubo_offsets = gen_code.uniform_offsets;
texture_uniforms = gen_code.texture_uniforms;
- //blend modes
+ pipeline_hash_map.clear_pipelines();
- // if any form of Alpha Antialiasing is enabled, set the blend mode to alpha to coverage
+ // If any form of Alpha Antialiasing is enabled, set the blend mode to alpha to coverage.
if (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF) {
blend_mode = BLEND_MODE_ALPHA_TO_COVERAGE;
}
- RD::PipelineColorBlendState::Attachment blend_attachment;
+ uses_blend_alpha = blend_mode_uses_blend_alpha(BlendMode(blend_mode));
+}
- switch (blend_mode) {
- case BLEND_MODE_MIX: {
- blend_attachment.enable_blend = true;
- blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+bool SceneShaderForwardClustered::ShaderData::is_animated() const {
+ return (uses_fragment_time && uses_discard) || (uses_vertex_time && uses_vertex);
+}
- } break;
- case BLEND_MODE_ADD: {
- blend_attachment.enable_blend = true;
- blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
- blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- uses_blend_alpha = true; //force alpha used because of blend
+bool SceneShaderForwardClustered::ShaderData::casts_shadows() const {
+ bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture;
+ bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing)) || has_read_screen_alpha;
+ bool has_alpha = has_base_alpha || uses_blend_alpha;
- } break;
- case BLEND_MODE_SUB: {
- blend_attachment.enable_blend = true;
- blend_attachment.alpha_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
- blend_attachment.color_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
- blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
- blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- uses_blend_alpha = true; //force alpha used because of blend
+ return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED));
+}
+RS::ShaderNativeSourceCode SceneShaderForwardClustered::ShaderData::get_native_source_code() const {
+ if (version.is_valid()) {
+ MutexLock lock(SceneShaderForwardClustered::singleton_mutex);
+ return SceneShaderForwardClustered::singleton->shader.version_get_native_source_code(version);
+ } else {
+ return RS::ShaderNativeSourceCode();
+ }
+}
+
+SceneShaderForwardClustered::ShaderVersion SceneShaderForwardClustered::ShaderData::_get_shader_version(PipelineVersion p_pipeline_version, uint32_t p_color_pass_flags, bool p_ubershader) const {
+ uint32_t ubershader_base = p_ubershader ? SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL : 0;
+ switch (p_pipeline_version) {
+ case PIPELINE_VERSION_DEPTH_PASS:
+ return ShaderVersion(SHADER_VERSION_DEPTH_PASS + ubershader_base);
+ case PIPELINE_VERSION_DEPTH_PASS_DP:
+ return ShaderVersion(SHADER_VERSION_DEPTH_PASS_DP + ubershader_base);
+ case PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS:
+ return ShaderVersion(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS + ubershader_base);
+ case PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI:
+ return ShaderVersion(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI + ubershader_base);
+ case PIPELINE_VERSION_DEPTH_PASS_MULTIVIEW:
+ return ShaderVersion(SHADER_VERSION_DEPTH_PASS_MULTIVIEW + ubershader_base);
+ case PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW:
+ return ShaderVersion(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW + ubershader_base);
+ case PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW:
+ return ShaderVersion(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW + ubershader_base);
+ case PIPELINE_VERSION_DEPTH_PASS_WITH_MATERIAL:
+ return ShaderVersion(SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL + SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL);
+ case PIPELINE_VERSION_DEPTH_PASS_WITH_SDF:
+ return ShaderVersion(SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL + SHADER_VERSION_DEPTH_PASS_WITH_SDF);
+ case PIPELINE_VERSION_COLOR_PASS: {
+ int shader_flags = 0;
+
+ if (p_ubershader) {
+ shader_flags |= SHADER_COLOR_PASS_FLAG_UBERSHADER;
+ }
+
+ if (p_color_pass_flags & PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR) {
+ shader_flags |= SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR;
+ }
+
+ if (p_color_pass_flags & PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS) {
+ shader_flags |= SHADER_COLOR_PASS_FLAG_MOTION_VECTORS;
+ }
+
+ if (p_color_pass_flags & PIPELINE_COLOR_PASS_FLAG_LIGHTMAP) {
+ shader_flags |= SHADER_COLOR_PASS_FLAG_LIGHTMAP;
+ }
+
+ if (p_color_pass_flags & PIPELINE_COLOR_PASS_FLAG_MULTIVIEW) {
+ shader_flags |= SHADER_COLOR_PASS_FLAG_MULTIVIEW;
+ }
+
+ return ShaderVersion(SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL + SHADER_VERSION_COLOR_PASS + shader_flags);
} break;
- case BLEND_MODE_MUL: {
- blend_attachment.enable_blend = true;
- blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_DST_COLOR;
- blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ZERO;
- blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_DST_ALPHA;
- blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
- uses_blend_alpha = true; //force alpha used because of blend
- } break;
- case BLEND_MODE_ALPHA_TO_COVERAGE: {
- blend_attachment.enable_blend = true;
- blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
- } break;
- case BLEND_MODE_PREMULT_ALPHA: {
- blend_attachment.enable_blend = true;
- blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_ONE;
- blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- uses_blend_alpha = true; // Force alpha used because of blend.
+ default: {
+ DEV_ASSERT(false && "Unknown pipeline version.");
+ return ShaderVersion(0);
} break;
}
+}
+
+void SceneShaderForwardClustered::ShaderData::_create_pipeline(PipelineKey p_pipeline_key) {
+#if PRINT_PIPELINE_COMPILATION_KEYS
+ print_line(
+ "HASH:", p_pipeline_key.hash(),
+ "VERSION:", version,
+ "VERTEX:", p_pipeline_key.vertex_format_id,
+ "FRAMEBUFFER:", p_pipeline_key.framebuffer_format_id,
+ "CULL:", p_pipeline_key.cull_mode,
+ "PRIMITIVE:", p_pipeline_key.primitive_type,
+ "VERSION:", p_pipeline_key.version,
+ "PASS FLAGS:", p_pipeline_key.color_pass_flags,
+ "SPEC PACKED #0:", p_pipeline_key.shader_specialization.packed_0,
+ "WIREFRAME:", p_pipeline_key.wireframe);
+#endif
// Color pass -> attachment 0: Color/Diffuse, attachment 1: Separate Specular, attachment 2: Motion Vectors
+ RD::PipelineColorBlendState::Attachment blend_attachment = blend_mode_to_blend_attachment(BlendMode(blend_mode));
RD::PipelineColorBlendState blend_state_color_blend;
blend_state_color_blend.attachments = { blend_attachment, RD::PipelineColorBlendState::Attachment(), RD::PipelineColorBlendState::Attachment() };
RD::PipelineColorBlendState blend_state_color_opaque = RD::PipelineColorBlendState::create_disabled(3);
RD::PipelineColorBlendState blend_state_depth_normal_roughness = RD::PipelineColorBlendState::create_disabled(1);
RD::PipelineColorBlendState blend_state_depth_normal_roughness_giprobe = RD::PipelineColorBlendState::create_disabled(2);
- //update pipelines
-
RD::PipelineDepthStencilState depth_stencil_state;
if (depth_test != DEPTH_TEST_DISABLED) {
@@ -276,171 +300,162 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
}
bool depth_pre_pass_enabled = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable"));
- for (int i = 0; i < CULL_VARIANT_MAX; i++) {
- RD::PolygonCullMode cull_mode_rd_table[CULL_VARIANT_MAX][3] = {
- { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_FRONT, RD::POLYGON_CULL_BACK },
- { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_BACK, RD::POLYGON_CULL_FRONT },
- { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED }
- };
+ RD::RenderPrimitive primitive_rd_table[RS::PRIMITIVE_MAX] = {
+ RD::RENDER_PRIMITIVE_POINTS,
+ RD::RENDER_PRIMITIVE_LINES,
+ RD::RENDER_PRIMITIVE_LINESTRIPS,
+ RD::RENDER_PRIMITIVE_TRIANGLES,
+ RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS,
+ };
+
+ RD::RenderPrimitive primitive_rd = uses_point_size ? RD::RENDER_PRIMITIVE_POINTS : primitive_rd_table[p_pipeline_key.primitive_type];
+
+ RD::PipelineRasterizationState raster_state;
+ raster_state.cull_mode = p_pipeline_key.cull_mode;
+ raster_state.wireframe = wireframe || p_pipeline_key.wireframe;
+
+ RD::PipelineMultisampleState multisample_state;
+ multisample_state.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_pipeline_key.framebuffer_format_id, 0);
+
+ RD::PipelineColorBlendState blend_state;
+ if (p_pipeline_key.version == PIPELINE_VERSION_COLOR_PASS) {
+ if (p_pipeline_key.color_pass_flags & PIPELINE_COLOR_PASS_FLAG_TRANSPARENT) {
+ if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE) {
+ multisample_state.enable_alpha_to_coverage = true;
+ } else if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE) {
+ multisample_state.enable_alpha_to_coverage = true;
+ multisample_state.enable_alpha_to_one = true;
+ }
- RD::PolygonCullMode cull_mode_rd = cull_mode_rd_table[i][cull_mode];
-
- for (int j = 0; j < RS::PRIMITIVE_MAX; j++) {
- RD::RenderPrimitive primitive_rd_table[RS::PRIMITIVE_MAX] = {
- RD::RENDER_PRIMITIVE_POINTS,
- RD::RENDER_PRIMITIVE_LINES,
- RD::RENDER_PRIMITIVE_LINESTRIPS,
- RD::RENDER_PRIMITIVE_TRIANGLES,
- RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS,
- };
-
- RD::RenderPrimitive primitive_rd = uses_point_size ? RD::RENDER_PRIMITIVE_POINTS : primitive_rd_table[j];
-
- for (int k = 0; k < PIPELINE_VERSION_MAX; k++) {
- ShaderVersion shader_version;
- static const ShaderVersion shader_version_table[PIPELINE_VERSION_MAX] = {
- SHADER_VERSION_DEPTH_PASS,
- SHADER_VERSION_DEPTH_PASS_DP,
- SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS,
- SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI,
- SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL,
- SHADER_VERSION_DEPTH_PASS_WITH_SDF,
- SHADER_VERSION_DEPTH_PASS_MULTIVIEW,
- SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW,
- SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW,
- SHADER_VERSION_COLOR_PASS,
- };
-
- shader_version = shader_version_table[k];
-
- if (!static_cast<SceneShaderForwardClustered *>(singleton)->shader.is_variant_enabled(shader_version)) {
- continue;
- }
- RD::PipelineRasterizationState raster_state;
- raster_state.cull_mode = cull_mode_rd;
- raster_state.wireframe = wireframe;
-
- if (k == PIPELINE_VERSION_COLOR_PASS) {
- for (int l = 0; l < PIPELINE_COLOR_PASS_FLAG_COUNT; l++) {
- if (!shader_singleton->valid_color_pass_pipelines[l]) {
- continue;
- }
-
- RD::PipelineDepthStencilState depth_stencil = depth_stencil_state;
-
- RD::PipelineColorBlendState blend_state;
- RD::PipelineMultisampleState multisample_state;
-
- int shader_flags = 0;
- if (l & PIPELINE_COLOR_PASS_FLAG_TRANSPARENT) {
- if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE) {
- multisample_state.enable_alpha_to_coverage = true;
- } else if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE) {
- multisample_state.enable_alpha_to_coverage = true;
- multisample_state.enable_alpha_to_one = true;
- }
-
- blend_state = blend_state_color_blend;
-
- if (depth_draw == DEPTH_DRAW_OPAQUE) {
- depth_stencil.enable_depth_write = false; //alpha does not draw depth
- }
- } else {
- blend_state = blend_state_color_opaque;
-
- if (depth_pre_pass_enabled) {
- // We already have a depth from the depth pre-pass, there is no need to write it again.
- // In addition we can use COMPARE_OP_EQUAL instead of COMPARE_OP_LESS_OR_EQUAL.
- // This way we can use the early depth test to discard transparent fragments before the fragment shader even starts.
- depth_stencil.depth_compare_operator = RD::COMPARE_OP_EQUAL;
- depth_stencil.enable_depth_write = false;
- }
-
- if (l & PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR) {
- shader_flags |= SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR;
- }
- }
-
- if (l & PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS) {
- shader_flags |= SHADER_COLOR_PASS_FLAG_MOTION_VECTORS;
- }
-
- if (l & PIPELINE_COLOR_PASS_FLAG_LIGHTMAP) {
- shader_flags |= SHADER_COLOR_PASS_FLAG_LIGHTMAP;
- }
-
- if (l & PIPELINE_COLOR_PASS_FLAG_MULTIVIEW) {
- shader_flags |= SHADER_COLOR_PASS_FLAG_MULTIVIEW;
- }
-
- int variant = shader_version + shader_flags;
-
- if (!static_cast<SceneShaderForwardClustered *>(singleton)->shader.is_variant_enabled(variant)) {
- continue;
- }
-
- RID shader_variant = shader_singleton->shader.version_get_shader(version, variant);
- color_pipelines[i][j][l].setup(shader_variant, primitive_rd, raster_state, multisample_state, depth_stencil, blend_state, 0, singleton->default_specialization_constants);
- }
- } else {
- RD::PipelineColorBlendState blend_state;
- RD::PipelineDepthStencilState depth_stencil = depth_stencil_state;
- RD::PipelineMultisampleState multisample_state;
-
- if (k == PIPELINE_VERSION_DEPTH_PASS || k == PIPELINE_VERSION_DEPTH_PASS_DP || k == PIPELINE_VERSION_DEPTH_PASS_MULTIVIEW) {
- //none, leave empty
- } else if (k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS || k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW) {
- blend_state = blend_state_depth_normal_roughness;
- } else if (k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI || k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW) {
- blend_state = blend_state_depth_normal_roughness_giprobe;
- } else if (k == PIPELINE_VERSION_DEPTH_PASS_WITH_MATERIAL) {
- blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
- } else if (k == PIPELINE_VERSION_DEPTH_PASS_WITH_SDF) {
- blend_state = RD::PipelineColorBlendState(); //no color targets for SDF
- }
-
- RID shader_variant = shader_singleton->shader.version_get_shader(version, shader_version);
- pipelines[i][j][k].setup(shader_variant, primitive_rd, raster_state, multisample_state, depth_stencil, blend_state, 0, singleton->default_specialization_constants);
- }
+ blend_state = blend_state_color_blend;
+
+ if (depth_draw == DEPTH_DRAW_OPAQUE) {
+ depth_stencil_state.enable_depth_write = false; //alpha does not draw depth
}
+ } else {
+ blend_state = blend_state_color_opaque;
+
+ if (depth_pre_pass_enabled) {
+ // We already have a depth from the depth pre-pass, there is no need to write it again.
+ // In addition we can use COMPARE_OP_EQUAL instead of COMPARE_OP_LESS_OR_EQUAL.
+ // This way we can use the early depth test to discard transparent fragments before the fragment shader even starts.
+ depth_stencil_state.depth_compare_operator = RD::COMPARE_OP_EQUAL;
+ depth_stencil_state.enable_depth_write = false;
+ }
+ }
+ } else {
+ switch (p_pipeline_key.version) {
+ case PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS:
+ case PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW:
+ blend_state = blend_state_depth_normal_roughness;
+ break;
+ case PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI:
+ case PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW:
+ blend_state = blend_state_depth_normal_roughness_giprobe;
+ break;
+ case PIPELINE_VERSION_DEPTH_PASS_WITH_MATERIAL:
+ // Writes to normal and roughness in opaque way.
+ blend_state = RD::PipelineColorBlendState::create_disabled(5);
+ break;
+ case PIPELINE_VERSION_DEPTH_PASS:
+ case PIPELINE_VERSION_DEPTH_PASS_DP:
+ case PIPELINE_VERSION_DEPTH_PASS_MULTIVIEW:
+ case PIPELINE_VERSION_DEPTH_PASS_WITH_SDF:
+ default:
+ break;
}
}
- valid = true;
+ // Convert the specialization from the key to pipeline specialization constants.
+ Vector<RD::PipelineSpecializationConstant> specialization_constants;
+ RD::PipelineSpecializationConstant sc;
+ sc.constant_id = 0;
+ sc.int_value = p_pipeline_key.shader_specialization.packed_0;
+ sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT;
+ specialization_constants.push_back(sc);
+
+ RID shader_rid = get_shader_variant(p_pipeline_key.version, p_pipeline_key.color_pass_flags, p_pipeline_key.ubershader);
+ ERR_FAIL_COND(shader_rid.is_null());
+
+ RID pipeline = RD::get_singleton()->render_pipeline_create(shader_rid, p_pipeline_key.framebuffer_format_id, p_pipeline_key.vertex_format_id, primitive_rd, raster_state, multisample_state, depth_stencil_state, blend_state, 0, 0, specialization_constants);
+ ERR_FAIL_COND(pipeline.is_null());
+
+ pipeline_hash_map.add_compiled_pipeline(p_pipeline_key.hash(), pipeline);
}
-bool SceneShaderForwardClustered::ShaderData::is_animated() const {
- return (uses_fragment_time && uses_discard) || (uses_vertex_time && uses_vertex);
+RD::PolygonCullMode SceneShaderForwardClustered::ShaderData::get_cull_mode_from_cull_variant(CullVariant p_cull_variant) {
+ const RD::PolygonCullMode cull_mode_rd_table[CULL_VARIANT_MAX][3] = {
+ { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_FRONT, RD::POLYGON_CULL_BACK },
+ { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_BACK, RD::POLYGON_CULL_FRONT },
+ { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED }
+ };
+
+ return cull_mode_rd_table[p_cull_variant][cull_mode];
}
-bool SceneShaderForwardClustered::ShaderData::casts_shadows() const {
- bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture;
- bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing)) || has_read_screen_alpha;
- bool has_alpha = has_base_alpha || uses_blend_alpha;
+RID SceneShaderForwardClustered::ShaderData::_get_shader_variant(ShaderVersion p_shader_version) const {
+ if (version.is_valid()) {
+ MutexLock lock(SceneShaderForwardClustered::singleton_mutex);
+ ERR_FAIL_NULL_V(SceneShaderForwardClustered::singleton, RID());
+ return SceneShaderForwardClustered::singleton->shader.version_get_shader(version, p_shader_version);
+ } else {
+ return RID();
+ }
+}
- return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED));
+void SceneShaderForwardClustered::ShaderData::_clear_vertex_input_mask_cache() {
+ for (uint32_t i = 0; i < VERTEX_INPUT_MASKS_SIZE; i++) {
+ vertex_input_masks[i].store(0);
+ }
}
-RS::ShaderNativeSourceCode SceneShaderForwardClustered::ShaderData::get_native_source_code() const {
- SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton;
+RID SceneShaderForwardClustered::ShaderData::get_shader_variant(PipelineVersion p_pipeline_version, uint32_t p_color_pass_flags, bool p_ubershader) const {
+ return _get_shader_variant(_get_shader_version(p_pipeline_version, p_color_pass_flags, p_ubershader));
+}
+
+uint64_t SceneShaderForwardClustered::ShaderData::get_vertex_input_mask(PipelineVersion p_pipeline_version, uint32_t p_color_pass_flags, bool p_ubershader) {
+ // Vertex input masks require knowledge of the shader. Since querying the shader can be expensive due to high contention and the necessary mutex, we cache the result instead.
+ ShaderVersion shader_version = _get_shader_version(p_pipeline_version, p_color_pass_flags, p_ubershader);
+ uint64_t input_mask = vertex_input_masks[shader_version].load(std::memory_order_relaxed);
+ if (input_mask == 0) {
+ RID shader_rid = _get_shader_variant(shader_version);
+ ERR_FAIL_COND_V(shader_rid.is_null(), 0);
- return shader_singleton->shader.version_get_native_source_code(version);
+ input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(shader_rid);
+ vertex_input_masks[shader_version].store(input_mask, std::memory_order_relaxed);
+ }
+
+ return input_mask;
+}
+
+bool SceneShaderForwardClustered::ShaderData::is_valid() const {
+ if (version.is_valid()) {
+ MutexLock lock(SceneShaderForwardClustered::singleton_mutex);
+ ERR_FAIL_NULL_V(SceneShaderForwardClustered::singleton, false);
+ return SceneShaderForwardClustered::singleton->shader.version_is_valid(version);
+ } else {
+ return false;
+ }
}
SceneShaderForwardClustered::ShaderData::ShaderData() :
shader_list_element(this) {
+ pipeline_hash_map.set_creation_object_and_function(this, &ShaderData::_create_pipeline);
+ pipeline_hash_map.set_compilations(SceneShaderForwardClustered::singleton->pipeline_compilations, &SceneShaderForwardClustered::singleton_mutex);
}
SceneShaderForwardClustered::ShaderData::~ShaderData() {
- SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton;
- ERR_FAIL_NULL(shader_singleton);
- //pipeline variants will clear themselves if shader is gone
+ pipeline_hash_map.clear_pipelines();
+
if (version.is_valid()) {
- shader_singleton->shader.version_free(version);
+ MutexLock lock(SceneShaderForwardClustered::singleton_mutex);
+ ERR_FAIL_NULL(SceneShaderForwardClustered::singleton);
+ SceneShaderForwardClustered::singleton->shader.version_free(version);
}
}
RendererRD::MaterialStorage::ShaderData *SceneShaderForwardClustered::_create_shader_func() {
+ MutexLock lock(SceneShaderForwardClustered::singleton_mutex);
ShaderData *shader_data = memnew(ShaderData);
singleton->shader_list.add(&shader_data->shader_list_element);
return shader_data;
@@ -455,9 +470,12 @@ void SceneShaderForwardClustered::MaterialData::set_next_pass(RID p_pass) {
}
bool SceneShaderForwardClustered::MaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
- SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton;
-
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardClustered::MATERIAL_UNIFORM_SET, true, true);
+ if (shader_data->version.is_valid()) {
+ MutexLock lock(SceneShaderForwardClustered::singleton_mutex);
+ return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, SceneShaderForwardClustered::singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardClustered::MATERIAL_UNIFORM_SET, true, true);
+ } else {
+ return false;
+ }
}
SceneShaderForwardClustered::MaterialData::~MaterialData() {
@@ -472,6 +490,7 @@ RendererRD::MaterialStorage::MaterialData *SceneShaderForwardClustered::_create_
}
SceneShaderForwardClustered *SceneShaderForwardClustered::singleton = nullptr;
+Mutex SceneShaderForwardClustered::singleton_mutex;
SceneShaderForwardClustered::SceneShaderForwardClustered() {
// there should be only one of these, contained within our RenderFM singleton.
@@ -498,17 +517,22 @@ void SceneShaderForwardClustered::init(const String p_defines) {
{
Vector<ShaderRD::VariantDefine> shader_versions;
- shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n", true)); // SHADER_VERSION_DEPTH_PASS
- shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n", true)); // SHADER_VERSION_DEPTH_PASS_DP
- shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n", true)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS
- shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI
+ for (uint32_t ubershader = 0; ubershader < 2; ubershader++) {
+ const String base_define = ubershader ? "\n#define UBERSHADER\n" : "";
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, base_define + "\n#define MODE_RENDER_DEPTH\n", true)); // SHADER_VERSION_DEPTH_PASS
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, base_define + "\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n", true)); // SHADER_VERSION_DEPTH_PASS_DP
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, base_define + "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n", true)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, base_define + "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, base_define + "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n", false)); // SHADER_VERSION_DEPTH_PASS_MULTIVIEW
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, base_define + "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED_MULTIVIEW, base_define + "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW
+ }
+
shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL
shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_SDF
- shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n", false)); // SHADER_VERSION_DEPTH_PASS_MULTIVIEW
- shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW
- shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW
Vector<String> color_pass_flags = {
+ "\n#define UBERSHADER\n", // SHADER_COLOR_PASS_FLAG_UBERSHADER
"\n#define MODE_SEPARATE_SPECULAR\n", // SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR
"\n#define USE_LIGHTMAP\n", // SHADER_COLOR_PASS_FLAG_LIGHTMAP
"\n#define USE_MULTIVIEW\n", // SHADER_COLOR_PASS_FLAG_MULTIVIEW
@@ -545,16 +569,6 @@ void SceneShaderForwardClustered::init(const String p_defines) {
}
}
- // Set flag to true if a combination is valid.
- // The only invalid combinations are those that include both TRANSPARENT and SEPARATE_SPECULAR.
- for (int i = 0; i < PIPELINE_COLOR_PASS_FLAG_COUNT; i++) {
- if ((i & PIPELINE_COLOR_PASS_FLAG_TRANSPARENT) && (i & PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR)) {
- valid_color_pass_pipelines[i] = false;
- } else {
- valid_color_pass_pipelines[i] = true;
- }
- }
-
material_storage->shader_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_3D, _create_shader_funcs);
material_storage->material_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_3D, _create_material_funcs);
@@ -779,8 +793,8 @@ void fragment() {
material_storage->material_set_shader(default_material, default_shader);
MaterialData *md = static_cast<MaterialData *>(material_storage->material_get_data(default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
- default_shader_rd = shader.version_get_shader(md->shader_data->version, SHADER_VERSION_COLOR_PASS);
- default_shader_sdfgi_rd = shader.version_get_shader(md->shader_data->version, SHADER_VERSION_DEPTH_PASS_WITH_SDF);
+ default_shader_rd = md->shader_data->get_shader_variant(PIPELINE_VERSION_COLOR_PASS, 0, false);
+ default_shader_sdfgi_rd = md->shader_data->get_shader_variant(PIPELINE_VERSION_DEPTH_PASS_WITH_SDF, 0, false);
default_material_shader_ptr = md->shader_data;
default_material_uniform_set = md->uniform_set;
@@ -855,19 +869,11 @@ void fragment() {
}
}
-void SceneShaderForwardClustered::set_default_specialization_constants(const Vector<RD::PipelineSpecializationConstant> &p_constants) {
- default_specialization_constants = p_constants;
+void SceneShaderForwardClustered::set_default_specialization(const ShaderSpecialization &p_specialization) {
+ default_specialization = p_specialization;
+
for (SelfList<ShaderData> *E = shader_list.first(); E; E = E->next()) {
- for (int i = 0; i < ShaderData::CULL_VARIANT_MAX; i++) {
- for (int j = 0; j < RS::PRIMITIVE_MAX; j++) {
- for (int k = 0; k < SHADER_VERSION_MAX; k++) {
- E->self()->pipelines[i][j][k].update_specialization_constants(default_specialization_constants);
- }
- for (int k = 0; k < PIPELINE_COLOR_PASS_FLAG_COUNT; k++) {
- E->self()->color_pipelines[i][j][k].update_specialization_constants(default_specialization_constants);
- }
- }
- }
+ E->self()->pipeline_hash_map.clear_pipelines();
}
}
@@ -877,3 +883,20 @@ void SceneShaderForwardClustered::enable_advanced_shader_group(bool p_needs_mult
}
shader.enable_group(SHADER_GROUP_ADVANCED);
}
+
+bool SceneShaderForwardClustered::is_multiview_shader_group_enabled() const {
+ return shader.is_group_enabled(SHADER_GROUP_MULTIVIEW);
+}
+
+bool SceneShaderForwardClustered::is_advanced_shader_group_enabled(bool p_multiview) const {
+ if (p_multiview) {
+ return shader.is_group_enabled(SHADER_GROUP_ADVANCED_MULTIVIEW);
+ } else {
+ return shader.is_group_enabled(SHADER_GROUP_ADVANCED);
+ }
+}
+
+uint32_t SceneShaderForwardClustered::get_pipeline_compilations(RS::PipelineSource p_source) {
+ MutexLock lock(SceneShaderForwardClustered::singleton_mutex);
+ return pipeline_compilations[p_source];
+}
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
index d5332032f9..136514588a 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
@@ -31,6 +31,7 @@
#ifndef SCENE_SHADER_FORWARD_CLUSTERED_H
#define SCENE_SHADER_FORWARD_CLUSTERED_H
+#include "servers/rendering/renderer_rd/pipeline_hash_map_rd.h"
#include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
#include "servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl.gen.h"
@@ -39,6 +40,7 @@ namespace RendererSceneRenderImplementation {
class SceneShaderForwardClustered {
private:
static SceneShaderForwardClustered *singleton;
+ static Mutex singleton_mutex;
public:
enum ShaderGroup {
@@ -53,21 +55,22 @@ public:
SHADER_VERSION_DEPTH_PASS_DP,
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS,
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI,
- SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL,
- SHADER_VERSION_DEPTH_PASS_WITH_SDF,
SHADER_VERSION_DEPTH_PASS_MULTIVIEW,
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW,
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW,
+ SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL,
+ SHADER_VERSION_DEPTH_PASS_WITH_SDF,
SHADER_VERSION_COLOR_PASS,
SHADER_VERSION_MAX
};
enum ShaderColorPassFlags {
- SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR = 1 << 0,
- SHADER_COLOR_PASS_FLAG_LIGHTMAP = 1 << 1,
- SHADER_COLOR_PASS_FLAG_MULTIVIEW = 1 << 2,
- SHADER_COLOR_PASS_FLAG_MOTION_VECTORS = 1 << 3,
- SHADER_COLOR_PASS_FLAG_COUNT = 1 << 4
+ SHADER_COLOR_PASS_FLAG_UBERSHADER = 1 << 0,
+ SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR = 1 << 1,
+ SHADER_COLOR_PASS_FLAG_LIGHTMAP = 1 << 2,
+ SHADER_COLOR_PASS_FLAG_MULTIVIEW = 1 << 3,
+ SHADER_COLOR_PASS_FLAG_MOTION_VECTORS = 1 << 4,
+ SHADER_COLOR_PASS_FLAG_COUNT = 1 << 5
};
enum PipelineVersion {
@@ -90,26 +93,45 @@ public:
PIPELINE_COLOR_PASS_FLAG_LIGHTMAP = 1 << 2,
PIPELINE_COLOR_PASS_FLAG_MULTIVIEW = 1 << 3,
PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS = 1 << 4,
- PIPELINE_COLOR_PASS_FLAG_COUNT = 1 << 5,
+ PIPELINE_COLOR_PASS_FLAG_OPTIONS = 5,
+ PIPELINE_COLOR_PASS_FLAG_COMBINATIONS = 1 << PIPELINE_COLOR_PASS_FLAG_OPTIONS,
};
- enum ShaderSpecializations {
- SHADER_SPECIALIZATION_FORWARD_GI = 1 << 0,
- SHADER_SPECIALIZATION_PROJECTOR = 1 << 1,
- SHADER_SPECIALIZATION_SOFT_SHADOWS = 1 << 2,
- SHADER_SPECIALIZATION_DIRECTIONAL_SOFT_SHADOWS = 1 << 3,
+ struct ShaderSpecialization {
+ union {
+ struct {
+ uint32_t use_forward_gi : 1;
+ uint32_t use_light_projector : 1;
+ uint32_t use_light_soft_shadows : 1;
+ uint32_t use_directional_soft_shadows : 1;
+ uint32_t decal_use_mipmaps : 1;
+ uint32_t projector_use_mipmaps : 1;
+ uint32_t use_depth_fog : 1;
+ uint32_t use_lightmap_bicubic_filter : 1;
+ uint32_t soft_shadow_samples : 4;
+ uint32_t penumbra_shadow_samples : 4;
+ uint32_t directional_soft_shadow_samples : 4;
+ uint32_t directional_penumbra_shadow_samples : 4;
+ };
+
+ uint32_t packed_0;
+ };
+
+ uint32_t packed_1;
+ uint32_t packed_2;
};
- struct ShaderData : public RendererRD::MaterialStorage::ShaderData {
- enum BlendMode { //used internally
- BLEND_MODE_MIX,
- BLEND_MODE_ADD,
- BLEND_MODE_SUB,
- BLEND_MODE_MUL,
- BLEND_MODE_ALPHA_TO_COVERAGE,
- BLEND_MODE_PREMULT_ALPHA,
+ struct UbershaderConstants {
+ union {
+ struct {
+ uint32_t cull_mode : 2;
+ };
+
+ uint32_t packed_0;
};
+ };
+ struct ShaderData : public RendererRD::MaterialStorage::ShaderData {
enum DepthDraw {
DEPTH_DRAW_DISABLED,
DEPTH_DRAW_OPAQUE,
@@ -141,11 +163,40 @@ public:
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE
};
- bool valid = false;
+ struct PipelineKey {
+ RD::VertexFormatID vertex_format_id;
+ RD::FramebufferFormatID framebuffer_format_id;
+ RD::PolygonCullMode cull_mode = RD::POLYGON_CULL_MAX;
+ RS::PrimitiveType primitive_type = RS::PRIMITIVE_MAX;
+ PipelineVersion version = PipelineVersion::PIPELINE_VERSION_MAX;
+ uint32_t color_pass_flags = 0;
+ ShaderSpecialization shader_specialization = {};
+ uint32_t wireframe = false;
+ uint32_t ubershader = false;
+
+ uint32_t hash() const {
+ uint32_t h = hash_murmur3_one_64(vertex_format_id);
+ h = hash_murmur3_one_32(framebuffer_format_id, h);
+ h = hash_murmur3_one_32(cull_mode, h);
+ h = hash_murmur3_one_32(primitive_type, h);
+ h = hash_murmur3_one_32(version, h);
+ h = hash_murmur3_one_32(color_pass_flags, h);
+ h = hash_murmur3_one_32(shader_specialization.packed_0, h);
+ h = hash_murmur3_one_32(shader_specialization.packed_1, h);
+ h = hash_murmur3_one_32(shader_specialization.packed_2, h);
+ h = hash_murmur3_one_32(wireframe, h);
+ h = hash_murmur3_one_32(ubershader, h);
+ return hash_fmix32(h);
+ }
+ };
+
+ void _create_pipeline(PipelineKey p_pipeline_key);
+ PipelineHashMapRD<PipelineKey, ShaderData, void (ShaderData::*)(PipelineKey)> pipeline_hash_map;
+
RID version;
- uint64_t vertex_input_mask = 0;
- PipelineCacheRD pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][PIPELINE_VERSION_MAX];
- PipelineCacheRD color_pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][PIPELINE_COLOR_PASS_FLAG_COUNT];
+
+ static const uint32_t VERTEX_INPUT_MASKS_SIZE = SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL + SHADER_VERSION_COLOR_PASS + SHADER_COLOR_PASS_FLAG_COUNT;
+ std::atomic<uint64_t> vertex_input_masks[VERTEX_INPUT_MASKS_SIZE] = {};
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
@@ -157,6 +208,10 @@ public:
DepthDraw depth_draw = DEPTH_DRAW_OPAQUE;
DepthTest depth_test = DEPTH_TEST_ENABLED;
+ int blend_mode = BLEND_MODE_MIX;
+ int depth_testi = DEPTH_TEST_ENABLED;
+ int alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
+
bool uses_point_size = false;
bool uses_alpha = false;
bool uses_blend_alpha = false;
@@ -168,6 +223,8 @@ public:
bool uses_normal = false;
bool uses_tangent = false;
bool uses_particle_trails = false;
+ bool uses_normal_map = false;
+ bool wireframe = false;
bool unshaded = false;
bool uses_vertex = false;
@@ -188,11 +245,39 @@ public:
uint64_t last_pass = 0;
uint32_t index = 0;
+ _FORCE_INLINE_ bool uses_alpha_pass() const {
+ bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture;
+ bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing)) || has_read_screen_alpha;
+ bool has_blend_alpha = uses_blend_alpha;
+ bool has_alpha = has_base_alpha || has_blend_alpha;
+ bool no_depth_draw = depth_draw == DEPTH_DRAW_DISABLED;
+ bool no_depth_test = depth_test == DEPTH_TEST_DISABLED;
+ return has_alpha || has_read_screen_alpha || no_depth_draw || no_depth_test;
+ }
+
+ _FORCE_INLINE_ bool uses_depth_in_alpha_pass() const {
+ bool no_depth_draw = depth_draw == DEPTH_DRAW_DISABLED;
+ bool no_depth_test = depth_test == DEPTH_TEST_DISABLED;
+ return (uses_depth_prepass_alpha || uses_alpha_antialiasing) && !(no_depth_draw || no_depth_test);
+ }
+
+ _FORCE_INLINE_ bool uses_shared_shadow_material() const {
+ bool backface_culling = cull_mode == CULL_BACK;
+ return !uses_particle_trails && !writes_modelview_or_projection && !uses_vertex && !uses_position && !uses_discard && !uses_depth_prepass_alpha && !uses_alpha_clip && !uses_alpha_antialiasing && backface_culling && !uses_point_size && !uses_world_coordinates;
+ }
+
virtual void set_code(const String &p_Code);
virtual bool is_animated() const;
virtual bool casts_shadows() const;
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
+ ShaderVersion _get_shader_version(PipelineVersion p_pipeline_version, uint32_t p_color_pass_flags, bool p_ubershader) const;
+ RID _get_shader_variant(ShaderVersion p_shader_version) const;
+ void _clear_vertex_input_mask_cache();
+ RID get_shader_variant(PipelineVersion p_pipeline_version, uint32_t p_color_pass_flags, bool p_ubershader) const;
+ uint64_t get_vertex_input_mask(PipelineVersion p_pipeline_version, uint32_t p_color_pass_flags, bool p_ubershader);
+ RD::PolygonCullMode get_cull_mode_from_cull_variant(CullVariant p_cull_variant);
+ bool is_valid() const;
SelfList<ShaderData> shader_list_element;
ShaderData();
@@ -250,14 +335,19 @@ public:
RID debug_shadow_splits_material_uniform_set;
ShaderData *debug_shadow_splits_material_shader_ptr = nullptr;
- Vector<RD::PipelineSpecializationConstant> default_specialization_constants;
- bool valid_color_pass_pipelines[PIPELINE_COLOR_PASS_FLAG_COUNT];
+ ShaderSpecialization default_specialization = {};
+
+ uint32_t pipeline_compilations[RS::PIPELINE_SOURCE_MAX] = {};
+
SceneShaderForwardClustered();
~SceneShaderForwardClustered();
void init(const String p_defines);
- void set_default_specialization_constants(const Vector<RD::PipelineSpecializationConstant> &p_constants);
+ void set_default_specialization(const ShaderSpecialization &p_specialization);
void enable_advanced_shader_group(bool p_needs_multiview = false);
+ bool is_multiview_shader_group_enabled() const;
+ bool is_advanced_shader_group_enabled(bool p_multiview) const;
+ uint32_t get_pipeline_compilations(RS::PipelineSource p_source);
};
} // namespace RendererSceneRenderImplementation
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 fa8796fae6..978ce097d3 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -38,6 +38,8 @@
#include "servers/rendering/rendering_device.h"
#include "servers/rendering/rendering_server_default.h"
+#define PRELOAD_PIPELINES_ON_SURFACE_CACHE_CONSTRUCTION 1
+
using namespace RendererSceneRenderImplementation;
RendererRD::ForwardID RenderForwardMobile::ForwardIDStorageMobile::allocate_forward_id(RendererRD::ForwardIDType p_type) {
@@ -277,6 +279,66 @@ void RenderForwardMobile::setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_r
p_render_buffers->set_custom_data(RB_SCOPE_MOBILE, data);
}
+void RenderForwardMobile::mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) {
+ RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
+ RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
+ RID shadow_mesh = mesh_storage->mesh_get_shadow_mesh(p_mesh);
+ uint32_t surface_count = 0;
+ const RID *materials = mesh_storage->mesh_get_surface_count_and_materials(p_mesh, surface_count);
+ Vector<ShaderPipelinePair> pipeline_pairs;
+ for (uint32_t i = 0; i < surface_count; i++) {
+ if (materials[i].is_null()) {
+ continue;
+ }
+
+ void *mesh_surface = mesh_storage->mesh_get_surface(p_mesh, i);
+ void *mesh_surface_shadow = mesh_surface;
+ SceneShaderForwardMobile::MaterialData *material = static_cast<SceneShaderForwardMobile::MaterialData *>(material_storage->material_get_data(materials[i], RendererRD::MaterialStorage::SHADER_TYPE_3D));
+ if (material == nullptr) {
+ continue;
+ }
+
+ SceneShaderForwardMobile::ShaderData *shader = material->shader_data;
+ SceneShaderForwardMobile::ShaderData *shader_shadow = shader;
+ if (material->shader_data->uses_shared_shadow_material()) {
+ SceneShaderForwardMobile::MaterialData *material_shadow = static_cast<SceneShaderForwardMobile::MaterialData *>(material_storage->material_get_data(scene_shader.default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
+ if (material_shadow != nullptr) {
+ shader_shadow = material_shadow->shader_data;
+ if (shadow_mesh.is_valid()) {
+ mesh_surface_shadow = mesh_storage->mesh_get_surface(shadow_mesh, i);
+ }
+ }
+ }
+
+ if (!shader->is_valid()) {
+ continue;
+ }
+
+ SurfacePipelineData surface;
+ surface.mesh_surface = mesh_surface;
+ surface.mesh_surface_shadow = mesh_surface_shadow;
+ surface.shader = shader;
+ surface.shader_shadow = shader_shadow;
+ surface.instanced = mesh_storage->mesh_needs_instance(p_mesh, true);
+ surface.uses_opaque = !material->shader_data->uses_alpha_pass();
+ surface.uses_transparent = material->shader_data->uses_alpha_pass();
+ surface.uses_depth = surface.uses_opaque || (surface.uses_transparent && material->shader_data->uses_depth_in_alpha_pass());
+ surface.can_use_lightmap = mesh_storage->mesh_surface_get_format(mesh_surface) & RS::ARRAY_FORMAT_TEX_UV2;
+ _mesh_compile_pipelines_for_surface(surface, global_pipeline_data_required, RS::PIPELINE_SOURCE_MESH, &pipeline_pairs);
+ }
+
+ // Try to retrieve all the pipeline pairs that were compiled. This will force the loader to wait on all ubershader pipelines to be ready.
+ if (!p_background_compilation && !pipeline_pairs.is_empty()) {
+ for (ShaderPipelinePair pair : pipeline_pairs) {
+ pair.first->pipeline_hash_map.get_pipeline(pair.second, pair.second.hash(), true, RS::PIPELINE_SOURCE_MESH);
+ }
+ }
+}
+
+uint32_t RenderForwardMobile::get_pipeline_compilations(RS::PipelineSource p_source) {
+ return scene_shader.get_pipeline_compilations(p_source);
+}
+
bool RenderForwardMobile::free(RID p_rid) {
if (RendererSceneRenderRD::free(p_rid)) {
return true;
@@ -284,6 +346,12 @@ bool RenderForwardMobile::free(RID p_rid) {
return false;
}
+void RenderForwardMobile::update() {
+ RendererSceneRenderRD::update();
+ _update_global_pipeline_data_requirements_from_project();
+ _update_global_pipeline_data_requirements_from_light_storage();
+}
+
/* Render functions */
float RenderForwardMobile::_render_buffers_get_luminance_multiplier() {
@@ -777,6 +845,9 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
merge_transparent_pass = true; // we ignore our screen/depth texture here
using_subpass_post_process = false; // not applicable at all for reflection probes.
samplers = RendererRD::MaterialStorage::get_singleton()->samplers_rd_get_default();
+
+ // Indicate pipelines for reflection probes are required.
+ global_pipeline_data_required.use_reflection_probes = true;
} else if (rb_data.is_valid()) {
// setup rendering to render buffer
screen_size = p_render_data->render_buffers->get_internal_size();
@@ -807,6 +878,16 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
ERR_FAIL(); //bug?
}
+ if (p_render_data->scene_data->view_count > 1) {
+ global_pipeline_data_required.use_multiview = true;
+ }
+
+ if (scene_state.used_lightmap) {
+ global_pipeline_data_required.use_lightmaps = true;
+ }
+
+ _update_dirty_geometry_pipelines();
+
p_render_data->scene_data->emissive_exposure_normalization = -1.0;
RD::get_singleton()->draw_command_begin_label("Render Setup");
@@ -924,25 +1005,23 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
_pre_opaque_render(p_render_data);
- uint32_t spec_constant_base_flags = 0;
+ SceneShaderForwardMobile::ShaderSpecialization base_specialization = scene_shader.default_specialization;
{
//figure out spec constants
if (p_render_data->directional_light_count > 0) {
- if (p_render_data->directional_light_soft_shadows) {
- spec_constant_base_flags |= 1 << SPEC_CONSTANT_USING_DIRECTIONAL_SOFT_SHADOWS;
- }
+ base_specialization.use_directional_soft_shadows = p_render_data->directional_light_soft_shadows;
} else {
- spec_constant_base_flags |= 1 << SPEC_CONSTANT_DISABLE_DIRECTIONAL_LIGHTS;
+ base_specialization.disable_directional_lights = true;
}
if (!is_environment(p_render_data->environment) || !environment_get_fog_enabled(p_render_data->environment)) {
- spec_constant_base_flags |= 1 << SPEC_CONSTANT_DISABLE_FOG;
+ base_specialization.disable_fog = true;
}
if (p_render_data->environment.is_valid() && environment_get_fog_mode(p_render_data->environment) == RS::EnvironmentFogMode::ENV_FOG_MODE_DEPTH) {
- spec_constant_base_flags |= 1 << SPEC_CONSTANT_USE_DEPTH_FOG;
+ base_specialization.use_depth_fog = true;
}
}
@@ -1010,7 +1089,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
}
if (render_list[RENDER_LIST_OPAQUE].elements.size() > 0) {
- RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].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);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, base_specialization, 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.
@@ -1037,7 +1116,7 @@ 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);
- 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, 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);
+ 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, rp_uniform_set, base_specialization, 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.
@@ -1088,7 +1167,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
// 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, 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);
+ 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, base_specialization, 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.
@@ -1395,7 +1474,7 @@ void RenderForwardMobile::_render_shadow_end() {
RD::get_singleton()->draw_command_begin_label("Shadow Render");
for (SceneState::ShadowPass &shadow_pass : scene_state.shadow_passes) {
- RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, 0, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from);
+ RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, scene_shader.default_specialization, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from);
_render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, RD::FINAL_ACTION_STORE, Vector<Color>(), 0.0, 0, shadow_pass.rect);
}
@@ -1439,7 +1518,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c
RENDER_TIMESTAMP("Render 3D Material");
{
- RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, rp_uniform_set, 0);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, rp_uniform_set, scene_shader.default_specialization);
//regular forward for now
Vector<Color> clear = {
Color(0, 0, 0, 0),
@@ -1484,7 +1563,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *>
RENDER_TIMESTAMP("Render 3D Material");
{
- RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, rp_uniform_set, true, false);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, rp_uniform_set, scene_shader.default_specialization, false);
//regular forward for now
Vector<Color> clear = {
Color(0, 0, 0, 0),
@@ -1568,7 +1647,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const
{
//regular forward for now
- RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), false, pass_mode, rp_uniform_set, 0);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), false, pass_mode, rp_uniform_set, scene_shader.default_specialization);
_render_list_with_draw_list(&render_list_params, p_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE);
}
RD::get_singleton()->draw_command_end_label();
@@ -1795,6 +1874,7 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const
scene_state.used_screen_texture = false;
scene_state.used_normal_texture = false;
scene_state.used_depth_texture = false;
+ scene_state.used_lightmap = false;
}
uint32_t lightmap_captures_used = 0;
@@ -1943,6 +2023,7 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const
if (uses_lightmap) {
surf->sort.uses_lightmap = 1; // This needs to become our lightmap index but we'll do that in a separate PR.
+ scene_state.used_lightmap = true;
}
if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_SUBSURFACE_SCATTERING) {
@@ -2036,14 +2117,21 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_params->render_pass_uniform_set, RENDER_PASS_UNIFORM_SET);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, scene_shader.default_vec4_xform_uniform_set, TRANSFORMS_UNIFORM_SET);
+ RID material_uniform_set;
RID prev_material_uniform_set;
RID prev_vertex_array_rd;
RID prev_index_array_rd;
- RID prev_pipeline_rd;
RID prev_xforms_uniform_set;
bool should_request_redraw = false;
+ void *mesh_surface;
+ SceneShaderForwardMobile::ShaderData *shader = nullptr;
+ SceneShaderForwardMobile::ShaderData *prev_shader = nullptr;
+ SceneShaderForwardMobile::ShaderData::PipelineKey pipeline_key;
+ uint32_t pipeline_hash = 0;
+ uint32_t prev_pipeline_hash = 0;
+
bool shadow_pass = (p_params->pass_mode == PASS_MODE_SHADOW) || (p_params->pass_mode == PASS_MODE_SHADOW_DP);
for (uint32_t i = p_from_element; i < p_to_element; i++) {
@@ -2055,11 +2143,8 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
continue;
}
- 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;
- }
+ SceneShaderForwardMobile::ShaderSpecialization pipeline_specialization = p_params->base_specialization;
+ pipeline_specialization.is_multimesh = bool(inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH);
SceneState::PushConstant push_constant;
push_constant.base_index = i + p_params->element_offset;
@@ -2072,35 +2157,18 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
push_constant.uv_offset[1] = 0.0;
}
- RID material_uniform_set;
- SceneShaderForwardMobile::ShaderData *shader;
- void *mesh_surface;
-
if (shadow_pass) {
material_uniform_set = surf->material_uniform_set_shadow;
shader = surf->shader_shadow;
mesh_surface = surf->surface_shadow;
} else {
- if (inst->use_projector) {
- base_spec_constants |= 1 << SPEC_CONSTANT_USING_PROJECTOR;
- }
- if (inst->use_soft_shadow) {
- base_spec_constants |= 1 << SPEC_CONSTANT_USING_SOFT_SHADOWS;
- }
-
- if (inst->omni_light_count == 0) {
- base_spec_constants |= 1 << SPEC_CONSTANT_DISABLE_OMNI_LIGHTS;
- }
- if (inst->spot_light_count == 0) {
- base_spec_constants |= 1 << SPEC_CONSTANT_DISABLE_SPOT_LIGHTS;
- }
- if (inst->reflection_probe_count == 0) {
- base_spec_constants |= 1 << SPEC_CONSTANT_DISABLE_REFLECTION_PROBES;
- }
- if (inst->decals_count == 0) {
- base_spec_constants |= 1 << SPEC_CONSTANT_DISABLE_DECALS;
- }
+ pipeline_specialization.use_light_projector = inst->use_projector;
+ pipeline_specialization.use_light_soft_shadows = inst->use_soft_shadow;
+ pipeline_specialization.disable_omni_lights = inst->omni_light_count == 0;
+ pipeline_specialization.disable_spot_lights = inst->spot_light_count == 0;
+ pipeline_specialization.disable_reflection_probes = inst->reflection_probe_count == 0;
+ pipeline_specialization.disable_decals = inst->decals_count == 0;
#ifdef DEBUG_ENABLED
if (unlikely(get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_LIGHTING)) {
@@ -2145,89 +2213,136 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
cull_variant = mirror ? SceneShaderForwardMobile::ShaderData::CULL_VARIANT_REVERSED : SceneShaderForwardMobile::ShaderData::CULL_VARIANT_NORMAL;
}
- RS::PrimitiveType primitive = surf->primitive;
+ pipeline_key.primitive_type = surf->primitive;
RID xforms_uniform_set = surf->owner->transforms_uniform_set;
- SceneShaderForwardMobile::ShaderVersion shader_version = SceneShaderForwardMobile::SHADER_VERSION_MAX; // Assigned to silence wrong -Wmaybe-initialized.
-
switch (p_params->pass_mode) {
case PASS_MODE_COLOR:
case PASS_MODE_COLOR_TRANSPARENT: {
if (element_info.uses_lightmap) {
- shader_version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_LIGHTMAP_COLOR_PASS;
+ pipeline_key.version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_LIGHTMAP_COLOR_PASS;
} else {
- shader_version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS;
+ pipeline_key.version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS;
}
} break;
case PASS_MODE_SHADOW: {
- shader_version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS;
+ pipeline_key.version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS;
} break;
case PASS_MODE_SHADOW_DP: {
ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for shadow DP pass");
- shader_version = SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS_DP;
+ pipeline_key.version = SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS_DP;
} break;
case PASS_MODE_DEPTH_MATERIAL: {
ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for material pass");
- shader_version = SceneShaderForwardMobile::SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL;
+ pipeline_key.version = SceneShaderForwardMobile::SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL;
} break;
}
- PipelineCacheRD *pipeline = &shader->pipelines[cull_variant][primitive][shader_version];
+ pipeline_key.framebuffer_format_id = framebuffer_format;
+ pipeline_key.wireframe = p_params->force_wireframe;
+ pipeline_key.render_pass = p_params->subpass;
+ pipeline_key.ubershader = 0;
+ const RD::PolygonCullMode cull_mode = shader->get_cull_mode_from_cull_variant(cull_variant);
RD::VertexFormatID vertex_format = -1;
+ RID pipeline_rd;
RID vertex_array_rd;
RID index_array_rd;
+ const uint32_t ubershader_iterations = 2;
+ bool pipeline_valid = false;
+ while (pipeline_key.ubershader < ubershader_iterations) {
+ // Skeleton and blend shape.
+ uint64_t input_mask = shader->get_vertex_input_mask(pipeline_key.version, pipeline_key.ubershader);
+ if (surf->owner->mesh_instance.is_valid()) {
+ mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, input_mask, false, vertex_array_rd, vertex_format);
+ } else {
+ mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, input_mask, false, vertex_array_rd, vertex_format);
+ }
- //skeleton and blend shape
- if (surf->owner->mesh_instance.is_valid()) {
- mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, pipeline->get_vertex_input_mask(), false, vertex_array_rd, vertex_format);
- } else {
- mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, pipeline->get_vertex_input_mask(), false, vertex_array_rd, vertex_format);
- }
+ index_array_rd = mesh_storage->mesh_surface_get_index_array(mesh_surface, element_info.lod_index);
+ pipeline_key.vertex_format_id = vertex_format;
- index_array_rd = mesh_storage->mesh_surface_get_index_array(mesh_surface, element_info.lod_index);
+ if (pipeline_key.ubershader) {
+ pipeline_key.shader_specialization = {};
+ pipeline_key.cull_mode = RD::POLYGON_CULL_DISABLED;
+ } else {
+ pipeline_key.shader_specialization = pipeline_specialization;
+ pipeline_key.cull_mode = cull_mode;
+ }
- if (prev_vertex_array_rd != vertex_array_rd) {
- RD::get_singleton()->draw_list_bind_vertex_array(draw_list, vertex_array_rd);
- prev_vertex_array_rd = vertex_array_rd;
- }
+ pipeline_hash = pipeline_key.hash();
- if (prev_index_array_rd != index_array_rd) {
- if (index_array_rd.is_valid()) {
- RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array_rd);
+ if (shader != prev_shader || pipeline_hash != prev_pipeline_hash) {
+ RS::PipelineSource pipeline_source = pipeline_key.ubershader ? RS::PIPELINE_SOURCE_DRAW : RS::PIPELINE_SOURCE_SPECIALIZATION;
+ pipeline_rd = shader->pipeline_hash_map.get_pipeline(pipeline_key, pipeline_hash, pipeline_key.ubershader, pipeline_source);
+
+ if (pipeline_rd.is_valid()) {
+ pipeline_valid = true;
+ prev_shader = shader;
+ prev_pipeline_hash = pipeline_hash;
+ break;
+ } else {
+ pipeline_key.ubershader++;
+ }
+ } else {
+ // The same pipeline is bound already.
+ pipeline_valid = true;
+ break;
}
- prev_index_array_rd = index_array_rd;
}
- RID pipeline_rd = pipeline->get_render_pipeline(vertex_format, framebuffer_format, p_params->force_wireframe, p_params->subpass, base_spec_constants);
+ if (pipeline_valid) {
+ index_array_rd = mesh_storage->mesh_surface_get_index_array(mesh_surface, element_info.lod_index);
- if (pipeline_rd != prev_pipeline_rd) {
- RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline_rd);
- prev_pipeline_rd = pipeline_rd;
- }
+ if (prev_vertex_array_rd != vertex_array_rd) {
+ RD::get_singleton()->draw_list_bind_vertex_array(draw_list, vertex_array_rd);
+ prev_vertex_array_rd = vertex_array_rd;
+ }
- if (xforms_uniform_set.is_valid() && prev_xforms_uniform_set != xforms_uniform_set) {
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, xforms_uniform_set, TRANSFORMS_UNIFORM_SET);
- prev_xforms_uniform_set = xforms_uniform_set;
- }
+ if (prev_index_array_rd != index_array_rd) {
+ if (index_array_rd.is_valid()) {
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array_rd);
+ }
+ prev_index_array_rd = index_array_rd;
+ }
- if (material_uniform_set != prev_material_uniform_set) {
- // Update uniform set.
- if (material_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set.
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_uniform_set, MATERIAL_UNIFORM_SET);
+ if (!pipeline_rd.is_null()) {
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline_rd);
}
- prev_material_uniform_set = material_uniform_set;
- }
+ if (xforms_uniform_set.is_valid() && prev_xforms_uniform_set != xforms_uniform_set) {
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, xforms_uniform_set, TRANSFORMS_UNIFORM_SET);
+ prev_xforms_uniform_set = xforms_uniform_set;
+ }
- RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(SceneState::PushConstant));
+ if (material_uniform_set != prev_material_uniform_set) {
+ // Update uniform set.
+ if (material_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set.
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_uniform_set, MATERIAL_UNIFORM_SET);
+ }
- uint32_t instance_count = surf->owner->instance_count > 1 ? surf->owner->instance_count : 1;
- if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_PARTICLE_TRAILS) {
- instance_count /= surf->owner->trail_steps;
- }
+ prev_material_uniform_set = material_uniform_set;
+ }
- RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count);
+ size_t push_constant_size = 0;
+ if (pipeline_key.ubershader) {
+ push_constant_size = sizeof(SceneState::PushConstant);
+ push_constant.ubershader.specialization = pipeline_specialization;
+ push_constant.ubershader.constants = {};
+ push_constant.ubershader.constants.cull_mode = cull_mode;
+ } else {
+ push_constant_size = sizeof(SceneState::PushConstant) - sizeof(SceneState::PushConstantUbershader);
+ }
+
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, push_constant_size);
+
+ uint32_t instance_count = surf->owner->instance_count > 1 ? surf->owner->instance_count : 1;
+ if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_PARTICLE_TRAILS) {
+ instance_count /= surf->owner->trail_steps;
+ }
+
+ RD::get_singleton()->draw_list_draw(draw_list, index_array_rd.is_valid(), instance_count);
+ }
}
// Make the actual redraw request
@@ -2363,14 +2478,25 @@ void RenderForwardMobile::GeometryInstanceForwardMobile::_mark_dirty() {
RenderForwardMobile::get_singleton()->geometry_instance_dirty_list.add(&dirty_list_element);
}
-void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryInstanceForwardMobile *ginstance, uint32_t p_surface, SceneShaderForwardMobile::MaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh) {
- RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
+void RenderForwardMobile::_update_global_pipeline_data_requirements_from_project() {
+ const int msaa_2d_mode = GLOBAL_GET("rendering/anti_aliasing/quality/msaa_2d");
+ const int msaa_3d_mode = GLOBAL_GET("rendering/anti_aliasing/quality/msaa_3d");
+ const bool directional_shadow_16_bits = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/16_bits");
+ const bool positional_shadow_16_bits = GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/atlas_16_bits");
+ global_pipeline_data_required.use_16_bit_shadows = directional_shadow_16_bits || positional_shadow_16_bits;
+ global_pipeline_data_required.use_32_bit_shadows = !directional_shadow_16_bits || !positional_shadow_16_bits;
+ global_pipeline_data_required.target_samples = RenderSceneBuffersRD::msaa_to_samples(RS::ViewportMSAA(msaa_2d_mode));
+ global_pipeline_data_required.texture_samples = RenderSceneBuffersRD::msaa_to_samples(RS::ViewportMSAA(msaa_3d_mode));
+}
- bool has_read_screen_alpha = p_material->shader_data->uses_screen_texture || p_material->shader_data->uses_depth_texture || p_material->shader_data->uses_normal_texture;
- bool has_base_alpha = p_material->shader_data->uses_alpha && (!p_material->shader_data->uses_alpha_clip || p_material->shader_data->uses_alpha_antialiasing);
- bool has_blend_alpha = p_material->shader_data->uses_blend_alpha;
- bool has_alpha = has_base_alpha || has_blend_alpha || has_read_screen_alpha;
+void RenderForwardMobile::_update_global_pipeline_data_requirements_from_light_storage() {
+ RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
+ global_pipeline_data_required.use_shadow_cubemaps = light_storage->get_shadow_cubemaps_used();
+ global_pipeline_data_required.use_shadow_dual_paraboloid = light_storage->get_shadow_dual_paraboloid_used();
+}
+void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryInstanceForwardMobile *ginstance, uint32_t p_surface, SceneShaderForwardMobile::MaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh) {
+ RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
uint32_t flags = 0;
if (p_material->shader_data->uses_sss) {
@@ -2393,10 +2519,9 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI
flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_DOUBLE_SIDED_SHADOWS;
}
- if (has_alpha || p_material->shader_data->depth_draw == SceneShaderForwardMobile::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardMobile::ShaderData::DEPTH_TEST_DISABLED) {
- //material is only meant for alpha pass
+ if (p_material->shader_data->uses_alpha_pass()) {
flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA;
- if ((p_material->shader_data->uses_depth_prepass_alpha || p_material->shader_data->uses_alpha_antialiasing) && !(p_material->shader_data->depth_draw == SceneShaderForwardMobile::ShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == SceneShaderForwardMobile::ShaderData::DEPTH_TEST_DISABLED)) {
+ if (p_material->shader_data->uses_depth_in_alpha_pass()) {
flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH;
flags |= GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW;
}
@@ -2412,7 +2537,7 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI
SceneShaderForwardMobile::MaterialData *material_shadow = nullptr;
void *surface_shadow = nullptr;
- if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_alpha_antialiasing && !p_material->shader_data->uses_world_coordinates) {
+ if (p_material->shader_data->uses_shared_shadow_material()) {
flags |= GeometryInstanceSurfaceDataCache::FLAG_USES_SHARED_SHADOW_MATERIAL;
material_shadow = static_cast<SceneShaderForwardMobile::MaterialData *>(RendererRD::MaterialStorage::get_singleton()->material_get_data(scene_shader.default_material, RendererRD::MaterialStorage::SHADER_TYPE_3D));
@@ -2471,6 +2596,16 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI
String mesh_path = mesh_storage->mesh_get_path(p_mesh).is_empty() ? "" : "(" + mesh_storage->mesh_get_path(p_mesh) + ")";
WARN_PRINT_ED(vformat("Attempting to use a shader %s that requires tangents with a mesh %s that doesn't contain tangents. Ensure that meshes are imported with the 'ensure_tangents' option. If creating your own meshes, add an `ARRAY_TANGENT` array (when using ArrayMesh) or call `generate_tangents()` (when using SurfaceTool).", shader_path, mesh_path));
}
+
+#if PRELOAD_PIPELINES_ON_SURFACE_CACHE_CONSTRUCTION
+ if (!sdcache->compilation_dirty_element.in_list()) {
+ geometry_surface_compilation_dirty_list.add(&sdcache->compilation_dirty_element);
+ }
+
+ if (!sdcache->compilation_all_element.in_list()) {
+ geometry_surface_compilation_all_list.add(&sdcache->compilation_all_element);
+ }
+#endif
}
void RenderForwardMobile::_geometry_instance_add_surface_with_material_chain(GeometryInstanceForwardMobile *ginstance, uint32_t p_surface, SceneShaderForwardMobile::MaterialData *p_material, RID p_mat_src, RID p_mesh) {
@@ -2482,7 +2617,7 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material_chain(Geo
while (material->next_pass.is_valid()) {
RID next_pass = material->next_pass;
material = static_cast<SceneShaderForwardMobile::MaterialData *>(material_storage->material_get_data(next_pass, RendererRD::MaterialStorage::SHADER_TYPE_3D));
- if (!material || !material->shader_data->valid) {
+ if (!material || !material->shader_data->is_valid()) {
break;
}
if (ginstance->data->dirty_dependencies) {
@@ -2502,7 +2637,7 @@ void RenderForwardMobile::_geometry_instance_add_surface(GeometryInstanceForward
if (m_src.is_valid()) {
material = static_cast<SceneShaderForwardMobile::MaterialData *>(material_storage->material_get_data(m_src, RendererRD::MaterialStorage::SHADER_TYPE_3D));
- if (!material || !material->shader_data->valid) {
+ if (!material || !material->shader_data->is_valid()) {
material = nullptr;
}
}
@@ -2524,7 +2659,7 @@ void RenderForwardMobile::_geometry_instance_add_surface(GeometryInstanceForward
m_src = ginstance->data->material_overlay;
material = static_cast<SceneShaderForwardMobile::MaterialData *>(material_storage->material_get_data(m_src, RendererRD::MaterialStorage::SHADER_TYPE_3D));
- if (material && material->shader_data->valid) {
+ if (material && material->shader_data->is_valid()) {
if (ginstance->data->dirty_dependencies) {
material_storage->material_update_dependency(m_src, &ginstance->data->dependency_tracker);
}
@@ -2685,10 +2820,262 @@ void RenderForwardMobile::_geometry_instance_update(RenderGeometryInstance *p_ge
ginstance->dirty_list_element.remove_from_list();
}
+static RD::FramebufferFormatID _get_color_framebuffer_format_for_pipeline(RD::DataFormat p_color_format, bool p_can_be_storage, RD::TextureSamples p_samples, RD::TextureSamples p_target_samples, bool p_vrs, bool p_post_pass, bool p_hdr, uint32_t p_view_count) {
+ const bool multisampling = p_samples > RD::TEXTURE_SAMPLES_1;
+ RD::AttachmentFormat attachment;
+
+ RD::AttachmentFormat unused_attachment;
+ unused_attachment.usage_flags = RD::AttachmentFormat::UNUSED_ATTACHMENT;
+
+ thread_local Vector<RD::AttachmentFormat> attachments;
+ attachments.clear();
+
+ // Color attachment.
+ attachment.samples = p_samples;
+ attachment.format = p_color_format;
+ attachment.usage_flags = RenderSceneBuffersRD::get_color_usage_bits(false, multisampling, p_can_be_storage);
+ attachments.push_back(attachment);
+
+ // Depth attachment.
+ attachment.samples = p_samples;
+ attachment.format = RenderSceneBuffersRD::get_depth_format(false, multisampling, p_can_be_storage);
+ attachment.usage_flags = RenderSceneBuffersRD::get_depth_usage_bits(false, multisampling, p_can_be_storage);
+ attachments.push_back(attachment);
+
+ if (p_vrs) {
+ attachment.samples = RD::TEXTURE_SAMPLES_1;
+ attachment.format = RenderSceneBuffersRD::get_vrs_format();
+ attachment.usage_flags = RenderSceneBuffersRD::get_vrs_usage_bits();
+ }
+
+ if (multisampling) {
+ // Resolve attachment.
+ attachment.samples = RD::TEXTURE_SAMPLES_1;
+ attachment.format = p_color_format;
+ attachment.usage_flags = RenderSceneBuffersRD::get_color_usage_bits(true, false, p_can_be_storage);
+ attachments.push_back(attachment);
+ }
+
+ RD::FramebufferPass pass;
+ thread_local Vector<RD::FramebufferPass> passes;
+ passes.clear();
+ pass.color_attachments.clear();
+ pass.color_attachments.push_back(0);
+ pass.depth_attachment = 1;
+
+ if (p_vrs) {
+ pass.vrs_attachment = 2;
+ }
+
+ if (multisampling) {
+ pass.resolve_attachments.push_back(attachments.size() - 1);
+ }
+
+ passes.push_back(pass);
+
+ if (p_post_pass) {
+ attachment.format = RendererRD::TextureStorage::render_target_get_color_format(p_hdr, false);
+
+ if (p_view_count > 1 || p_target_samples == RD::TEXTURE_SAMPLES_1) {
+ attachment.samples = RD::TEXTURE_SAMPLES_1;
+ attachment.usage_flags = RendererRD::TextureStorage::render_target_get_color_usage_bits(false);
+ } else {
+ attachment.samples = p_target_samples;
+ attachment.usage_flags = RendererRD::TextureStorage::render_target_get_color_usage_bits(true);
+ }
+
+ attachments.push_back(attachment);
+
+ RD::FramebufferPass blit_pass;
+ blit_pass.input_attachments.push_back(multisampling ? (attachments.size() - 2) : 0);
+ blit_pass.color_attachments.push_back(attachments.size() - 1);
+ passes.push_back(blit_pass);
+ }
+
+ return RD::get_singleton()->framebuffer_format_create_multipass(attachments, passes, p_view_count);
+}
+
+static RD::FramebufferFormatID _get_reflection_probe_color_framebuffer_format_for_pipeline() {
+ RD::AttachmentFormat attachment;
+ thread_local Vector<RD::AttachmentFormat> attachments;
+ attachments.clear();
+
+ attachment.format = RendererRD::LightStorage::get_reflection_probe_color_format();
+ attachment.usage_flags = RendererRD::LightStorage::get_reflection_probe_color_usage_bits();
+ attachments.push_back(attachment);
+
+ attachment.format = RendererRD::LightStorage::get_reflection_probe_depth_format();
+ attachment.usage_flags = RendererRD::LightStorage::get_reflection_probe_depth_usage_bits();
+ attachments.push_back(attachment);
+
+ return RD::get_singleton()->framebuffer_format_create(attachments);
+}
+
+static RD::FramebufferFormatID _get_shadow_cubemap_framebuffer_format_for_pipeline() {
+ thread_local LocalVector<RD::AttachmentFormat> attachments;
+ attachments.clear();
+
+ RD::AttachmentFormat attachment;
+ attachment.format = RendererRD::LightStorage::get_cubemap_depth_format();
+ attachment.usage_flags = RendererRD::LightStorage::get_cubemap_depth_usage_bits();
+ attachments.push_back(attachment);
+
+ return RD::get_singleton()->framebuffer_format_create(attachments);
+}
+
+static RD::FramebufferFormatID _get_shadow_atlas_framebuffer_format_for_pipeline(bool p_use_16_bits) {
+ thread_local LocalVector<RD::AttachmentFormat> attachments;
+ attachments.clear();
+
+ RD::AttachmentFormat attachment;
+ attachment.format = RendererRD::LightStorage::get_shadow_atlas_depth_format(p_use_16_bits);
+ attachment.usage_flags = RendererRD::LightStorage::get_shadow_atlas_depth_usage_bits();
+ attachments.push_back(attachment);
+
+ return RD::get_singleton()->framebuffer_format_create(attachments);
+}
+
+void RenderForwardMobile::_mesh_compile_pipeline_for_surface(SceneShaderForwardMobile::ShaderData *p_shader, void *p_mesh_surface, bool p_instanced_surface, RS::PipelineSource p_source, SceneShaderForwardMobile::ShaderData::PipelineKey &r_pipeline_key, Vector<ShaderPipelinePair> *r_pipeline_pairs) {
+ RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
+ uint64_t input_mask = p_shader->get_vertex_input_mask(r_pipeline_key.version, true);
+ r_pipeline_key.vertex_format_id = mesh_storage->mesh_surface_get_vertex_format(p_mesh_surface, input_mask, p_instanced_surface, false);
+ r_pipeline_key.ubershader = true;
+ p_shader->pipeline_hash_map.compile_pipeline(r_pipeline_key, r_pipeline_key.hash(), p_source);
+
+ if (r_pipeline_pairs != nullptr) {
+ r_pipeline_pairs->push_back({ p_shader, r_pipeline_key });
+ }
+}
+
+void RenderForwardMobile::_mesh_compile_pipelines_for_surface(const SurfacePipelineData &p_surface, const GlobalPipelineData &p_global, RS::PipelineSource p_source, Vector<ShaderPipelinePair> *r_pipeline_pairs) {
+ RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
+
+ // Set the attributes common to all pipelines.
+ SceneShaderForwardMobile::ShaderData::PipelineKey pipeline_key;
+ pipeline_key.cull_mode = RD::POLYGON_CULL_DISABLED;
+ pipeline_key.primitive_type = mesh_storage->mesh_surface_get_primitive(p_surface.mesh_surface);
+ pipeline_key.wireframe = false;
+
+ const bool multiview_enabled = p_global.use_multiview && scene_shader.is_multiview_enabled();
+ const RD::DataFormat buffers_color_format = _render_buffers_get_color_format();
+ const bool buffers_can_be_storage = _render_buffers_can_be_storage();
+ const uint32_t vrs_iterations = is_vrs_supported() ? 2 : 1;
+ for (uint32_t use_vrs = 0; use_vrs < vrs_iterations; use_vrs++) {
+ for (uint32_t use_post_pass = 0; use_post_pass < 2; use_post_pass++) {
+ const uint32_t hdr_iterations = use_post_pass ? 2 : 1;
+ for (uint32_t use_hdr = 0; use_hdr < hdr_iterations; use_hdr++) {
+ pipeline_key.version = SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS;
+ pipeline_key.framebuffer_format_id = _get_color_framebuffer_format_for_pipeline(buffers_color_format, buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), RD::TextureSamples(p_global.target_samples), use_vrs, use_post_pass, use_hdr, 1);
+ _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+
+ if (p_global.use_lightmaps && p_surface.can_use_lightmap) {
+ pipeline_key.version = SceneShaderForwardMobile::SHADER_VERSION_LIGHTMAP_COLOR_PASS;
+ _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+
+ if (multiview_enabled) {
+ // View count is assumed to be 2 as the configuration is dependent on the viewport. It's likely a safe assumption for stereo rendering.
+ const uint32_t view_count = 2;
+ pipeline_key.version = SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS_MULTIVIEW;
+ pipeline_key.framebuffer_format_id = _get_color_framebuffer_format_for_pipeline(buffers_color_format, buffers_can_be_storage, RD::TextureSamples(p_global.texture_samples), RD::TextureSamples(p_global.target_samples), use_vrs, use_post_pass, use_hdr, view_count);
+ _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+
+ if (p_global.use_lightmaps && p_surface.can_use_lightmap) {
+ pipeline_key.version = SceneShaderForwardMobile::SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW;
+ _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+ }
+ }
+ }
+ }
+
+ if (p_global.use_reflection_probes) {
+ pipeline_key.version = SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS;
+ pipeline_key.framebuffer_format_id = _get_reflection_probe_color_framebuffer_format_for_pipeline();
+ _mesh_compile_pipeline_for_surface(p_surface.shader, p_surface.mesh_surface, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+
+ if (!p_surface.uses_depth) {
+ return;
+ }
+
+ if (p_global.use_shadow_cubemaps) {
+ pipeline_key.version = SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS;
+ pipeline_key.framebuffer_format_id = _get_shadow_cubemap_framebuffer_format_for_pipeline();
+ _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+
+ const uint32_t use_16_bits_start = p_global.use_32_bit_shadows ? 0 : 1;
+ const uint32_t use_16_bits_iterations = p_global.use_16_bit_shadows ? 2 : 1;
+ for (uint32_t use_16_bits = use_16_bits_start; use_16_bits < use_16_bits_iterations; use_16_bits++) {
+ pipeline_key.version = SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS;
+ pipeline_key.framebuffer_format_id = _get_shadow_atlas_framebuffer_format_for_pipeline(use_16_bits);
+ _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+
+ if (p_global.use_shadow_dual_paraboloid) {
+ pipeline_key.version = SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS_DP;
+ _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+
+ if (multiview_enabled) {
+ pipeline_key.version = SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS_MULTIVIEW;
+ _mesh_compile_pipeline_for_surface(p_surface.shader_shadow, p_surface.mesh_surface_shadow, p_surface.instanced, p_source, pipeline_key, r_pipeline_pairs);
+ }
+ }
+}
+
+void RenderForwardMobile::_mesh_generate_all_pipelines_for_surface_cache(GeometryInstanceSurfaceDataCache *p_surface_cache, const GlobalPipelineData &p_global) {
+ bool uses_alpha_pass = (p_surface_cache->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_ALPHA) != 0;
+ SurfacePipelineData surface;
+ surface.mesh_surface = p_surface_cache->surface;
+ surface.mesh_surface_shadow = p_surface_cache->surface_shadow;
+ surface.shader = p_surface_cache->shader;
+ surface.shader_shadow = p_surface_cache->shader_shadow;
+ surface.instanced = p_surface_cache->owner->mesh_instance.is_valid();
+ surface.uses_opaque = !uses_alpha_pass;
+ surface.uses_transparent = uses_alpha_pass;
+ surface.uses_depth = (p_surface_cache->flags & (GeometryInstanceSurfaceDataCache::FLAG_PASS_DEPTH | GeometryInstanceSurfaceDataCache::FLAG_PASS_OPAQUE | GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW)) != 0;
+ surface.can_use_lightmap = p_surface_cache->owner->lightmap_instance.is_valid() || p_surface_cache->owner->lightmap_sh;
+ _mesh_compile_pipelines_for_surface(surface, p_global, RS::PIPELINE_SOURCE_SURFACE);
+}
+
void RenderForwardMobile::_update_dirty_geometry_instances() {
while (geometry_instance_dirty_list.first()) {
_geometry_instance_update(geometry_instance_dirty_list.first()->self());
}
+
+ _update_dirty_geometry_pipelines();
+}
+
+void RenderForwardMobile::_update_dirty_geometry_pipelines() {
+ if (global_pipeline_data_required.key != global_pipeline_data_compiled.key) {
+ // Go through the entire list of surfaces and compile pipelines for everything again.
+ SelfList<GeometryInstanceSurfaceDataCache> *list = geometry_surface_compilation_all_list.first();
+ while (list != nullptr) {
+ GeometryInstanceSurfaceDataCache *surface_cache = list->self();
+ _mesh_generate_all_pipelines_for_surface_cache(surface_cache, global_pipeline_data_required);
+
+ if (surface_cache->compilation_dirty_element.in_list()) {
+ // Remove any elements from the dirty list as they don't need to be processed again.
+ geometry_surface_compilation_dirty_list.remove(&surface_cache->compilation_dirty_element);
+ }
+
+ list = list->next();
+ }
+
+ global_pipeline_data_compiled.key = global_pipeline_data_required.key;
+ } else {
+ // Compile pipelines only for the dirty list.
+ if (!geometry_surface_compilation_dirty_list.first()) {
+ return;
+ }
+
+ while (geometry_surface_compilation_dirty_list.first() != nullptr) {
+ GeometryInstanceSurfaceDataCache *surface_cache = geometry_surface_compilation_dirty_list.first()->self();
+ _mesh_generate_all_pipelines_for_surface_cache(surface_cache, global_pipeline_data_compiled);
+ surface_cache->compilation_dirty_element.remove_from_list();
+ }
+ }
}
void RenderForwardMobile::_geometry_instance_dependency_changed(Dependency::DependencyChangedNotification p_notification, DependencyTracker *p_tracker) {
@@ -2734,54 +3121,26 @@ uint32_t RenderForwardMobile::get_max_elements() const {
RenderForwardMobile *RenderForwardMobile::singleton = nullptr;
void RenderForwardMobile::_update_shader_quality_settings() {
- Vector<RD::PipelineSpecializationConstant> spec_constants;
-
- RD::PipelineSpecializationConstant sc;
- sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT;
-
- sc.constant_id = SPEC_CONSTANT_SOFT_SHADOW_SAMPLES;
- sc.int_value = soft_shadow_samples_get();
-
- spec_constants.push_back(sc);
-
- sc.constant_id = SPEC_CONSTANT_PENUMBRA_SHADOW_SAMPLES;
- sc.int_value = penumbra_shadow_samples_get();
-
- spec_constants.push_back(sc);
-
- sc.constant_id = SPEC_CONSTANT_DIRECTIONAL_SOFT_SHADOW_SAMPLES;
- sc.int_value = directional_soft_shadow_samples_get();
-
- spec_constants.push_back(sc);
-
- sc.constant_id = SPEC_CONSTANT_DIRECTIONAL_PENUMBRA_SHADOW_SAMPLES;
- sc.int_value = directional_penumbra_shadow_samples_get();
-
- spec_constants.push_back(sc);
-
- sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL;
- sc.constant_id = SPEC_CONSTANT_DECAL_USE_MIPMAPS;
- sc.bool_value = decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS ||
+ SceneShaderForwardMobile::ShaderSpecialization specialization = {};
+ specialization.soft_shadow_samples = soft_shadow_samples_get();
+ specialization.penumbra_shadow_samples = penumbra_shadow_samples_get();
+ specialization.directional_soft_shadow_samples = directional_soft_shadow_samples_get();
+ specialization.directional_penumbra_shadow_samples = directional_penumbra_shadow_samples_get();
+ specialization.decal_use_mipmaps =
+ decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS ||
decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS ||
decals_get_filter() == RS::DECAL_FILTER_NEAREST_MIPMAPS_ANISOTROPIC ||
decals_get_filter() == RS::DECAL_FILTER_LINEAR_MIPMAPS_ANISOTROPIC;
- spec_constants.push_back(sc);
-
- sc.constant_id = SPEC_CONSTANT_PROJECTOR_USE_MIPMAPS;
- sc.bool_value = light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS ||
+ specialization.projector_use_mipmaps =
+ light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS ||
light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS ||
light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_NEAREST_MIPMAPS_ANISOTROPIC ||
light_projectors_get_filter() == RS::LIGHT_PROJECTOR_FILTER_LINEAR_MIPMAPS_ANISOTROPIC;
- 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);
+ specialization.use_lightmap_bicubic_filter = lightmap_filter_bicubic_get();
+ specialization.luminance_multiplier = 2.0f;
+ scene_shader.set_default_specialization(specialization);
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 9e429d598a..96f535ef45 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
@@ -60,32 +60,6 @@ private:
};
enum {
-
- SPEC_CONSTANT_USING_PROJECTOR = 0,
- SPEC_CONSTANT_USING_SOFT_SHADOWS = 1,
- SPEC_CONSTANT_USING_DIRECTIONAL_SOFT_SHADOWS = 2,
-
- SPEC_CONSTANT_SOFT_SHADOW_SAMPLES = 3,
- SPEC_CONSTANT_PENUMBRA_SHADOW_SAMPLES = 4,
- SPEC_CONSTANT_DIRECTIONAL_SOFT_SHADOW_SAMPLES = 5,
- SPEC_CONSTANT_DIRECTIONAL_PENUMBRA_SHADOW_SAMPLES = 6,
-
- SPEC_CONSTANT_DECAL_USE_MIPMAPS = 7,
- SPEC_CONSTANT_PROJECTOR_USE_MIPMAPS = 8,
-
- SPEC_CONSTANT_DISABLE_OMNI_LIGHTS = 9,
- SPEC_CONSTANT_DISABLE_SPOT_LIGHTS = 10,
- SPEC_CONSTANT_DISABLE_REFLECTION_PROBES = 11,
- SPEC_CONSTANT_DISABLE_DIRECTIONAL_LIGHTS = 12,
-
- 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 {
MAX_LIGHTMAPS = 8,
MAX_RDL_CULL = 8, // maximum number of reflection probes, decals or lights we can cull per geometry instance
INSTANCE_DATA_BUFFER_MIN_SIZE = 4096
@@ -152,14 +126,14 @@ private:
RID render_pass_uniform_set;
bool force_wireframe = false;
Vector2 uv_offset;
- uint32_t spec_constant_base_flags = 0;
+ SceneShaderForwardMobile::ShaderSpecialization base_specialization;
float lod_distance_multiplier = 0.0;
float screen_mesh_lod_threshold = 0.0;
RD::FramebufferFormatID framebuffer_format = 0;
uint32_t element_offset = 0;
uint32_t subpass = 0;
- RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, uint32_t p_spec_constant_base_flags = 0, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0) {
+ RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, SceneShaderForwardMobile::ShaderSpecialization p_base_specialization, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0) {
elements = p_elements;
element_info = p_element_info;
element_count = p_element_count;
@@ -173,7 +147,7 @@ private:
lod_distance_multiplier = p_lod_distance_multiplier;
screen_mesh_lod_threshold = p_screen_mesh_lod_threshold;
element_offset = p_element_offset;
- spec_constant_base_flags = p_spec_constant_base_flags;
+ base_specialization = p_base_specialization;
}
};
@@ -222,10 +196,16 @@ private:
struct SceneState {
LocalVector<RID> uniform_buffers;
+ struct PushConstantUbershader {
+ SceneShaderForwardMobile::ShaderSpecialization specialization;
+ SceneShaderForwardMobile::UbershaderConstants constants;
+ };
+
struct PushConstant {
float uv_offset[2];
uint32_t base_index;
uint32_t pad;
+ PushConstantUbershader ubershader;
};
struct InstanceData {
@@ -264,6 +244,7 @@ private:
bool used_normal_texture = false;
bool used_depth_texture = false;
bool used_sss = false;
+ bool used_lightmap = false;
struct ShadowPass {
uint32_t element_from;
@@ -455,6 +436,12 @@ protected:
GeometryInstanceSurfaceDataCache *next = nullptr;
GeometryInstanceForwardMobile *owner = nullptr;
+
+ SelfList<GeometryInstanceSurfaceDataCache> compilation_dirty_element;
+ SelfList<GeometryInstanceSurfaceDataCache> compilation_all_element;
+
+ GeometryInstanceSurfaceDataCache() :
+ compilation_dirty_element(this), compilation_all_element(this) {}
};
class GeometryInstanceForwardMobile : public RenderGeometryInstanceBase {
@@ -560,24 +547,74 @@ public:
static void _geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker);
SelfList<GeometryInstanceForwardMobile>::List geometry_instance_dirty_list;
+ SelfList<GeometryInstanceSurfaceDataCache>::List geometry_surface_compilation_dirty_list;
+ SelfList<GeometryInstanceSurfaceDataCache>::List geometry_surface_compilation_all_list;
PagedAllocator<GeometryInstanceForwardMobile> geometry_instance_alloc;
PagedAllocator<GeometryInstanceSurfaceDataCache> geometry_instance_surface_alloc;
PagedAllocator<GeometryInstanceLightmapSH> geometry_instance_lightmap_sh;
+ struct SurfacePipelineData {
+ void *mesh_surface = nullptr;
+ void *mesh_surface_shadow = nullptr;
+ SceneShaderForwardMobile::ShaderData *shader = nullptr;
+ SceneShaderForwardMobile::ShaderData *shader_shadow = nullptr;
+ bool instanced = false;
+ bool uses_opaque = false;
+ bool uses_transparent = false;
+ bool uses_depth = false;
+ bool can_use_lightmap = false;
+ };
+
+ struct GlobalPipelineData {
+ union {
+ struct {
+ uint32_t texture_samples : 3;
+ uint32_t target_samples : 3;
+ uint32_t use_reflection_probes : 1;
+ uint32_t use_lightmaps : 1;
+ uint32_t use_multiview : 1;
+ uint32_t use_16_bit_shadows : 1;
+ uint32_t use_32_bit_shadows : 1;
+ uint32_t use_shadow_cubemaps : 1;
+ uint32_t use_shadow_dual_paraboloid : 1;
+ };
+
+ uint32_t key;
+ };
+ };
+
+ GlobalPipelineData global_pipeline_data_compiled = {};
+ GlobalPipelineData global_pipeline_data_required = {};
+
+ typedef Pair<SceneShaderForwardMobile::ShaderData *, SceneShaderForwardMobile::ShaderData::PipelineKey> ShaderPipelinePair;
+
+ void _update_global_pipeline_data_requirements_from_project();
+ void _update_global_pipeline_data_requirements_from_light_storage();
void _geometry_instance_add_surface_with_material(GeometryInstanceForwardMobile *ginstance, uint32_t p_surface, SceneShaderForwardMobile::MaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh);
void _geometry_instance_add_surface_with_material_chain(GeometryInstanceForwardMobile *ginstance, uint32_t p_surface, SceneShaderForwardMobile::MaterialData *p_material, RID p_mat_src, RID p_mesh);
void _geometry_instance_add_surface(GeometryInstanceForwardMobile *ginstance, uint32_t p_surface, RID p_material, RID p_mesh);
void _geometry_instance_update(RenderGeometryInstance *p_geometry_instance);
+ void _mesh_compile_pipeline_for_surface(SceneShaderForwardMobile::ShaderData *p_shader, void *p_mesh_surface, bool p_instanced_surface, RS::PipelineSource p_source, SceneShaderForwardMobile::ShaderData::PipelineKey &r_pipeline_key, Vector<ShaderPipelinePair> *r_pipeline_pairs = nullptr);
+ void _mesh_compile_pipelines_for_surface(const SurfacePipelineData &p_surface, const GlobalPipelineData &p_global, RS::PipelineSource p_source, Vector<ShaderPipelinePair> *r_pipeline_pairs = nullptr);
+ void _mesh_generate_all_pipelines_for_surface_cache(GeometryInstanceSurfaceDataCache *p_surface_cache, const GlobalPipelineData &p_global);
void _update_dirty_geometry_instances();
+ void _update_dirty_geometry_pipelines();
virtual RenderGeometryInstance *geometry_instance_create(RID p_base) override;
virtual void geometry_instance_free(RenderGeometryInstance *p_geometry_instance) override;
virtual uint32_t geometry_instance_get_pair_mask() override;
+ /* PIPELINES */
+
+ virtual void mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) override;
+ virtual uint32_t get_pipeline_compilations(RS::PipelineSource p_source) override;
+
virtual bool free(RID p_rid) override;
+ virtual void update() override;
+
virtual void base_uniforms_changed() override;
virtual bool is_dynamic_gi_supported() const override;
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 5dec4d8f21..69f084f4c0 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
@@ -43,9 +43,9 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
//compile
code = p_code;
- valid = false;
ubo_size = 0;
uniforms.clear();
+ _clear_vertex_input_mask_cache();
if (code.is_empty()) {
return; //just invalid, but no error
@@ -53,10 +53,10 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
ShaderCompiler::GeneratedCode gen_code;
- int blend_mode = BLEND_MODE_MIX;
- int depth_testi = DEPTH_TEST_ENABLED;
- int alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
- int cull = CULL_BACK;
+ blend_mode = BLEND_MODE_MIX;
+ depth_testi = DEPTH_TEST_ENABLED;
+ alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
+ cull_mode = CULL_BACK;
uses_point_size = false;
uses_alpha = false;
@@ -68,8 +68,8 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
uses_roughness = false;
uses_normal = false;
uses_tangent = false;
- bool uses_normal_map = false;
- bool wireframe = false;
+ uses_normal_map = false;
+ wireframe = false;
unshaded = false;
uses_vertex = false;
@@ -91,7 +91,7 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MIX);
actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_mode, BLEND_MODE_SUB);
actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MUL);
- actions.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&blend_mode, BLEND_MODE_PREMULT_ALPHA);
+ actions.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&blend_mode, BLEND_MODE_PREMULTIPLIED_ALPHA);
actions.render_mode_values["alpha_to_coverage"] = Pair<int *, int>(&alpha_antialiasing_mode, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE);
actions.render_mode_values["alpha_to_coverage_and_one"] = Pair<int *, int>(&alpha_antialiasing_mode, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE);
@@ -102,9 +102,9 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED);
- actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull, CULL_DISABLED);
- actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull, CULL_FRONT);
- actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull, CULL_BACK);
+ actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull_mode, CULL_DISABLED);
+ actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull_mode, CULL_FRONT);
+ actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull_mode, CULL_BACK);
actions.render_mode_flags["unshaded"] = &unshaded;
actions.render_mode_flags["wireframe"] = &wireframe;
@@ -141,13 +141,12 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
actions.uniforms = &uniforms;
- SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton;
-
- Error err = shader_singleton->compiler.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code);
+ MutexLock lock(SceneShaderForwardMobile::singleton_mutex);
+ Error err = SceneShaderForwardMobile::singleton->compiler.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code);
ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
if (version.is_null()) {
- version = shader_singleton->shader.version_create();
+ version = SceneShaderForwardMobile::singleton->shader.version_create();
}
depth_draw = DepthDraw(depth_drawi);
@@ -189,86 +188,60 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]);
#endif
- shader_singleton->shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines);
- ERR_FAIL_COND(!shader_singleton->shader.version_is_valid(version));
+ SceneShaderForwardMobile::singleton->shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines);
ubo_size = gen_code.uniform_total_size;
ubo_offsets = gen_code.uniform_offsets;
texture_uniforms = gen_code.texture_uniforms;
- //blend modes
+ pipeline_hash_map.clear_pipelines();
- // if any form of Alpha Antialiasing is enabled, set the blend mode to alpha to coverage
+ // If any form of Alpha Antialiasing is enabled, set the blend mode to alpha to coverage.
if (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF) {
blend_mode = BLEND_MODE_ALPHA_TO_COVERAGE;
}
- RD::PipelineColorBlendState::Attachment blend_attachment;
-
- switch (blend_mode) {
- case BLEND_MODE_MIX: {
- blend_attachment.enable_blend = true;
- blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
-
- } break;
- case BLEND_MODE_ADD: {
- blend_attachment.enable_blend = true;
- blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
- blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- uses_blend_alpha = true; //force alpha used because of blend
-
- } break;
- case BLEND_MODE_SUB: {
- blend_attachment.enable_blend = true;
- blend_attachment.alpha_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
- blend_attachment.color_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
- blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
- blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- uses_blend_alpha = true; //force alpha used because of blend
-
- } break;
- case BLEND_MODE_MUL: {
- blend_attachment.enable_blend = true;
- blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_DST_COLOR;
- blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ZERO;
- blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_DST_ALPHA;
- blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
- uses_blend_alpha = true; //force alpha used because of blend
- } break;
- case BLEND_MODE_ALPHA_TO_COVERAGE: {
- blend_attachment.enable_blend = true;
- blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
- } break;
- case BLEND_MODE_PREMULT_ALPHA: {
- blend_attachment.enable_blend = true;
- blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_ONE;
- blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- uses_blend_alpha = true; // Force alpha used because of blend.
- } break;
+ uses_blend_alpha = blend_mode_uses_blend_alpha(BlendMode(blend_mode));
+}
+
+bool SceneShaderForwardMobile::ShaderData::is_animated() const {
+ return (uses_fragment_time && uses_discard) || (uses_vertex_time && uses_vertex);
+}
+
+bool SceneShaderForwardMobile::ShaderData::casts_shadows() const {
+ bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture;
+ bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing)) || has_read_screen_alpha;
+ bool has_alpha = has_base_alpha || uses_blend_alpha;
+
+ return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED));
+}
+
+RS::ShaderNativeSourceCode SceneShaderForwardMobile::ShaderData::get_native_source_code() const {
+ if (version.is_valid()) {
+ MutexLock lock(SceneShaderForwardMobile::singleton_mutex);
+ return SceneShaderForwardMobile::singleton->shader.version_get_native_source_code(version);
+ } else {
+ return RS::ShaderNativeSourceCode();
}
+}
+
+void SceneShaderForwardMobile::ShaderData::_create_pipeline(PipelineKey p_pipeline_key) {
+#if PRINT_PIPELINE_COMPILATION_KEYS
+ print_line(
+ "HASH:", p_pipeline_key.hash(),
+ "VERSION:", version,
+ "VERTEX:", p_pipeline_key.vertex_format_id,
+ "FRAMEBUFFER:", p_pipeline_key.framebuffer_format_id,
+ "CULL:", p_pipeline_key.cull_mode,
+ "PRIMITIVE:", p_pipeline_key.primitive_type,
+ "VERSION:", p_pipeline_key.version,
+ "SPEC PACKED #0:", p_pipeline_key.shader_specialization.packed_0,
+ "SPEC PACKED #1:", p_pipeline_key.shader_specialization.packed_1,
+ "RENDER PASS:", p_pipeline_key.render_pass,
+ "WIREFRAME:", p_pipeline_key.wireframe);
+#endif
+ RD::PipelineColorBlendState::Attachment blend_attachment = blend_mode_to_blend_attachment(BlendMode(blend_mode));
RD::PipelineColorBlendState blend_state_blend;
blend_state_blend.attachments.push_back(blend_attachment);
RD::PipelineColorBlendState blend_state_opaque = RD::PipelineColorBlendState::create_disabled(1);
@@ -286,113 +259,151 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
depth_stencil_state.enable_depth_write = depth_draw != DEPTH_DRAW_DISABLED ? true : false;
}
- for (int i = 0; i < CULL_VARIANT_MAX; i++) {
- RD::PolygonCullMode cull_mode_rd_table[CULL_VARIANT_MAX][3] = {
- { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_FRONT, RD::POLYGON_CULL_BACK },
- { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_BACK, RD::POLYGON_CULL_FRONT },
- { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED }
- };
-
- RD::PolygonCullMode cull_mode_rd = cull_mode_rd_table[i][cull];
-
- for (int j = 0; j < RS::PRIMITIVE_MAX; j++) {
- RD::RenderPrimitive primitive_rd_table[RS::PRIMITIVE_MAX] = {
- RD::RENDER_PRIMITIVE_POINTS,
- RD::RENDER_PRIMITIVE_LINES,
- RD::RENDER_PRIMITIVE_LINESTRIPS,
- RD::RENDER_PRIMITIVE_TRIANGLES,
- RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS,
- };
-
- RD::RenderPrimitive primitive_rd = uses_point_size ? RD::RENDER_PRIMITIVE_POINTS : primitive_rd_table[j];
-
- for (int k = 0; k < SHADER_VERSION_MAX; k++) {
- if (!static_cast<SceneShaderForwardMobile *>(singleton)->shader.is_variant_enabled(k)) {
- continue;
- }
- RD::PipelineRasterizationState raster_state;
- raster_state.cull_mode = cull_mode_rd;
- raster_state.wireframe = wireframe;
-
- RD::PipelineColorBlendState blend_state;
- RD::PipelineDepthStencilState depth_stencil = depth_stencil_state;
- RD::PipelineMultisampleState multisample_state;
-
- if (uses_alpha || uses_blend_alpha) {
- // only allow these flags to go through if we have some form of msaa
- if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE) {
- multisample_state.enable_alpha_to_coverage = true;
- } else if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE) {
- multisample_state.enable_alpha_to_coverage = true;
- multisample_state.enable_alpha_to_one = true;
- }
-
- if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_MULTIVIEW || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) {
- blend_state = blend_state_blend;
- if (depth_draw == DEPTH_DRAW_OPAQUE && !uses_alpha_clip) {
- depth_stencil.enable_depth_write = false; //alpha does not draw depth
- }
- } else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || k == SHADER_VERSION_SHADOW_PASS_DP) {
- //none, blend state contains nothing
- } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
- blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
- } else {
- pipelines[i][j][k].clear();
- continue; // do not use this version (will error if using it is attempted)
- }
- } else {
- if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_MULTIVIEW || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) {
- blend_state = blend_state_opaque;
- } else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || k == SHADER_VERSION_SHADOW_PASS_DP) {
- //none, leave empty
- } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
- blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
- } else {
- // ???
- }
- }
-
- RID shader_variant = shader_singleton->shader.version_get_shader(version, k);
- pipelines[i][j][k].setup(shader_variant, primitive_rd, raster_state, multisample_state, depth_stencil, blend_state, 0, singleton->default_specialization_constants);
+ RD::RenderPrimitive primitive_rd_table[RS::PRIMITIVE_MAX] = {
+ RD::RENDER_PRIMITIVE_POINTS,
+ RD::RENDER_PRIMITIVE_LINES,
+ RD::RENDER_PRIMITIVE_LINESTRIPS,
+ RD::RENDER_PRIMITIVE_TRIANGLES,
+ RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS,
+ };
+
+ RD::RenderPrimitive primitive_rd = uses_point_size ? RD::RENDER_PRIMITIVE_POINTS : primitive_rd_table[p_pipeline_key.primitive_type];
+
+ RD::PipelineRasterizationState raster_state;
+ raster_state.cull_mode = p_pipeline_key.cull_mode;
+ raster_state.wireframe = wireframe || p_pipeline_key.wireframe;
+
+ RD::PipelineMultisampleState multisample_state;
+ multisample_state.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_pipeline_key.framebuffer_format_id, 0);
+
+ RD::PipelineColorBlendState blend_state;
+ if (uses_alpha || uses_blend_alpha) {
+ // These flags should only go through if we have some form of MSAA.
+ if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE) {
+ multisample_state.enable_alpha_to_coverage = true;
+ } else if (alpha_antialiasing_mode == ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE) {
+ multisample_state.enable_alpha_to_coverage = true;
+ multisample_state.enable_alpha_to_one = true;
+ }
+
+ if (p_pipeline_key.version == SHADER_VERSION_COLOR_PASS || p_pipeline_key.version == SHADER_VERSION_COLOR_PASS_MULTIVIEW || p_pipeline_key.version == SHADER_VERSION_LIGHTMAP_COLOR_PASS || p_pipeline_key.version == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) {
+ blend_state = blend_state_blend;
+ if (depth_draw == DEPTH_DRAW_OPAQUE && !uses_alpha_clip) {
+ // Alpha does not write to depth.
+ depth_stencil_state.enable_depth_write = false;
}
+ } else if (p_pipeline_key.version == SHADER_VERSION_SHADOW_PASS || p_pipeline_key.version == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || p_pipeline_key.version == SHADER_VERSION_SHADOW_PASS_DP) {
+ // Contains nothing.
+ } else if (p_pipeline_key.version == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
+ // Writes to normal and roughness in opaque way.
+ blend_state = RD::PipelineColorBlendState::create_disabled(5);
+ } else {
+ // Do not use this version (error case).
+ }
+ } else {
+ if (p_pipeline_key.version == SHADER_VERSION_COLOR_PASS || p_pipeline_key.version == SHADER_VERSION_COLOR_PASS_MULTIVIEW || p_pipeline_key.version == SHADER_VERSION_LIGHTMAP_COLOR_PASS || p_pipeline_key.version == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) {
+ blend_state = blend_state_opaque;
+ } else if (p_pipeline_key.version == SHADER_VERSION_SHADOW_PASS || p_pipeline_key.version == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || p_pipeline_key.version == SHADER_VERSION_SHADOW_PASS_DP) {
+ // Contains nothing.
+ } else if (p_pipeline_key.version == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
+ // Writes to normal and roughness in opaque way.
+ blend_state = RD::PipelineColorBlendState::create_disabled(5);
+ } else {
+ // Unknown pipeline version.
}
}
- valid = true;
+ // Convert the specialization from the key to pipeline specialization constants.
+ Vector<RD::PipelineSpecializationConstant> specialization_constants;
+ RD::PipelineSpecializationConstant sc;
+ sc.constant_id = 0;
+ sc.int_value = p_pipeline_key.shader_specialization.packed_0;
+ sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT;
+ specialization_constants.push_back(sc);
+
+ sc.constant_id = 1;
+ sc.float_value = p_pipeline_key.shader_specialization.packed_1;
+ sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT;
+ specialization_constants.push_back(sc);
+
+ RID shader_rid = get_shader_variant(p_pipeline_key.version, p_pipeline_key.ubershader);
+ ERR_FAIL_COND(shader_rid.is_null());
+
+ RID pipeline = RD::get_singleton()->render_pipeline_create(shader_rid, p_pipeline_key.framebuffer_format_id, p_pipeline_key.vertex_format_id, primitive_rd, raster_state, multisample_state, depth_stencil_state, blend_state, 0, p_pipeline_key.render_pass, specialization_constants);
+ ERR_FAIL_COND(pipeline.is_null());
+
+ pipeline_hash_map.add_compiled_pipeline(p_pipeline_key.hash(), pipeline);
}
-bool SceneShaderForwardMobile::ShaderData::is_animated() const {
- return (uses_fragment_time && uses_discard) || (uses_vertex_time && uses_vertex);
+RD::PolygonCullMode SceneShaderForwardMobile::ShaderData::get_cull_mode_from_cull_variant(CullVariant p_cull_variant) {
+ const RD::PolygonCullMode cull_mode_rd_table[CULL_VARIANT_MAX][3] = {
+ { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_FRONT, RD::POLYGON_CULL_BACK },
+ { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_BACK, RD::POLYGON_CULL_FRONT },
+ { RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED, RD::POLYGON_CULL_DISABLED }
+ };
+
+ return cull_mode_rd_table[p_cull_variant][cull_mode];
}
-bool SceneShaderForwardMobile::ShaderData::casts_shadows() const {
- bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture;
- bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing)) || has_read_screen_alpha;
- bool has_alpha = has_base_alpha || uses_blend_alpha;
+void SceneShaderForwardMobile::ShaderData::_clear_vertex_input_mask_cache() {
+ for (uint32_t i = 0; i < VERTEX_INPUT_MASKS_SIZE; i++) {
+ vertex_input_masks[i].store(0);
+ }
+}
- return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED));
+RID SceneShaderForwardMobile::ShaderData::get_shader_variant(ShaderVersion p_shader_version, bool p_ubershader) const {
+ if (version.is_valid()) {
+ MutexLock lock(SceneShaderForwardMobile::singleton_mutex);
+ ERR_FAIL_NULL_V(SceneShaderForwardMobile::singleton, RID());
+ return SceneShaderForwardMobile::singleton->shader.version_get_shader(version, p_shader_version + (p_ubershader ? SHADER_VERSION_MAX : 0));
+ } else {
+ return RID();
+ }
}
-RS::ShaderNativeSourceCode SceneShaderForwardMobile::ShaderData::get_native_source_code() const {
- SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton;
+uint64_t SceneShaderForwardMobile::ShaderData::get_vertex_input_mask(ShaderVersion p_shader_version, bool p_ubershader) {
+ // Vertex input masks require knowledge of the shader. Since querying the shader can be expensive due to high contention and the necessary mutex, we cache the result instead.
+ uint32_t input_mask_index = p_shader_version + (p_ubershader ? SHADER_VERSION_MAX : 0);
+ uint64_t input_mask = vertex_input_masks[input_mask_index].load(std::memory_order_relaxed);
+ if (input_mask == 0) {
+ RID shader_rid = get_shader_variant(p_shader_version, p_ubershader);
+ ERR_FAIL_COND_V(shader_rid.is_null(), 0);
+
+ input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(shader_rid);
+ vertex_input_masks[input_mask_index].store(input_mask, std::memory_order_relaxed);
+ }
- return shader_singleton->shader.version_get_native_source_code(version);
+ return input_mask;
+}
+
+bool SceneShaderForwardMobile::ShaderData::is_valid() const {
+ if (version.is_valid()) {
+ MutexLock lock(SceneShaderForwardMobile::singleton_mutex);
+ ERR_FAIL_NULL_V(SceneShaderForwardMobile::singleton, false);
+ return SceneShaderForwardMobile::singleton->shader.version_is_valid(version);
+ } else {
+ return false;
+ }
}
SceneShaderForwardMobile::ShaderData::ShaderData() :
shader_list_element(this) {
+ pipeline_hash_map.set_creation_object_and_function(this, &ShaderData::_create_pipeline);
+ pipeline_hash_map.set_compilations(SceneShaderForwardMobile::singleton->pipeline_compilations, &SceneShaderForwardMobile::singleton_mutex);
}
SceneShaderForwardMobile::ShaderData::~ShaderData() {
- SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton;
- ERR_FAIL_NULL(shader_singleton);
- //pipeline variants will clear themselves if shader is gone
+ pipeline_hash_map.clear_pipelines();
+
if (version.is_valid()) {
- shader_singleton->shader.version_free(version);
+ MutexLock lock(SceneShaderForwardMobile::singleton_mutex);
+ ERR_FAIL_NULL(SceneShaderForwardMobile::singleton);
+ SceneShaderForwardMobile::singleton->shader.version_free(version);
}
}
RendererRD::MaterialStorage::ShaderData *SceneShaderForwardMobile::_create_shader_func() {
+ MutexLock lock(SceneShaderForwardMobile::singleton_mutex);
ShaderData *shader_data = memnew(ShaderData);
singleton->shader_list.add(&shader_data->shader_list_element);
return shader_data;
@@ -407,9 +418,12 @@ void SceneShaderForwardMobile::MaterialData::set_next_pass(RID p_pass) {
}
bool SceneShaderForwardMobile::MaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
- SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton;
-
- return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardMobile::MATERIAL_UNIFORM_SET, true, true);
+ if (shader_data->version.is_valid()) {
+ MutexLock lock(SceneShaderForwardMobile::singleton_mutex);
+ return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, SceneShaderForwardMobile::singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardMobile::MATERIAL_UNIFORM_SET, true, true);
+ } else {
+ return false;
+ }
}
SceneShaderForwardMobile::MaterialData::~MaterialData() {
@@ -426,6 +440,7 @@ RendererRD::MaterialStorage::MaterialData *SceneShaderForwardMobile::_create_mat
/* Scene Shader */
SceneShaderForwardMobile *SceneShaderForwardMobile::singleton = nullptr;
+Mutex SceneShaderForwardMobile::singleton_mutex;
SceneShaderForwardMobile::SceneShaderForwardMobile() {
// there should be only one of these, contained within our RenderForwardMobile singleton.
@@ -439,23 +454,29 @@ void SceneShaderForwardMobile::init(const String p_defines) {
{
Vector<String> shader_versions;
- shader_versions.push_back(""); // SHADER_VERSION_COLOR_PASS
- shader_versions.push_back("\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_SHADOW_PASS, should probably change this to MODE_RENDER_SHADOW because we don't have a depth pass here...
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); // SHADER_VERSION_SHADOW_PASS_DP
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL
-
- // multiview versions of our shaders
- shader_versions.push_back("\n#define USE_MULTIVIEW\n"); // SHADER_VERSION_COLOR_PASS_MULTIVIEW
- shader_versions.push_back("\n#define USE_MULTIVIEW\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW
- shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_SHADOW_PASS_MULTIVIEW
+ for (uint32_t ubershader = 0; ubershader < 2; ubershader++) {
+ const String base_define = ubershader ? "\n#define UBERSHADER\n" : "";
+ shader_versions.push_back(base_define + ""); // SHADER_VERSION_COLOR_PASS
+ shader_versions.push_back(base_define + "\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS
+ shader_versions.push_back(base_define + "\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_SHADOW_PASS, should probably change this to MODE_RENDER_SHADOW because we don't have a depth pass here...
+ shader_versions.push_back(base_define + "\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); // SHADER_VERSION_SHADOW_PASS_DP
+ shader_versions.push_back(base_define + "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL
+
+ // Multiview versions of our shaders.
+ shader_versions.push_back(base_define + "\n#define USE_MULTIVIEW\n"); // SHADER_VERSION_COLOR_PASS_MULTIVIEW
+ shader_versions.push_back(base_define + "\n#define USE_MULTIVIEW\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW
+ shader_versions.push_back(base_define + "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_SHADOW_PASS_MULTIVIEW
+ }
shader.initialize(shader_versions, p_defines);
if (!RendererCompositorRD::get_singleton()->is_xr_enabled()) {
- shader.set_variant_enabled(SHADER_VERSION_COLOR_PASS_MULTIVIEW, false);
- shader.set_variant_enabled(SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW, false);
- shader.set_variant_enabled(SHADER_VERSION_SHADOW_PASS_MULTIVIEW, false);
+ for (uint32_t ubershader = 0; ubershader < 2; ubershader++) {
+ uint32_t base_variant = ubershader ? SHADER_VERSION_MAX : 0;
+ shader.set_variant_enabled(base_variant + SHADER_VERSION_COLOR_PASS_MULTIVIEW, false);
+ shader.set_variant_enabled(base_variant + SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW, false);
+ shader.set_variant_enabled(base_variant + SHADER_VERSION_SHADOW_PASS_MULTIVIEW, false);
+ }
}
}
@@ -759,19 +780,23 @@ void fragment() {
}
}
-void SceneShaderForwardMobile::set_default_specialization_constants(const Vector<RD::PipelineSpecializationConstant> &p_constants) {
- default_specialization_constants = p_constants;
+void SceneShaderForwardMobile::set_default_specialization(const ShaderSpecialization &p_specialization) {
+ default_specialization = p_specialization;
+
for (SelfList<ShaderData> *E = shader_list.first(); E; E = E->next()) {
- for (int i = 0; i < ShaderData::CULL_VARIANT_MAX; i++) {
- for (int j = 0; j < RS::PRIMITIVE_MAX; j++) {
- for (int k = 0; k < SHADER_VERSION_MAX; k++) {
- E->self()->pipelines[i][j][k].update_specialization_constants(default_specialization_constants);
- }
- }
- }
+ E->self()->pipeline_hash_map.clear_pipelines();
}
}
+uint32_t SceneShaderForwardMobile::get_pipeline_compilations(RS::PipelineSource p_source) {
+ MutexLock lock(SceneShaderForwardMobile::singleton_mutex);
+ return pipeline_compilations[p_source];
+}
+
+bool SceneShaderForwardMobile::is_multiview_enabled() const {
+ return shader.is_variant_enabled(SHADER_VERSION_COLOR_PASS_MULTIVIEW);
+}
+
SceneShaderForwardMobile::~SceneShaderForwardMobile() {
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
index 833b06c1e3..c1095d29dc 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
@@ -31,6 +31,7 @@
#ifndef SCENE_SHADER_FORWARD_MOBILE_H
#define SCENE_SHADER_FORWARD_MOBILE_H
+#include "servers/rendering/renderer_rd/pipeline_hash_map_rd.h"
#include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
#include "servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl.gen.h"
@@ -39,6 +40,7 @@ namespace RendererSceneRenderImplementation {
class SceneShaderForwardMobile {
private:
static SceneShaderForwardMobile *singleton;
+ static Mutex singleton_mutex;
public:
enum ShaderVersion {
@@ -55,16 +57,52 @@ public:
SHADER_VERSION_MAX
};
- struct ShaderData : public RendererRD::MaterialStorage::ShaderData {
- enum BlendMode { //used internally
- BLEND_MODE_MIX,
- BLEND_MODE_ADD,
- BLEND_MODE_SUB,
- BLEND_MODE_MUL,
- BLEND_MODE_PREMULT_ALPHA,
- BLEND_MODE_ALPHA_TO_COVERAGE
+ struct ShaderSpecialization {
+ union {
+ struct {
+ uint32_t use_light_projector : 1;
+ uint32_t use_light_soft_shadows : 1;
+ uint32_t use_directional_soft_shadows : 1;
+ uint32_t decal_use_mipmaps : 1;
+ uint32_t projector_use_mipmaps : 1;
+ uint32_t disable_omni_lights : 1;
+ uint32_t disable_spot_lights : 1;
+ uint32_t disable_reflection_probes : 1;
+ uint32_t disable_directional_lights : 1;
+ uint32_t disable_decals : 1;
+ uint32_t disable_fog : 1;
+ uint32_t use_depth_fog : 1;
+ uint32_t is_multimesh : 1;
+ uint32_t use_lightmap_bicubic_filter : 1;
+ uint32_t pad : 2;
+ uint32_t soft_shadow_samples : 4;
+ uint32_t penumbra_shadow_samples : 4;
+ uint32_t directional_soft_shadow_samples : 4;
+ uint32_t directional_penumbra_shadow_samples : 4;
+ };
+
+ uint32_t packed_0;
+ };
+
+ union {
+ float luminance_multiplier;
+ float packed_1;
};
+ uint32_t packed_2;
+ };
+
+ struct UbershaderConstants {
+ union {
+ struct {
+ uint32_t cull_mode : 2;
+ };
+
+ uint32_t packed_0;
+ };
+ };
+
+ struct ShaderData : public RendererRD::MaterialStorage::ShaderData {
enum DepthDraw {
DEPTH_DRAW_DISABLED,
DEPTH_DRAW_OPAQUE,
@@ -96,10 +134,40 @@ public:
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE
};
- bool valid = false;
+ struct PipelineKey {
+ RD::VertexFormatID vertex_format_id;
+ RD::FramebufferFormatID framebuffer_format_id;
+ RD::PolygonCullMode cull_mode = RD::POLYGON_CULL_MAX;
+ RS::PrimitiveType primitive_type = RS::PRIMITIVE_MAX;
+ ShaderSpecialization shader_specialization = {};
+ ShaderVersion version = SHADER_VERSION_MAX;
+ uint32_t render_pass = 0;
+ uint32_t wireframe = false;
+ uint32_t ubershader = false;
+
+ uint32_t hash() const {
+ uint32_t h = hash_murmur3_one_32(vertex_format_id);
+ h = hash_murmur3_one_32(framebuffer_format_id, h);
+ h = hash_murmur3_one_32(cull_mode, h);
+ h = hash_murmur3_one_32(primitive_type, h);
+ h = hash_murmur3_one_32(shader_specialization.packed_0, h);
+ h = hash_murmur3_one_float(shader_specialization.packed_1, h);
+ h = hash_murmur3_one_32(shader_specialization.packed_2, h);
+ h = hash_murmur3_one_32(version, h);
+ h = hash_murmur3_one_32(render_pass, h);
+ h = hash_murmur3_one_32(wireframe, h);
+ h = hash_murmur3_one_32(ubershader, h);
+ return hash_fmix32(h);
+ }
+ };
+
+ void _create_pipeline(PipelineKey p_pipeline_key);
+ PipelineHashMapRD<PipelineKey, ShaderData, void (ShaderData::*)(PipelineKey)> pipeline_hash_map;
+
RID version;
- uint64_t vertex_input_mask = 0;
- PipelineCacheRD pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][SHADER_VERSION_MAX];
+
+ static const uint32_t VERTEX_INPUT_MASKS_SIZE = SHADER_VERSION_MAX * 2;
+ std::atomic<uint64_t> vertex_input_masks[VERTEX_INPUT_MASKS_SIZE] = {};
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
@@ -111,6 +179,11 @@ public:
DepthDraw depth_draw;
DepthTest depth_test;
+ int blend_mode = BLEND_MODE_MIX;
+ int depth_testi = DEPTH_TEST_ENABLED;
+ int alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF;
+ int cull_mode = CULL_BACK;
+
bool uses_point_size = false;
bool uses_alpha = false;
bool uses_blend_alpha = false;
@@ -122,6 +195,8 @@ public:
bool uses_normal = false;
bool uses_tangent = false;
bool uses_particle_trails = false;
+ bool uses_normal_map = false;
+ bool wireframe = false;
bool unshaded = false;
bool uses_vertex = false;
@@ -140,10 +215,35 @@ public:
uint64_t last_pass = 0;
uint32_t index = 0;
+ _FORCE_INLINE_ bool uses_alpha_pass() const {
+ bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture;
+ bool has_base_alpha = (uses_alpha && (!uses_alpha_clip || uses_alpha_antialiasing));
+ bool has_blend_alpha = uses_blend_alpha;
+ bool has_alpha = has_base_alpha || has_blend_alpha;
+ bool no_depth_draw = depth_draw == DEPTH_DRAW_DISABLED;
+ bool no_depth_test = depth_test == DEPTH_TEST_DISABLED;
+ return has_alpha || has_read_screen_alpha || no_depth_draw || no_depth_test;
+ }
+
+ _FORCE_INLINE_ bool uses_depth_in_alpha_pass() const {
+ bool no_depth_draw = depth_draw == DEPTH_DRAW_DISABLED;
+ bool no_depth_test = depth_test == DEPTH_TEST_DISABLED;
+ return (uses_depth_prepass_alpha || uses_alpha_antialiasing) && !(no_depth_draw || no_depth_test);
+ }
+
+ _FORCE_INLINE_ bool uses_shared_shadow_material() const {
+ return !uses_particle_trails && !writes_modelview_or_projection && !uses_vertex && !uses_discard && !uses_depth_prepass_alpha && !uses_alpha_clip && !uses_alpha_antialiasing && !uses_world_coordinates;
+ }
+
virtual void set_code(const String &p_Code);
virtual bool is_animated() const;
virtual bool casts_shadows() const;
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
+ RD::PolygonCullMode get_cull_mode_from_cull_variant(CullVariant p_cull_variant);
+ void _clear_vertex_input_mask_cache();
+ RID get_shader_variant(ShaderVersion p_shader_version, bool p_ubershader) const;
+ uint64_t get_vertex_input_mask(ShaderVersion p_shader_version, bool p_ubershader);
+ bool is_valid() const;
SelfList<ShaderData> shader_list_element;
@@ -204,10 +304,14 @@ public:
SceneShaderForwardMobile();
~SceneShaderForwardMobile();
- Vector<RD::PipelineSpecializationConstant> default_specialization_constants;
+ ShaderSpecialization default_specialization = {};
+
+ uint32_t pipeline_compilations[RS::PIPELINE_SOURCE_MAX] = {};
void init(const String p_defines);
- void set_default_specialization_constants(const Vector<RD::PipelineSpecializationConstant> &p_constants);
+ void set_default_specialization(const ShaderSpecialization &p_specialization);
+ uint32_t get_pipeline_compilations(RS::PipelineSource p_source);
+ bool is_multiview_enabled() const;
};
} // namespace RendererSceneRenderImplementation
diff --git a/servers/rendering/renderer_rd/pipeline_cache_rd.cpp b/servers/rendering/renderer_rd/pipeline_cache_rd.cpp
index c00e5f8b5e..ed03143812 100644
--- a/servers/rendering/renderer_rd/pipeline_cache_rd.cpp
+++ b/servers/rendering/renderer_rd/pipeline_cache_rd.cpp
@@ -89,7 +89,6 @@ void PipelineCacheRD::setup(RID p_shader, RD::RenderPrimitive p_primitive, const
ERR_FAIL_COND(p_shader.is_null());
_clear();
shader = p_shader;
- input_mask = 0;
render_primitive = p_primitive;
rasterization_state = p_rasterization_state;
multisample_state = p_multisample;
@@ -112,13 +111,11 @@ void PipelineCacheRD::update_shader(RID p_shader) {
void PipelineCacheRD::clear() {
_clear();
shader = RID(); //clear shader
- input_mask = 0;
}
PipelineCacheRD::PipelineCacheRD() {
version_count = 0;
versions = nullptr;
- input_mask = 0;
}
PipelineCacheRD::~PipelineCacheRD() {
diff --git a/servers/rendering/renderer_rd/pipeline_cache_rd.h b/servers/rendering/renderer_rd/pipeline_cache_rd.h
index 0ebebd0540..64e6b5078a 100644
--- a/servers/rendering/renderer_rd/pipeline_cache_rd.h
+++ b/servers/rendering/renderer_rd/pipeline_cache_rd.h
@@ -38,7 +38,6 @@ class PipelineCacheRD {
SpinLock spin_lock;
RID shader;
- uint64_t input_mask;
RD::RenderPrimitive render_primitive;
RD::PipelineRasterizationState rasterization_state;
@@ -92,11 +91,8 @@ public:
}
_FORCE_INLINE_ uint64_t get_vertex_input_mask() {
- if (input_mask == 0) {
- ERR_FAIL_COND_V(shader.is_null(), 0);
- input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(shader);
- }
- return input_mask;
+ ERR_FAIL_COND_V(shader.is_null(), 0);
+ return RD::get_singleton()->shader_get_vertex_input_attribute_mask(shader);
}
void clear();
PipelineCacheRD();
diff --git a/servers/rendering/renderer_rd/pipeline_hash_map_rd.h b/servers/rendering/renderer_rd/pipeline_hash_map_rd.h
new file mode 100644
index 0000000000..b76f8ae5e6
--- /dev/null
+++ b/servers/rendering/renderer_rd/pipeline_hash_map_rd.h
@@ -0,0 +1,218 @@
+/**************************************************************************/
+/* pipeline_hash_map_rd.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 PIPELINE_HASH_MAP_RD_H
+#define PIPELINE_HASH_MAP_RD_H
+
+#include "servers/rendering/rendering_device.h"
+#include "servers/rendering_server.h"
+
+#define PRINT_PIPELINE_COMPILATION_KEYS 0
+
+template <typename Key, typename CreationClass, typename CreationFunction>
+class PipelineHashMapRD {
+private:
+ CreationClass *creation_object = nullptr;
+ CreationFunction creation_function = nullptr;
+ Mutex *compilations_mutex = nullptr;
+ uint32_t *compilations = nullptr;
+ RBMap<uint32_t, RID> hash_map;
+ LocalVector<Pair<uint32_t, RID>> compiled_queue;
+ Mutex compiled_queue_mutex;
+ HashMap<uint32_t, WorkerThreadPool::TaskID> compilation_tasks;
+ Mutex local_mutex;
+
+ bool _add_new_pipelines_to_map() {
+ thread_local Vector<uint32_t> hashes_added;
+ hashes_added.clear();
+
+ {
+ MutexLock lock(compiled_queue_mutex);
+ for (const Pair<uint32_t, RID> &pair : compiled_queue) {
+ hash_map[pair.first] = pair.second;
+ hashes_added.push_back(pair.first);
+ }
+
+ compiled_queue.clear();
+ }
+
+ {
+ MutexLock local_lock(local_mutex);
+ for (uint32_t hash : hashes_added) {
+ HashMap<uint32_t, WorkerThreadPool::TaskID>::Iterator task_it = compilation_tasks.find(hash);
+ if (task_it != compilation_tasks.end()) {
+ compilation_tasks.remove(task_it);
+ }
+ }
+ }
+
+ return !hashes_added.is_empty();
+ }
+
+ void _wait_for_compilation() {
+ MutexLock local_lock(local_mutex);
+ for (KeyValue<uint32_t, WorkerThreadPool::TaskID> key_value : compilation_tasks) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(key_value.value);
+ }
+ }
+
+public:
+ void add_compiled_pipeline(uint32_t p_hash, RID p_pipeline) {
+ compiled_queue_mutex.lock();
+ compiled_queue.push_back({ p_hash, p_pipeline });
+ compiled_queue_mutex.unlock();
+ }
+
+ // Start compilation of a pipeline ahead of time in the background. Returns true if the compilation was started, false if it wasn't required. Source is only used for collecting statistics.
+ bool compile_pipeline(const Key &p_key, uint32_t p_key_hash, RS::PipelineSource p_source) {
+ DEV_ASSERT((creation_object != nullptr) && (creation_function != nullptr) && "Creation object and function was not set before attempting to compile a pipeline.");
+
+ // Check if the pipeline was already compiled.
+ compiled_queue_mutex.lock();
+ bool already_exists = hash_map.has(p_key_hash);
+ compiled_queue_mutex.unlock();
+ if (already_exists) {
+ return false;
+ }
+
+ // Check if the pipeline is currently being compiled.
+ MutexLock local_lock(local_mutex);
+ if (compilation_tasks.has(p_key_hash)) {
+ return false;
+ }
+
+ if (compilations_mutex != nullptr) {
+ MutexLock compilations_lock(*compilations_mutex);
+ compilations[p_source]++;
+ }
+
+#if PRINT_PIPELINE_COMPILATION_KEYS
+ String source_name = "UNKNOWN";
+ switch (p_source) {
+ case RS::PIPELINE_SOURCE_CANVAS:
+ source_name = "CANVAS";
+ break;
+ case RS::PIPELINE_SOURCE_MESH:
+ source_name = "MESH";
+ break;
+ case RS::PIPELINE_SOURCE_SURFACE:
+ source_name = "SURFACE";
+ break;
+ case RS::PIPELINE_SOURCE_DRAW:
+ source_name = "DRAW";
+ break;
+ case RS::PIPELINE_SOURCE_SPECIALIZATION:
+ source_name = "SPECIALIZATION";
+ break;
+ }
+
+ print_line("HASH:", p_key_hash, "SOURCE:", source_name);
+#endif
+
+ // Queue a background compilation task.
+ WorkerThreadPool::TaskID task_id = WorkerThreadPool::get_singleton()->add_template_task(creation_object, creation_function, p_key, false, "PipelineCompilation");
+ compilation_tasks.insert(p_key_hash, task_id);
+
+ return true;
+ }
+
+ // Retrieve a pipeline. It'll return an empty pipeline if it's not available yet, but it'll be guaranteed to succeed if 'wait for compilation' is true and stall as necessary. Source is just an optional number to aid debugging.
+ RID get_pipeline(const Key &p_key, uint32_t p_key_hash, bool p_wait_for_compilation, RS::PipelineSource p_source) {
+ RBMap<uint32_t, RID>::Element *e = hash_map.find(p_key_hash);
+
+ if (e == nullptr) {
+ // Check if there's any new pipelines that need to be added and try again. This method triggers a mutex lock.
+ if (_add_new_pipelines_to_map()) {
+ e = hash_map.find(p_key_hash);
+ }
+ }
+
+ if (e == nullptr) {
+ // Lock access to the compilation maps.
+ MutexLock local_lock(local_mutex);
+
+ // Request compilation. The method will ignore the request if it's already being compiled.
+ compile_pipeline(p_key, p_key_hash, p_source);
+
+ if (p_wait_for_compilation) {
+ if (compilation_tasks.has(p_key_hash)) {
+ // If a background compilation task was used, wait for it.
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(compilation_tasks[p_key_hash]);
+ }
+
+ _add_new_pipelines_to_map();
+
+ e = hash_map.find(p_key_hash);
+ if (e != nullptr) {
+ return e->value();
+ } else {
+ // Pipeline could not be compiled due to an internal error. Store an empty RID so compilation is not attempted again.
+ hash_map[p_key_hash] = RID();
+ return RID();
+ }
+ } else {
+ return RID();
+ }
+ } else {
+ return e->value();
+ }
+ }
+
+ // Delete all cached pipelines. Can stall if background compilation is in progress.
+ void clear_pipelines() {
+ _wait_for_compilation();
+ _add_new_pipelines_to_map();
+
+ for (KeyValue<uint32_t, RID> entry : hash_map) {
+ RD::get_singleton()->free(entry.value);
+ }
+
+ hash_map.clear();
+ }
+
+ // Set the external pipeline compilations array to increase the counters on every time a pipeline is compiled.
+ void set_compilations(uint32_t *p_compilations, Mutex *p_compilations_mutex) {
+ compilations = p_compilations;
+ compilations_mutex = p_compilations_mutex;
+ }
+
+ void set_creation_object_and_function(CreationClass *p_creation_object, CreationFunction p_creation_function) {
+ creation_object = p_creation_object;
+ creation_function = p_creation_function;
+ }
+
+ PipelineHashMapRD() {}
+
+ ~PipelineHashMapRD() {
+ clear_pipelines();
+ }
+};
+
+#endif // PIPELINE_HASH_MAP_RD_H
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 5441e28be0..979f590c4c 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -351,6 +351,23 @@ void RendererCanvasRenderRD::free_polygon(PolygonID p_polygon) {
////////////////////
+static RD::RenderPrimitive _primitive_type_to_render_primitive(RS::PrimitiveType p_primitive) {
+ switch (p_primitive) {
+ case RS::PRIMITIVE_POINTS:
+ return RD::RENDER_PRIMITIVE_POINTS;
+ case RS::PRIMITIVE_LINES:
+ return RD::RENDER_PRIMITIVE_LINES;
+ case RS::PRIMITIVE_LINE_STRIP:
+ return RD::RENDER_PRIMITIVE_LINESTRIPS;
+ case RS::PRIMITIVE_TRIANGLES:
+ return RD::RENDER_PRIMITIVE_TRIANGLES;
+ case RS::PRIMITIVE_TRIANGLE_STRIP:
+ return RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS;
+ default:
+ return RD::RENDER_PRIMITIVE_MAX;
+ }
+}
+
_FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primitive, uint32_t p_indices) {
static const uint32_t divisor[RS::PRIMITIVE_MAX] = { 1, 2, 1, 3, 1 };
static const uint32_t subtractor[RS::PRIMITIVE_MAX] = { 0, 0, 1, 0, 1 };
@@ -450,6 +467,42 @@ RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, boo
return uniform_set;
}
+RID RendererCanvasRenderRD::_get_pipeline_specialization_or_ubershader(CanvasShaderData *p_shader_data, PipelineKey &r_pipeline_key, PushConstant &r_push_constant, RID p_mesh_instance, void *p_surface, uint32_t p_surface_index, RID *r_vertex_array) {
+ r_pipeline_key.ubershader = 0;
+
+ const uint32_t ubershader_iterations = 1;
+ while (r_pipeline_key.ubershader < ubershader_iterations) {
+ if (r_vertex_array != nullptr) {
+ RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
+ uint64_t input_mask = p_shader_data->get_vertex_input_mask(r_pipeline_key.variant, r_pipeline_key.ubershader);
+ if (p_mesh_instance.is_valid()) {
+ mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(p_mesh_instance, p_surface_index, input_mask, false, *r_vertex_array, r_pipeline_key.vertex_format_id);
+ } else {
+ mesh_storage->mesh_surface_get_vertex_arrays_and_format(p_surface, input_mask, false, *r_vertex_array, r_pipeline_key.vertex_format_id);
+ }
+ }
+
+ if (r_pipeline_key.ubershader) {
+ r_push_constant.shader_specialization = r_pipeline_key.shader_specialization;
+ r_pipeline_key.shader_specialization = {};
+ } else {
+ r_push_constant.shader_specialization = {};
+ }
+
+ bool wait_for_compilation = r_pipeline_key.ubershader || ubershader_iterations == 1;
+ RS::PipelineSource source = RS::PIPELINE_SOURCE_CANVAS;
+ RID pipeline = p_shader_data->pipeline_hash_map.get_pipeline(r_pipeline_key, r_pipeline_key.hash(), wait_for_compilation, source);
+ if (pipeline.is_valid()) {
+ return pipeline;
+ }
+
+ r_pipeline_key.ubershader++;
+ }
+
+ // This case should never be reached unless the shader wasn't available.
+ return RID();
+}
+
void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
@@ -717,7 +770,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
if (material.is_valid()) {
CanvasMaterialData *md = static_cast<CanvasMaterialData *>(material_storage->material_get_data(material, RendererRD::MaterialStorage::SHADER_TYPE_2D));
- if (md && md->shader_data->valid) {
+ if (md && md->shader_data->is_valid()) {
if (md->shader_data->uses_screen_texture && canvas_group_owner == nullptr) {
if (!material_screen_texture_cached) {
backbuffer_copy = true;
@@ -1355,17 +1408,75 @@ void RendererCanvasRenderRD::occluder_polygon_set_cull_mode(RID p_occluder, RS::
oc->cull_mode = p_mode;
}
+void RendererCanvasRenderRD::CanvasShaderData::_clear_vertex_input_mask_cache() {
+ for (uint32_t i = 0; i < VERTEX_INPUT_MASKS_SIZE; i++) {
+ vertex_input_masks[i].store(0);
+ }
+}
+
+void RendererCanvasRenderRD::CanvasShaderData::_create_pipeline(PipelineKey p_pipeline_key) {
+#if PRINT_PIPELINE_COMPILATION_KEYS
+ print_line(
+ "HASH:", p_pipeline_key.hash(),
+ "VERSION:", version,
+ "VARIANT:", p_pipeline_key.variant,
+ "FRAMEBUFFER:", p_pipeline_key.framebuffer_format_id,
+ "VERTEX:", p_pipeline_key.vertex_format_id,
+ "PRIMITIVE:", p_pipeline_key.render_primitive,
+ "SPEC PACKED #0:", p_pipeline_key.shader_specialization.packed_0,
+ "LCD:", p_pipeline_key.lcd_blend);
+#endif
+
+ RendererRD::MaterialStorage::ShaderData::BlendMode blend_mode_rd = RendererRD::MaterialStorage::ShaderData::BlendMode(blend_mode);
+ RD::PipelineColorBlendState blend_state;
+ RD::PipelineColorBlendState::Attachment attachment;
+ uint32_t dynamic_state_flags = 0;
+ if (p_pipeline_key.lcd_blend) {
+ attachment.enable_blend = true;
+ attachment.alpha_blend_op = RD::BLEND_OP_ADD;
+ attachment.color_blend_op = RD::BLEND_OP_ADD;
+ attachment.src_color_blend_factor = RD::BLEND_FACTOR_CONSTANT_COLOR;
+ attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
+ attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ dynamic_state_flags = RD::DYNAMIC_STATE_BLEND_CONSTANTS;
+ } else {
+ attachment = RendererRD::MaterialStorage::ShaderData::blend_mode_to_blend_attachment(blend_mode_rd);
+ }
+
+ blend_state.attachments.push_back(attachment);
+
+ RD::PipelineMultisampleState multisample_state;
+ multisample_state.sample_count = RD::get_singleton()->framebuffer_format_get_texture_samples(p_pipeline_key.framebuffer_format_id, 0);
+
+ // Convert the specialization from the key to pipeline specialization constants.
+ Vector<RD::PipelineSpecializationConstant> specialization_constants;
+ RD::PipelineSpecializationConstant sc;
+ sc.constant_id = 0;
+ sc.int_value = p_pipeline_key.shader_specialization.packed_0;
+ sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT;
+ specialization_constants.push_back(sc);
+
+ RID shader_rid = get_shader(p_pipeline_key.variant, p_pipeline_key.ubershader);
+ ERR_FAIL_COND(shader_rid.is_null());
+
+ RID pipeline = RD::get_singleton()->render_pipeline_create(shader_rid, p_pipeline_key.framebuffer_format_id, p_pipeline_key.vertex_format_id, p_pipeline_key.render_primitive, RD::PipelineRasterizationState(), multisample_state, RD::PipelineDepthStencilState(), blend_state, dynamic_state_flags, 0, specialization_constants);
+ ERR_FAIL_COND(pipeline.is_null());
+
+ pipeline_hash_map.add_compiled_pipeline(p_pipeline_key.hash(), pipeline);
+}
+
void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) {
//compile
code = p_code;
- valid = false;
ubo_size = 0;
uniforms.clear();
uses_screen_texture = false;
uses_screen_texture_mipmaps = false;
uses_sdf = false;
uses_time = false;
+ _clear_vertex_input_mask_cache();
if (code.is_empty()) {
return; //just invalid, but no error
@@ -1373,7 +1484,7 @@ void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) {
ShaderCompiler::GeneratedCode gen_code;
- int blend_mode = BLEND_MODE_MIX;
+ blend_mode = BLEND_MODE_MIX;
ShaderCompiler::IdentifierActions actions;
actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX;
@@ -1384,7 +1495,7 @@ void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) {
actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MIX);
actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_mode, BLEND_MODE_SUB);
actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_mode, BLEND_MODE_MUL);
- actions.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&blend_mode, BLEND_MODE_PMALPHA);
+ actions.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&blend_mode, BLEND_MODE_PREMULTIPLIED_ALPHA);
actions.render_mode_values["blend_disabled"] = Pair<int *, int>(&blend_mode, BLEND_MODE_DISABLED);
actions.usage_flag_pointers["texture_sdf"] = &uses_sdf;
@@ -1393,6 +1504,7 @@ void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) {
actions.uniforms = &uniforms;
RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton);
+ MutexLock lock(canvas_singleton->shader.mutex);
Error err = canvas_singleton->shader.compiler.compile(RS::SHADER_CANVAS_ITEM, code, &actions, path, gen_code);
ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
@@ -1400,6 +1512,8 @@ void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) {
uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps;
uses_screen_texture = gen_code.uses_screen_texture;
+ pipeline_hash_map.clear_pipelines();
+
if (version.is_null()) {
version = canvas_singleton->shader.canvas_shader.version_create();
}
@@ -1422,168 +1536,70 @@ void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) {
print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]);
#endif
canvas_singleton->shader.canvas_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines);
- ERR_FAIL_COND(!canvas_singleton->shader.canvas_shader.version_is_valid(version));
ubo_size = gen_code.uniform_total_size;
ubo_offsets = gen_code.uniform_offsets;
texture_uniforms = gen_code.texture_uniforms;
+}
- //update them pipelines
-
- RD::PipelineColorBlendState::Attachment attachment;
-
- switch (blend_mode) {
- case BLEND_MODE_DISABLED: {
- // nothing to do here, disabled by default
-
- } break;
- case BLEND_MODE_MIX: {
- attachment.enable_blend = true;
- attachment.color_blend_op = RD::BLEND_OP_ADD;
- attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
-
- attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
-
- } break;
- case BLEND_MODE_ADD: {
- attachment.enable_blend = true;
- attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- attachment.color_blend_op = RD::BLEND_OP_ADD;
- attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
- attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
-
- } break;
- case BLEND_MODE_SUB: {
- attachment.enable_blend = true;
- attachment.alpha_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
- attachment.color_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
- attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
- attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+bool RendererCanvasRenderRD::CanvasShaderData::is_animated() const {
+ return false;
+}
- } break;
- case BLEND_MODE_MUL: {
- attachment.enable_blend = true;
- attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- attachment.color_blend_op = RD::BLEND_OP_ADD;
- attachment.src_color_blend_factor = RD::BLEND_FACTOR_DST_COLOR;
- attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ZERO;
- attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_DST_ALPHA;
- attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
+bool RendererCanvasRenderRD::CanvasShaderData::casts_shadows() const {
+ return false;
+}
- } break;
- case BLEND_MODE_PMALPHA: {
- attachment.enable_blend = true;
- attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- attachment.color_blend_op = RD::BLEND_OP_ADD;
- attachment.src_color_blend_factor = RD::BLEND_FACTOR_ONE;
- attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
- attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+RS::ShaderNativeSourceCode RendererCanvasRenderRD::CanvasShaderData::get_native_source_code() const {
+ RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton);
+ MutexLock lock(canvas_singleton->shader.mutex);
+ return canvas_singleton->shader.canvas_shader.version_get_native_source_code(version);
+}
- } break;
+RID RendererCanvasRenderRD::CanvasShaderData::get_shader(ShaderVariant p_shader_variant, bool p_ubershader) const {
+ if (version.is_valid()) {
+ uint32_t variant_index = p_shader_variant + (p_ubershader ? SHADER_VARIANT_MAX : 0);
+ RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton);
+ MutexLock lock(canvas_singleton->shader.mutex);
+ return canvas_singleton->shader.canvas_shader.version_get_shader(version, variant_index);
+ } else {
+ return RID();
}
+}
- RD::PipelineColorBlendState blend_state;
- blend_state.attachments.push_back(attachment);
+uint64_t RendererCanvasRenderRD::CanvasShaderData::get_vertex_input_mask(ShaderVariant p_shader_variant, bool p_ubershader) {
+ // Vertex input masks require knowledge of the shader. Since querying the shader can be expensive due to high contention and the necessary mutex, we cache the result instead.
+ uint32_t input_mask_index = p_shader_variant + (p_ubershader ? SHADER_VARIANT_MAX : 0);
+ uint64_t input_mask = vertex_input_masks[input_mask_index].load(std::memory_order_relaxed);
+ if (input_mask == 0) {
+ RID shader_rid = get_shader(p_shader_variant, p_ubershader);
+ ERR_FAIL_COND_V(shader_rid.is_null(), 0);
- RD::PipelineColorBlendState::Attachment attachment_lcd;
- attachment_lcd.enable_blend = true;
- attachment_lcd.alpha_blend_op = RD::BLEND_OP_ADD;
- attachment_lcd.color_blend_op = RD::BLEND_OP_ADD;
- attachment_lcd.src_color_blend_factor = RD::BLEND_FACTOR_CONSTANT_COLOR;
- attachment_lcd.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
- attachment_lcd.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- attachment_lcd.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
-
- RD::PipelineColorBlendState blend_state_lcd;
- blend_state_lcd.attachments.push_back(attachment_lcd);
-
- //update pipelines
-
- for (int i = 0; i < PIPELINE_LIGHT_MODE_MAX; i++) {
- for (int j = 0; j < PIPELINE_VARIANT_MAX; j++) {
- RD::RenderPrimitive primitive[PIPELINE_VARIANT_MAX] = {
- RD::RENDER_PRIMITIVE_TRIANGLES,
- RD::RENDER_PRIMITIVE_TRIANGLES,
- RD::RENDER_PRIMITIVE_TRIANGLES,
- RD::RENDER_PRIMITIVE_LINES,
- RD::RENDER_PRIMITIVE_POINTS,
- RD::RENDER_PRIMITIVE_TRIANGLES,
- RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS,
- RD::RENDER_PRIMITIVE_LINES,
- RD::RENDER_PRIMITIVE_LINESTRIPS,
- RD::RENDER_PRIMITIVE_POINTS,
- RD::RENDER_PRIMITIVE_TRIANGLES,
- };
-
- ShaderVariant shader_variants[PIPELINE_LIGHT_MODE_MAX][PIPELINE_VARIANT_MAX] = {
- {
- //non lit
- SHADER_VARIANT_QUAD,
- SHADER_VARIANT_NINEPATCH,
- SHADER_VARIANT_PRIMITIVE,
- SHADER_VARIANT_PRIMITIVE,
- SHADER_VARIANT_PRIMITIVE_POINTS,
- SHADER_VARIANT_ATTRIBUTES,
- SHADER_VARIANT_ATTRIBUTES,
- SHADER_VARIANT_ATTRIBUTES,
- SHADER_VARIANT_ATTRIBUTES,
- SHADER_VARIANT_ATTRIBUTES_POINTS,
- SHADER_VARIANT_QUAD,
- },
- {
- //lit
- SHADER_VARIANT_QUAD_LIGHT,
- SHADER_VARIANT_NINEPATCH_LIGHT,
- SHADER_VARIANT_PRIMITIVE_LIGHT,
- SHADER_VARIANT_PRIMITIVE_LIGHT,
- SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT,
- SHADER_VARIANT_ATTRIBUTES_LIGHT,
- SHADER_VARIANT_ATTRIBUTES_LIGHT,
- SHADER_VARIANT_ATTRIBUTES_LIGHT,
- SHADER_VARIANT_ATTRIBUTES_LIGHT,
- SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT,
- SHADER_VARIANT_QUAD_LIGHT,
- },
- };
-
- RID shader_variant = canvas_singleton->shader.canvas_shader.version_get_shader(version, shader_variants[i][j]);
- if (j == PIPELINE_VARIANT_QUAD_LCD_BLEND) {
- pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state_lcd, RD::DYNAMIC_STATE_BLEND_CONSTANTS);
- } else {
- pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0);
- }
- }
+ input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(shader_rid);
+ vertex_input_masks[input_mask_index].store(input_mask, std::memory_order_relaxed);
}
- valid = true;
-}
-
-bool RendererCanvasRenderRD::CanvasShaderData::is_animated() const {
- return false;
+ return input_mask;
}
-bool RendererCanvasRenderRD::CanvasShaderData::casts_shadows() const {
- return false;
+bool RendererCanvasRenderRD::CanvasShaderData::is_valid() const {
+ RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton);
+ MutexLock lock(canvas_singleton->shader.mutex);
+ return canvas_singleton->shader.canvas_shader.version_is_valid(version);
}
-RS::ShaderNativeSourceCode RendererCanvasRenderRD::CanvasShaderData::get_native_source_code() const {
+RendererCanvasRenderRD::CanvasShaderData::CanvasShaderData() {
RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton);
- return canvas_singleton->shader.canvas_shader.version_get_native_source_code(version);
+ pipeline_hash_map.set_creation_object_and_function(this, &CanvasShaderData::_create_pipeline);
+ pipeline_hash_map.set_compilations(&canvas_singleton->shader.pipeline_compilations[0], &canvas_singleton->shader.mutex);
}
RendererCanvasRenderRD::CanvasShaderData::~CanvasShaderData() {
- RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton);
- ERR_FAIL_NULL(canvas_singleton);
- //pipeline variants will clear themselves if shader is gone
+ pipeline_hash_map.clear_pipelines();
+
if (version.is_valid()) {
+ RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton);
+ MutexLock lock(canvas_singleton->shader.mutex);
canvas_singleton->shader.canvas_shader.version_free(version);
}
}
@@ -1595,8 +1611,10 @@ RendererRD::MaterialStorage::ShaderData *RendererCanvasRenderRD::_create_shader_
bool RendererCanvasRenderRD::CanvasMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton);
- bool uniform_set_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, true, false);
- bool uniform_set_srgb_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set_srgb, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, false, false);
+ MutexLock lock(canvas_singleton->shader.mutex);
+ RID shader_to_update = canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0);
+ bool uniform_set_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_to_update, MATERIAL_UNIFORM_SET, true, false);
+ bool uniform_set_srgb_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set_srgb, shader_to_update, MATERIAL_UNIFORM_SET, false, false);
return uniform_set_changed || uniform_set_srgb_changed;
}
@@ -1647,107 +1665,23 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
state.light_uniforms = memnew_arr(LightUniform, state.max_lights_per_render);
Vector<String> variants;
- //non light variants
- variants.push_back(""); //none by default is first variant
- variants.push_back("#define USE_NINEPATCH\n"); //ninepatch is the second variant
- variants.push_back("#define USE_PRIMITIVE\n"); //primitive is the third
- variants.push_back("#define USE_PRIMITIVE\n#define USE_POINT_SIZE\n"); //points need point size
- variants.push_back("#define USE_ATTRIBUTES\n"); // attributes for vertex arrays
- variants.push_back("#define USE_ATTRIBUTES\n#define USE_POINT_SIZE\n"); //attributes with point size
- //light variants
- variants.push_back("#define USE_LIGHTING\n"); //none by default is first variant
- variants.push_back("#define USE_LIGHTING\n#define USE_NINEPATCH\n"); //ninepatch is the second variant
- variants.push_back("#define USE_LIGHTING\n#define USE_PRIMITIVE\n"); //primitive is the third
- variants.push_back("#define USE_LIGHTING\n#define USE_PRIMITIVE\n#define USE_POINT_SIZE\n"); //points need point size
- variants.push_back("#define USE_LIGHTING\n#define USE_ATTRIBUTES\n"); // attributes for vertex arrays
- variants.push_back("#define USE_LIGHTING\n#define USE_ATTRIBUTES\n#define USE_POINT_SIZE\n"); //attributes with point size
+ const uint32_t ubershader_iterations = 1;
+ for (uint32_t ubershader = 0; ubershader < ubershader_iterations; ubershader++) {
+ const String base_define = ubershader ? "\n#define UBERSHADER\n" : "";
+ variants.push_back(base_define + ""); // SHADER_VARIANT_QUAD
+ variants.push_back(base_define + "#define USE_NINEPATCH\n"); // SHADER_VARIANT_NINEPATCH
+ variants.push_back(base_define + "#define USE_PRIMITIVE\n"); // SHADER_VARIANT_PRIMITIVE
+ variants.push_back(base_define + "#define USE_PRIMITIVE\n#define USE_POINT_SIZE\n"); // SHADER_VARIANT_PRIMITIVE_POINTS
+ variants.push_back(base_define + "#define USE_ATTRIBUTES\n"); // SHADER_VARIANT_ATTRIBUTES
+ variants.push_back(base_define + "#define USE_ATTRIBUTES\n#define USE_POINT_SIZE\n"); // SHADER_VARIANT_ATTRIBUTES_POINTS
+ }
shader.canvas_shader.initialize(variants, global_defines);
- shader.default_version = shader.canvas_shader.version_create();
- shader.default_version_rd_shader = shader.canvas_shader.version_get_shader(shader.default_version, SHADER_VARIANT_QUAD);
-
- RD::PipelineColorBlendState blend_state;
- RD::PipelineColorBlendState::Attachment blend_attachment;
-
- blend_attachment.enable_blend = true;
- blend_attachment.color_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
- blend_attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
-
- blend_attachment.alpha_blend_op = RD::BLEND_OP_ADD;
- blend_attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- blend_attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
-
- blend_state.attachments.push_back(blend_attachment);
-
- RD::PipelineColorBlendState::Attachment attachment_lcd;
- attachment_lcd.enable_blend = true;
- attachment_lcd.alpha_blend_op = RD::BLEND_OP_ADD;
- attachment_lcd.color_blend_op = RD::BLEND_OP_ADD;
- attachment_lcd.src_color_blend_factor = RD::BLEND_FACTOR_CONSTANT_COLOR;
- attachment_lcd.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
- attachment_lcd.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
- attachment_lcd.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
-
- RD::PipelineColorBlendState blend_state_lcd;
- blend_state_lcd.attachments.push_back(attachment_lcd);
-
- for (int i = 0; i < PIPELINE_LIGHT_MODE_MAX; i++) {
- for (int j = 0; j < PIPELINE_VARIANT_MAX; j++) {
- RD::RenderPrimitive primitive[PIPELINE_VARIANT_MAX] = {
- RD::RENDER_PRIMITIVE_TRIANGLES,
- RD::RENDER_PRIMITIVE_TRIANGLES,
- RD::RENDER_PRIMITIVE_TRIANGLES,
- RD::RENDER_PRIMITIVE_LINES,
- RD::RENDER_PRIMITIVE_POINTS,
- RD::RENDER_PRIMITIVE_TRIANGLES,
- RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS,
- RD::RENDER_PRIMITIVE_LINES,
- RD::RENDER_PRIMITIVE_LINESTRIPS,
- RD::RENDER_PRIMITIVE_POINTS,
- RD::RENDER_PRIMITIVE_TRIANGLES,
- };
-
- ShaderVariant shader_variants[PIPELINE_LIGHT_MODE_MAX][PIPELINE_VARIANT_MAX] = {
- {
- //non lit
- SHADER_VARIANT_QUAD,
- SHADER_VARIANT_NINEPATCH,
- SHADER_VARIANT_PRIMITIVE,
- SHADER_VARIANT_PRIMITIVE,
- SHADER_VARIANT_PRIMITIVE_POINTS,
- SHADER_VARIANT_ATTRIBUTES,
- SHADER_VARIANT_ATTRIBUTES,
- SHADER_VARIANT_ATTRIBUTES,
- SHADER_VARIANT_ATTRIBUTES,
- SHADER_VARIANT_ATTRIBUTES_POINTS,
- SHADER_VARIANT_QUAD,
- },
- {
- //lit
- SHADER_VARIANT_QUAD_LIGHT,
- SHADER_VARIANT_NINEPATCH_LIGHT,
- SHADER_VARIANT_PRIMITIVE_LIGHT,
- SHADER_VARIANT_PRIMITIVE_LIGHT,
- SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT,
- SHADER_VARIANT_ATTRIBUTES_LIGHT,
- SHADER_VARIANT_ATTRIBUTES_LIGHT,
- SHADER_VARIANT_ATTRIBUTES_LIGHT,
- SHADER_VARIANT_ATTRIBUTES_LIGHT,
- SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT,
- SHADER_VARIANT_QUAD_LIGHT,
- },
- };
-
- RID shader_variant = shader.canvas_shader.version_get_shader(shader.default_version, shader_variants[i][j]);
- if (j == PIPELINE_VARIANT_QUAD_LCD_BLEND) {
- shader.pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state_lcd, RD::DYNAMIC_STATE_BLEND_CONSTANTS);
- } else {
- shader.pipeline_variants.variants[i][j].setup(shader_variant, primitive[j], RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), blend_state, 0);
- }
- }
- }
+ shader.default_version_data = memnew(CanvasShaderData);
+ shader.default_version_data->version = shader.canvas_shader.version_create();
+ shader.default_version_data->blend_mode = RendererRD::MaterialStorage::ShaderData::BLEND_MODE_MIX;
+ shader.default_version_rd_shader = shader.default_version_data->get_shader(SHADER_VARIANT_QUAD, false);
}
{
@@ -2101,6 +2035,12 @@ void RendererCanvasRenderRD::set_debug_redraw(bool p_enabled, double p_time, con
debug_redraw_color = p_color;
}
+uint32_t RendererCanvasRenderRD::get_pipeline_compilations(RS::PipelineSource p_source) {
+ RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton);
+ MutexLock lock(canvas_singleton->shader.mutex);
+ return shader.pipeline_compilations[p_source];
+}
+
void RendererCanvasRenderRD::_render_batch_items(RenderTarget 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, RenderingMethod::RenderInfo *r_render_info) {
// Record batches
uint32_t instance_index = 0;
@@ -2244,12 +2184,11 @@ void RendererCanvasRenderRD::_render_batch_items(RenderTarget p_to_render_target
}
}
- PipelineVariants *pipeline_variants = &shader.pipeline_variants;
-
+ CanvasShaderData *shader_data = shader.default_version_data;
CanvasMaterialData *material_data = current_batch->material_data;
if (material_data) {
- if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) {
- pipeline_variants = &material_data->shader_data->pipeline_variants;
+ if (material_data->shader_data->version.is_valid() && material_data->shader_data->is_valid()) {
+ shader_data = material_data->shader_data;
// Update uniform set.
RID uniform_set = texture_storage->render_target_is_using_hdr(p_to_render_target.render_target) ? material_data->uniform_set : material_data->uniform_set_srgb;
if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { // Material may not have a uniform set.
@@ -2259,7 +2198,7 @@ void RendererCanvasRenderRD::_render_batch_items(RenderTarget p_to_render_target
}
}
- _render_batch(draw_list, pipeline_variants, fb_format, p_lights, current_batch, r_render_info);
+ _render_batch(draw_list, shader_data, fb_format, p_lights, current_batch, r_render_info);
}
RD::get_singleton()->draw_list_end();
@@ -2291,7 +2230,6 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
uint32_t lights[4] = { 0, 0, 0, 0 };
uint16_t light_count = 0;
- PipelineLightMode light_mode;
{
Light *light = p_lights;
@@ -2313,11 +2251,11 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT;
}
- light_mode = (light_count > 0 || using_directional_lights) ? PIPELINE_LIGHT_MODE_ENABLED : PIPELINE_LIGHT_MODE_DISABLED;
+ bool use_lighting = (light_count > 0 || using_directional_lights);
- if (light_mode != r_current_batch->light_mode) {
+ if (use_lighting != r_current_batch->use_lighting) {
r_current_batch = _new_batch(r_batch_broken);
- r_current_batch->light_mode = light_mode;
+ r_current_batch->use_lighting = use_lighting;
}
// new_instance_data should be called after the current_batch is set.
@@ -2369,7 +2307,8 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->command_type = Item::Command::TYPE_RECT;
r_current_batch->command = c;
// default variant
- r_current_batch->pipeline_variant = PIPELINE_VARIANT_QUAD;
+ r_current_batch->shader_variant = SHADER_VARIANT_QUAD;
+ r_current_batch->render_primitive = RD::RENDER_PRIMITIVE_TRIANGLES;
}
if (bool(rect->flags & CANVAS_RECT_TILE)) {
@@ -2397,7 +2336,8 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch = _new_batch(r_batch_broken);
r_current_batch->has_blend = has_blend;
r_current_batch->modulate = modulated;
- r_current_batch->pipeline_variant = has_blend ? PIPELINE_VARIANT_QUAD_LCD_BLEND : PIPELINE_VARIANT_QUAD;
+ r_current_batch->shader_variant = SHADER_VARIANT_QUAD;
+ r_current_batch->render_primitive = RD::RENDER_PRIMITIVE_TRIANGLES;
}
InstanceData *instance_data = new_instance_data();
@@ -2486,7 +2426,8 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->command_type = Item::Command::TYPE_NINEPATCH;
r_current_batch->command = c;
r_current_batch->has_blend = false;
- r_current_batch->pipeline_variant = PipelineVariant::PIPELINE_VARIANT_NINEPATCH;
+ r_current_batch->shader_variant = SHADER_VARIANT_NINEPATCH;
+ r_current_batch->render_primitive = RD::RENDER_PRIMITIVE_TRIANGLES;
}
TextureState tex_state(np->texture, texture_filter, texture_repeat, false, use_linear_colors);
@@ -2567,9 +2508,9 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
// pipeline variant
{
- static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP };
ERR_CONTINUE(polygon->primitive < 0 || polygon->primitive >= RS::PRIMITIVE_MAX);
- r_current_batch->pipeline_variant = variant[polygon->primitive];
+ r_current_batch->shader_variant = polygon->primitive == RS::PRIMITIVE_POINTS ? SHADER_VARIANT_ATTRIBUTES_POINTS : SHADER_VARIANT_ATTRIBUTES;
+ r_current_batch->render_primitive = _primitive_type_to_render_primitive(polygon->primitive);
}
InstanceData *instance_data = new_instance_data();
@@ -2597,9 +2538,26 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->command = c;
r_current_batch->primitive_points = primitive->point_count;
- static const PipelineVariant variant[4] = { PIPELINE_VARIANT_PRIMITIVE_POINTS, PIPELINE_VARIANT_PRIMITIVE_LINES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES };
ERR_CONTINUE(primitive->point_count == 0 || primitive->point_count > 4);
- r_current_batch->pipeline_variant = variant[primitive->point_count - 1];
+
+ switch (primitive->point_count) {
+ case 1:
+ r_current_batch->shader_variant = SHADER_VARIANT_PRIMITIVE_POINTS;
+ r_current_batch->render_primitive = RD::RENDER_PRIMITIVE_POINTS;
+ break;
+ case 2:
+ r_current_batch->shader_variant = SHADER_VARIANT_PRIMITIVE;
+ r_current_batch->render_primitive = RD::RENDER_PRIMITIVE_LINES;
+ break;
+ case 3:
+ case 4:
+ r_current_batch->shader_variant = SHADER_VARIANT_PRIMITIVE;
+ r_current_batch->render_primitive = RD::RENDER_PRIMITIVE_TRIANGLES;
+ break;
+ default:
+ // Unknown point count.
+ break;
+ };
TextureState tex_state(primitive->texture, texture_filter, texture_repeat, false, use_linear_colors);
if (tex_state != r_current_batch->tex_info.state) {
@@ -2795,7 +2753,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
}
}
-void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineVariants *p_pipeline_variants, RenderingDevice::FramebufferFormatID p_framebuffer_format, Light *p_lights, Batch const *p_batch, RenderingMethod::RenderInfo *r_render_info) {
+void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, CanvasShaderData *p_shader_data, RenderingDevice::FramebufferFormatID p_framebuffer_format, Light *p_lights, Batch const *p_batch, RenderingMethod::RenderInfo *r_render_info) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
@@ -2816,17 +2774,24 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
}
}
+ RID pipeline;
+ PipelineKey pipeline_key;
+ PushConstant push_constant;
+ pipeline_key.framebuffer_format_id = p_framebuffer_format;
+ pipeline_key.variant = p_batch->shader_variant;
+ pipeline_key.render_primitive = p_batch->render_primitive;
+ pipeline_key.shader_specialization.use_lighting = p_batch->use_lighting;
+ pipeline_key.lcd_blend = p_batch->has_blend;
+
switch (p_batch->command_type) {
case Item::Command::TYPE_RECT:
case Item::Command::TYPE_NINEPATCH: {
- RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][p_batch->pipeline_variant].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format);
+ pipeline = _get_pipeline_specialization_or_ubershader(p_shader_data, pipeline_key, push_constant);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
if (p_batch->has_blend) {
- DEV_ASSERT(p_batch->pipeline_variant == PIPELINE_VARIANT_QUAD_LCD_BLEND);
RD::get_singleton()->draw_list_set_blend_constants(p_draw_list, p_batch->modulate);
}
- PushConstant push_constant;
push_constant.base_instance_index = p_batch->start;
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array);
@@ -2845,10 +2810,10 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id);
ERR_FAIL_NULL(pb);
- RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][p_batch->pipeline_variant].get_render_pipeline(pb->vertex_format_id, p_framebuffer_format);
+ pipeline_key.vertex_format_id = pb->vertex_format_id;
+ pipeline = _get_pipeline_specialization_or_ubershader(p_shader_data, pipeline_key, push_constant);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
- PushConstant push_constant;
push_constant.base_instance_index = p_batch->start;
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, pb->vertex_array);
@@ -2867,10 +2832,9 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
case Item::Command::TYPE_PRIMITIVE: {
const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(p_batch->command);
- RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][p_batch->pipeline_variant].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format);
+ pipeline = _get_pipeline_specialization_or_ubershader(p_shader_data, pipeline_key, push_constant);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
- PushConstant push_constant;
push_constant.base_instance_index = p_batch->start;
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->draw_list_bind_index_array(p_draw_list, primitive_arrays.index_array[MIN(3u, primitive->point_count) - 1]);
@@ -2933,7 +2897,6 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
}
uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh);
- static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP };
for (uint32_t j = 0; j < surf_count; j++) {
void *surface = mesh_storage->mesh_get_surface(mesh, j);
@@ -2941,21 +2904,14 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface);
ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX);
- uint64_t input_mask = p_pipeline_variants->variants[p_batch->light_mode][variant[primitive]].get_vertex_input_mask();
-
RID vertex_array;
- RD::VertexFormatID vertex_format = RD::INVALID_FORMAT_ID;
+ pipeline_key.variant = primitive == RS::PRIMITIVE_POINTS ? SHADER_VARIANT_ATTRIBUTES_POINTS : SHADER_VARIANT_ATTRIBUTES;
+ pipeline_key.render_primitive = _primitive_type_to_render_primitive(primitive);
+ pipeline_key.vertex_format_id = RD::INVALID_FORMAT_ID;
- if (mesh_instance.is_valid()) {
- mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, false, vertex_array, vertex_format);
- } else {
- mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, false, vertex_array, vertex_format);
- }
-
- RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][variant[primitive]].get_render_pipeline(vertex_format, p_framebuffer_format);
+ pipeline = _get_pipeline_specialization_or_ubershader(p_shader_data, pipeline_key, push_constant, mesh_instance, surface, j, &vertex_array);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
- PushConstant push_constant;
push_constant.base_instance_index = p_batch->start;
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
@@ -3105,11 +3061,6 @@ RendererCanvasRenderRD::~RendererCanvasRenderRD() {
//this will also automatically clear all pipelines
RD::get_singleton()->free(state.shadow_sampler);
}
- //bindings
-
- //shaders
-
- shader.canvas_shader.version_free(shader.default_version);
//buffers
{
@@ -3132,4 +3083,6 @@ RendererCanvasRenderRD::~RendererCanvasRenderRD() {
RendererRD::TextureStorage::get_singleton()->canvas_texture_free(default_canvas_texture);
//pipelines don't need freeing, they are all gone after shaders are gone
+
+ memdelete(shader.default_version_data);
}
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index b0f4a4595a..07445b5c53 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -34,6 +34,7 @@
#include "servers/rendering/renderer_canvas_render.h"
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
+#include "servers/rendering/renderer_rd/pipeline_hash_map_rd.h"
#include "servers/rendering/renderer_rd/shaders/canvas.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/canvas_occlusion.glsl.gen.h"
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
@@ -57,12 +58,6 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
SHADER_VARIANT_PRIMITIVE_POINTS,
SHADER_VARIANT_ATTRIBUTES,
SHADER_VARIANT_ATTRIBUTES_POINTS,
- SHADER_VARIANT_QUAD_LIGHT,
- SHADER_VARIANT_NINEPATCH_LIGHT,
- SHADER_VARIANT_PRIMITIVE_LIGHT,
- SHADER_VARIANT_PRIMITIVE_POINTS_LIGHT,
- SHADER_VARIANT_ATTRIBUTES_LIGHT,
- SHADER_VARIANT_ATTRIBUTES_POINTS_LIGHT,
SHADER_VARIANT_MAX
};
@@ -84,14 +79,14 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
FLAGS_NINEPATCH_V_MODE_SHIFT = 18,
FLAGS_LIGHT_COUNT_SHIFT = 20,
- FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 26),
- FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27),
+ FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 24),
+ FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 25),
- FLAGS_USE_MSDF = (1 << 28),
- FLAGS_USE_LCD = (1 << 29),
+ FLAGS_USE_MSDF = (1 << 26),
+ FLAGS_USE_LCD = (1 << 27),
- FLAGS_FLIP_H = (1 << 30),
- FLAGS_FLIP_V = (1 << 31),
+ FLAGS_FLIP_H = (1 << 28),
+ FLAGS_FLIP_V = (1 << 29),
};
enum {
@@ -118,76 +113,82 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
/**** SHADER ****/
/****************/
- enum PipelineVariant {
- PIPELINE_VARIANT_QUAD,
- PIPELINE_VARIANT_NINEPATCH,
- PIPELINE_VARIANT_PRIMITIVE_TRIANGLES,
- PIPELINE_VARIANT_PRIMITIVE_LINES,
- PIPELINE_VARIANT_PRIMITIVE_POINTS,
- PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES,
- PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP,
- PIPELINE_VARIANT_ATTRIBUTE_LINES,
- PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP,
- PIPELINE_VARIANT_ATTRIBUTE_POINTS,
- PIPELINE_VARIANT_QUAD_LCD_BLEND,
- PIPELINE_VARIANT_MAX
- };
- enum PipelineLightMode {
- PIPELINE_LIGHT_MODE_DISABLED,
- PIPELINE_LIGHT_MODE_ENABLED,
- PIPELINE_LIGHT_MODE_MAX
- };
+ struct ShaderSpecialization {
+ union {
+ struct {
+ uint32_t use_lighting : 1;
+ };
- struct PipelineVariants {
- PipelineCacheRD variants[PIPELINE_LIGHT_MODE_MAX][PIPELINE_VARIANT_MAX];
+ uint32_t packed_0;
+ };
};
- struct {
- CanvasShaderRD canvas_shader;
- RID default_version;
- RID default_version_rd_shader;
- RID quad_index_buffer;
- RID quad_index_array;
- PipelineVariants pipeline_variants;
-
- ShaderCompiler compiler;
- } shader;
+ struct PipelineKey {
+ ShaderVariant variant = SHADER_VARIANT_MAX;
+ RD::FramebufferFormatID framebuffer_format_id = RD::INVALID_FORMAT_ID;
+ RD::VertexFormatID vertex_format_id = RD::INVALID_ID;
+ RD::RenderPrimitive render_primitive = RD::RENDER_PRIMITIVE_MAX;
+ ShaderSpecialization shader_specialization = {};
+ uint32_t lcd_blend = 0;
+ uint32_t ubershader = 0;
+
+ uint32_t hash() const {
+ uint32_t h = hash_murmur3_one_32(variant);
+ h = hash_murmur3_one_32(framebuffer_format_id, h);
+ h = hash_murmur3_one_32(vertex_format_id, h);
+ h = hash_murmur3_one_32(render_primitive, h);
+ h = hash_murmur3_one_32(shader_specialization.packed_0, h);
+ h = hash_murmur3_one_32(lcd_blend, h);
+ h = hash_murmur3_one_32(ubershader, h);
+ return hash_fmix32(h);
+ }
+ };
struct CanvasShaderData : public RendererRD::MaterialStorage::ShaderData {
- enum BlendMode { //used internally
- BLEND_MODE_MIX,
- BLEND_MODE_ADD,
- BLEND_MODE_SUB,
- BLEND_MODE_MUL,
- BLEND_MODE_PMALPHA,
- BLEND_MODE_DISABLED,
- };
-
- bool valid = false;
- RID version;
- PipelineVariants pipeline_variants;
-
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
+ int blend_mode = 0;
Vector<uint32_t> ubo_offsets;
uint32_t ubo_size = 0;
String code;
+ RID version;
+ PipelineHashMapRD<PipelineKey, CanvasShaderData, void (CanvasShaderData::*)(PipelineKey)> pipeline_hash_map;
+
+ static const uint32_t VERTEX_INPUT_MASKS_SIZE = SHADER_VARIANT_MAX * 2;
+ std::atomic<uint64_t> vertex_input_masks[VERTEX_INPUT_MASKS_SIZE] = {};
bool uses_screen_texture = false;
bool uses_screen_texture_mipmaps = false;
bool uses_sdf = false;
bool uses_time = false;
+ void _clear_vertex_input_mask_cache();
+ void _create_pipeline(PipelineKey p_pipeline_key);
virtual void set_code(const String &p_Code);
virtual bool is_animated() const;
virtual bool casts_shadows() const;
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
+ RID get_shader(ShaderVariant p_shader_variant, bool p_ubershader) const;
+ uint64_t get_vertex_input_mask(ShaderVariant p_shader_variant, bool p_ubershader);
+ bool is_valid() const;
- CanvasShaderData() {}
+ CanvasShaderData();
virtual ~CanvasShaderData();
};
+ struct {
+ // Data must be guaranteed to be erased before the rest on the destructor.
+ CanvasShaderData *default_version_data = nullptr;
+ CanvasShaderRD canvas_shader;
+ RID default_version_rd_shader;
+ RID quad_index_buffer;
+ RID quad_index_array;
+ ShaderCompiler compiler;
+ uint32_t pipeline_compilations[RS::PIPELINE_SOURCE_MAX] = {};
+ Mutex mutex;
+ } shader;
+
RendererRD::MaterialStorage::ShaderData *_create_shader_func();
static RendererRD::MaterialStorage::ShaderData *_create_shader_funcs() {
return static_cast<RendererCanvasRenderRD *>(singleton)->_create_shader_func();
@@ -364,7 +365,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
struct PushConstant {
uint32_t base_instance_index;
- uint32_t pad1;
+ ShaderSpecialization shader_specialization;
uint32_t pad2;
uint32_t pad3;
};
@@ -448,11 +449,12 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
RID material;
CanvasMaterialData *material_data = nullptr;
- PipelineLightMode light_mode = PipelineLightMode::PIPELINE_LIGHT_MODE_DISABLED;
- PipelineVariant pipeline_variant = PipelineVariant::PIPELINE_VARIANT_QUAD;
const Item::Command *command = nullptr;
Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch.
+ ShaderVariant shader_variant = SHADER_VARIANT_QUAD;
+ RD::RenderPrimitive render_primitive = RD::RENDER_PRIMITIVE_TRIANGLES;
+ bool use_lighting = false;
// batch-specific data
union {
@@ -552,9 +554,10 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
uint32_t base_flags = 0;
};
+ inline RID _get_pipeline_specialization_or_ubershader(CanvasShaderData *p_shader_data, PipelineKey &r_pipeline_key, PushConstant &r_push_constant, RID p_mesh_instance = RID(), void *p_surface = nullptr, uint32_t p_surface_index = 0, RID *r_vertex_array = nullptr);
void _render_batch_items(RenderTarget 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);
void _record_item_commands(const Item *p_item, RenderTarget p_render_target, const Transform2D &p_base_transform, Item *&r_current_clip, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, Batch *&r_current_batch);
- void _render_batch(RD::DrawListID p_draw_list, PipelineVariants *p_pipeline_variants, RenderingDevice::FramebufferFormatID p_framebuffer_format, Light *p_lights, Batch const *p_batch, RenderingMethod::RenderInfo *r_render_info = nullptr);
+ void _render_batch(RD::DrawListID p_draw_list, CanvasShaderData *p_shader_data, RenderingDevice::FramebufferFormatID p_framebuffer_format, Light *p_lights, Batch const *p_batch, RenderingMethod::RenderInfo *r_render_info = nullptr);
void _prepare_batch_texture_info(Batch *p_current_batch, RID p_texture) const;
[[nodiscard]] Batch *_new_batch(bool &r_batch_broken);
void _add_to_batch(uint32_t &r_index, bool &r_batch_broken, Batch *&r_current_batch);
@@ -589,6 +592,7 @@ public:
virtual void set_shadow_texture_size(int p_size) override;
void set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) override;
+ uint32_t get_pipeline_compilations(RS::PipelineSource p_source) override;
void set_time(double p_time);
void update() override;
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h
index 0222a99577..dcd3e90e1b 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.h
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h
@@ -129,6 +129,7 @@ public:
_ALWAYS_INLINE_ uint64_t get_frame_number() const { return frame; }
_ALWAYS_INLINE_ double get_frame_delta_time() const { return delta; }
_ALWAYS_INLINE_ double get_total_time() const { return time; }
+ _ALWAYS_INLINE_ bool can_create_resources_async() const { return true; }
static Error is_viable() {
return OK;
diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp
index 39c3e9b168..6234cddee3 100644
--- a/servers/rendering/renderer_rd/shader_rd.cpp
+++ b/servers/rendering/renderer_rd/shader_rd.cpp
@@ -39,6 +39,8 @@
#include "servers/rendering/rendering_device.h"
#include "thirdparty/misc/smolv.h"
+#define ENABLE_SHADER_CACHE 1
+
void ShaderRD::_add_stage(const char *p_code, StageType p_stage_type) {
Vector<String> lines = String(p_code).split("\n");
@@ -144,7 +146,8 @@ RID ShaderRD::version_create() {
version.dirty = true;
version.valid = false;
version.initialize_needed = true;
- version.variants = nullptr;
+ version.variants.clear();
+ version.variant_data.clear();
return version_owner.make_rid(version);
}
@@ -154,23 +157,25 @@ void ShaderRD::_initialize_version(Version *p_version) {
p_version->valid = false;
p_version->dirty = false;
- p_version->variants = memnew_arr(RID, variant_defines.size());
+ p_version->variants.resize_zeroed(variant_defines.size());
+ p_version->variant_data.resize(variant_defines.size());
+ p_version->group_compilation_tasks.resize(group_enabled.size());
+ p_version->group_compilation_tasks.fill(0);
}
void ShaderRD::_clear_version(Version *p_version) {
+ _compile_ensure_finished(p_version);
+
// Clear versions if they exist.
- if (p_version->variants) {
+ if (!p_version->variants.is_empty()) {
for (int i = 0; i < variant_defines.size(); i++) {
if (p_version->variants[i].is_valid()) {
RD::get_singleton()->free(p_version->variants[i]);
}
}
- memdelete_arr(p_version->variants);
- if (p_version->variant_data) {
- memdelete_arr(p_version->variant_data);
- }
- p_version->variants = nullptr;
+ p_version->variants.clear();
+ p_version->variant_data.clear();
}
}
@@ -227,8 +232,8 @@ void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, c
}
}
-void ShaderRD::_compile_variant(uint32_t p_variant, const CompileData *p_data) {
- uint32_t variant = group_to_variant_map[p_data->group][p_variant];
+void ShaderRD::_compile_variant(uint32_t p_variant, CompileData p_data) {
+ uint32_t variant = group_to_variant_map[p_data.group][p_variant];
if (!variants_enabled[variant]) {
return; // Variant is disabled, return.
@@ -245,7 +250,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, const CompileData *p_data) {
//vertex stage
StringBuilder builder;
- _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_VERTEX]);
+ _build_variant_code(builder, variant, p_data.version, stage_templates[STAGE_TYPE_VERTEX]);
current_source = builder.as_string();
RD::ShaderStageSPIRVData stage;
@@ -263,7 +268,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, const CompileData *p_data) {
current_stage = RD::SHADER_STAGE_FRAGMENT;
StringBuilder builder;
- _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_FRAGMENT]);
+ _build_variant_code(builder, variant, p_data.version, stage_templates[STAGE_TYPE_FRAGMENT]);
current_source = builder.as_string();
RD::ShaderStageSPIRVData stage;
@@ -281,7 +286,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, const CompileData *p_data) {
current_stage = RD::SHADER_STAGE_COMPUTE;
StringBuilder builder;
- _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_COMPUTE]);
+ _build_variant_code(builder, variant, p_data.version, stage_templates[STAGE_TYPE_COMPUTE]);
current_source = builder.as_string();
@@ -313,8 +318,8 @@ void ShaderRD::_compile_variant(uint32_t p_variant, const CompileData *p_data) {
{
MutexLock lock(variant_set_mutex);
- p_data->version->variants[variant] = RD::get_singleton()->shader_create_from_bytecode(shader_data, p_data->version->variants[variant]);
- p_data->version->variant_data[variant] = shader_data;
+ p_data.version->variants.write[variant] = RD::get_singleton()->shader_create_from_bytecode(shader_data, p_data.version->variants[variant]);
+ p_data.version->variant_data.write[variant] = shader_data;
}
}
@@ -443,14 +448,14 @@ bool ShaderRD::_load_from_cache(Version *p_version, int p_group) {
ERR_FAIL_COND_V(br != variant_size, false);
- p_version->variant_data[variant_id] = variant_bytes;
+ p_version->variant_data.write[variant_id] = variant_bytes;
}
for (uint32_t i = 0; i < variant_count; i++) {
int variant_id = group_to_variant_map[p_group][i];
if (!variants_enabled[variant_id]) {
MutexLock lock(variant_set_mutex);
- p_version->variants[variant_id] = RID();
+ p_version->variants.write[variant_id] = RID();
continue;
}
{
@@ -464,12 +469,10 @@ bool ShaderRD::_load_from_cache(Version *p_version, int p_group) {
ERR_FAIL_COND_V(shader.is_null(), false);
}
- p_version->variants[variant_id] = shader;
+ p_version->variants.write[variant_id] = shader;
}
}
- memdelete_arr(p_version->variant_data); //clear stages
- p_version->variant_data = nullptr;
p_version->valid = true;
return true;
}
@@ -491,48 +494,51 @@ void ShaderRD::_save_to_cache(Version *p_version, int p_group) {
}
void ShaderRD::_allocate_placeholders(Version *p_version, int p_group) {
- ERR_FAIL_NULL(p_version->variants);
+ ERR_FAIL_COND(p_version->variants.is_empty());
+
for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) {
int variant_id = group_to_variant_map[p_group][i];
RID shader = RD::get_singleton()->shader_create_placeholder();
{
MutexLock lock(variant_set_mutex);
- p_version->variants[variant_id] = shader;
+ p_version->variants.write[variant_id] = shader;
}
}
}
// Try to compile all variants for a given group.
// Will skip variants that are disabled.
-void ShaderRD::_compile_version(Version *p_version, int p_group) {
+void ShaderRD::_compile_version_start(Version *p_version, int p_group) {
if (!group_enabled[p_group]) {
return;
}
- typedef Vector<uint8_t> ShaderStageData;
- p_version->variant_data = memnew_arr(ShaderStageData, variant_defines.size());
-
p_version->dirty = false;
+#if ENABLE_SHADER_CACHE
if (shader_cache_dir_valid) {
if (_load_from_cache(p_version, p_group)) {
return;
}
}
+#endif
CompileData compile_data;
compile_data.version = p_version;
compile_data.group = p_group;
-#if 1
- WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &ShaderRD::_compile_variant, &compile_data, group_to_variant_map[p_group].size(), -1, true, SNAME("ShaderCompilation"));
- WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &ShaderRD::_compile_variant, compile_data, group_to_variant_map[p_group].size(), -1, true, SNAME("ShaderCompilation"));
+ p_version->group_compilation_tasks.write[p_group] = group_task;
+}
-#else
- for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) {
- _compile_variant(i, &compile_data);
+void ShaderRD::_compile_version_end(Version *p_version, int p_group) {
+ if (p_version->group_compilation_tasks.size() <= p_group || p_version->group_compilation_tasks[p_group] == 0) {
+ return;
}
-#endif
+
+ WorkerThreadPool::GroupID group_task = p_version->group_compilation_tasks[p_group];
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
+ p_version->group_compilation_tasks.write[p_group] = 0;
bool all_valid = true;
@@ -557,29 +563,35 @@ void ShaderRD::_compile_version(Version *p_version, int p_group) {
RD::get_singleton()->free(p_version->variants[i]);
}
}
- memdelete_arr(p_version->variants);
- if (p_version->variant_data) {
- memdelete_arr(p_version->variant_data);
- }
- p_version->variants = nullptr;
- p_version->variant_data = nullptr;
+
+ p_version->variants.clear();
+ p_version->variant_data.clear();
return;
- } else if (shader_cache_dir_valid) {
- // Save shader cache.
+ }
+#if ENABLE_SHADER_CACHE
+ else if (shader_cache_dir_valid) {
_save_to_cache(p_version, p_group);
}
-
- memdelete_arr(p_version->variant_data); //clear stages
- p_version->variant_data = nullptr;
+#endif
p_version->valid = true;
}
+void ShaderRD::_compile_ensure_finished(Version *p_version) {
+ // Wait for compilation of existing groups if necessary.
+ for (int i = 0; i < group_enabled.size(); i++) {
+ _compile_version_end(p_version, i);
+ }
+}
+
void ShaderRD::version_set_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines) {
ERR_FAIL_COND(is_compute);
Version *version = version_owner.get_or_null(p_version);
ERR_FAIL_NULL(version);
+
+ _compile_ensure_finished(version);
+
version->vertex_globals = p_vertex_globals.utf8();
version->fragment_globals = p_fragment_globals.utf8();
version->uniforms = p_uniforms.utf8();
@@ -601,7 +613,7 @@ void ShaderRD::version_set_code(RID p_version, const HashMap<String, String> &p_
_allocate_placeholders(version, i);
continue;
}
- _compile_version(version, i);
+ _compile_version_start(version, i);
}
version->initialize_needed = false;
}
@@ -613,6 +625,8 @@ void ShaderRD::version_set_compute_code(RID p_version, const HashMap<String, Str
Version *version = version_owner.get_or_null(p_version);
ERR_FAIL_NULL(version);
+ _compile_ensure_finished(version);
+
version->compute_globals = p_compute_globals.utf8();
version->uniforms = p_uniforms.utf8();
@@ -634,7 +648,7 @@ void ShaderRD::version_set_compute_code(RID p_version, const HashMap<String, Str
_allocate_placeholders(version, i);
continue;
}
- _compile_version(version, i);
+ _compile_version_start(version, i);
}
version->initialize_needed = false;
}
@@ -651,10 +665,12 @@ bool ShaderRD::version_is_valid(RID p_version) {
_allocate_placeholders(version, i);
continue;
}
- _compile_version(version, i);
+ _compile_version_start(version, i);
}
}
+ _compile_ensure_finished(version);
+
return version->valid;
}
@@ -696,7 +712,7 @@ void ShaderRD::enable_group(int p_group) {
version_owner.get_owned_list(&all_versions);
for (const RID &E : all_versions) {
Version *version = version_owner.get_or_null(E);
- _compile_version(version, p_group);
+ _compile_version_start(version, p_group);
}
}
@@ -735,6 +751,7 @@ void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String
for (int i = 0; i < p_variant_defines.size(); i++) {
variant_defines.push_back(VariantDefine(0, p_variant_defines[i], true));
variants_enabled.push_back(true);
+ variant_to_group.push_back(0);
group_to_variant_map[0].push_back(i);
}
@@ -796,6 +813,7 @@ void ShaderRD::initialize(const Vector<VariantDefine> &p_variant_defines, const
// Fill variant array.
variant_defines.push_back(p_variant_defines[i]);
variants_enabled.push_back(true);
+ variant_to_group.push_back(p_variant_defines[i].group);
// Map variant array index to group id, so we can iterate over groups later.
if (!group_to_variant_map.has(p_variant_defines[i].group)) {
diff --git a/servers/rendering/renderer_rd/shader_rd.h b/servers/rendering/renderer_rd/shader_rd.h
index 688092d604..90e41947b9 100644
--- a/servers/rendering/renderer_rd/shader_rd.h
+++ b/servers/rendering/renderer_rd/shader_rd.h
@@ -59,6 +59,7 @@ private:
CharString general_defines;
Vector<VariantDefine> variant_defines;
Vector<bool> variants_enabled;
+ Vector<uint32_t> variant_to_group;
HashMap<int, LocalVector<int>> group_to_variant_map;
Vector<bool> group_enabled;
@@ -69,9 +70,10 @@ private:
CharString fragment_globals;
HashMap<StringName, CharString> code_sections;
Vector<CharString> custom_defines;
+ Vector<WorkerThreadPool::GroupID> group_compilation_tasks;
- Vector<uint8_t> *variant_data = nullptr;
- RID *variants = nullptr; // Same size as variant defines.
+ Vector<Vector<uint8_t>> variant_data;
+ Vector<RID> variants;
bool valid;
bool dirty;
@@ -85,11 +87,13 @@ private:
int group = 0;
};
- void _compile_variant(uint32_t p_variant, const CompileData *p_data);
+ void _compile_variant(uint32_t p_variant, CompileData p_data);
void _initialize_version(Version *p_version);
void _clear_version(Version *p_version);
- void _compile_version(Version *p_version, int p_group);
+ void _compile_version_start(Version *p_version, int p_group);
+ void _compile_version_end(Version *p_version, int p_group);
+ void _compile_ensure_finished(Version *p_version);
void _allocate_placeholders(Version *p_version, int p_group);
RID_Owner<Version> version_owner;
@@ -172,10 +176,15 @@ public:
_allocate_placeholders(version, i);
continue;
}
- _compile_version(version, i);
+ _compile_version_start(version, i);
}
}
+ uint32_t group = variant_to_group[p_variant];
+ if (version->group_compilation_tasks[group] != 0) {
+ _compile_version_end(version, group);
+ }
+
if (!version->valid) {
return RID();
}
diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl
index fd0cd5bfad..dafcce37ad 100644
--- a/servers/rendering/renderer_rd/shaders/canvas.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas.glsl
@@ -193,9 +193,7 @@ void main() {
}
#endif // USE_ATTRIBUTES
-#ifdef USE_POINT_SIZE
float point_size = 1.0;
-#endif
#ifdef USE_WORLD_VERTEX_COORDS
vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy;
@@ -368,8 +366,6 @@ float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, flo
#endif
-#ifdef USE_LIGHTING
-
vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 light_color, vec4 specular_shininess, bool specular_shininess_used) {
float cNdotL = max(0.0, dot(normal, light_vec));
@@ -459,8 +455,6 @@ void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) {
}
}
-#endif
-
float msdf_median(float r, float g, float b, float a) {
return min(max(min(r, g), min(max(r, g), b)), a);
}
@@ -534,7 +528,7 @@ void main() {
color *= texture(sampler2D(color_texture, texture_sampler), uv);
}
- uint light_count = bitfieldExtract(draw_data.flags, FLAGS_LIGHT_COUNT_SHIFT, 4); //max 16 lights
+ uint light_count = bitfieldExtract(draw_data.flags, FLAGS_LIGHT_COUNT_SHIFT, 4); //max 15 lights
bool using_light = (light_count + canvas_data.directional_light_count) > 0;
vec3 normal;
@@ -618,134 +612,135 @@ void main() {
color *= canvas_data.canvas_modulation;
#endif
-#if defined(USE_LIGHTING) && !defined(MODE_UNSHADED)
-
- // Directional Lights
+#if !defined(MODE_UNSHADED)
+ if (sc_use_lighting()) {
+ // Directional Lights
- for (uint i = 0; i < canvas_data.directional_light_count; i++) {
- uint light_base = i;
+ for (uint i = 0; i < canvas_data.directional_light_count; i++) {
+ uint light_base = i;
- vec2 direction = light_array.data[light_base].position;
- vec4 light_color = light_array.data[light_base].color;
+ vec2 direction = light_array.data[light_base].position;
+ vec4 light_color = light_array.data[light_base].color;
#ifdef LIGHT_CODE_USED
- vec4 shadow_modulate = vec4(1.0);
- light_color = light_compute(light_vertex, vec3(direction, light_array.data[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, true);
+ vec4 shadow_modulate = vec4(1.0);
+ light_color = light_compute(light_vertex, vec3(direction, light_array.data[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, true);
#else
- if (normal_used) {
- vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_array.data[light_base].height));
- light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used);
- } else {
- light_color.rgb *= base_color.rgb;
- }
+ if (normal_used) {
+ vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_array.data[light_base].height));
+ light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used);
+ } else {
+ light_color.rgb *= base_color.rgb;
+ }
#endif
- if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
- vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
+ if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
+ vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
- vec4 shadow_uv = vec4(shadow_pos.x, light_array.data[light_base].shadow_y_ofs, shadow_pos.y * light_array.data[light_base].shadow_zfar_inv, 1.0);
+ vec4 shadow_uv = vec4(shadow_pos.x, light_array.data[light_base].shadow_y_ofs, shadow_pos.y * light_array.data[light_base].shadow_zfar_inv, 1.0);
- light_color = light_shadow_compute(light_base, light_color, shadow_uv
+ light_color = light_shadow_compute(light_base, light_color, shadow_uv
#ifdef LIGHT_CODE_USED
- ,
- shadow_modulate.rgb
+ ,
+ shadow_modulate.rgb
#endif
- );
- }
+ );
+ }
- light_blend_compute(light_base, light_color, color.rgb);
+ light_blend_compute(light_base, light_color, color.rgb);
#ifdef MODE_LIGHT_ONLY
- light_only_alpha += light_color.a;
+ light_only_alpha += light_color.a;
#endif
- }
+ }
- // Positional Lights
+ // Positional Lights
- for (uint i = 0; i < MAX_LIGHTS_PER_ITEM; i++) {
- if (i >= light_count) {
- break;
- }
- uint light_base = bitfieldExtract(draw_data.lights[i >> 2], (int(i) & 0x3) * 8, 8);
+ for (uint i = 0; i < MAX_LIGHTS_PER_ITEM; i++) {
+ if (i >= light_count) {
+ break;
+ }
+ uint light_base = bitfieldExtract(draw_data.lights[i >> 2], (int(i) & 0x3) * 8, 8);
- vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array.data[light_base].texture_matrix[0], light_array.data[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
- vec2 tex_uv_atlas = tex_uv * light_array.data[light_base].atlas_rect.zw + light_array.data[light_base].atlas_rect.xy;
+ vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array.data[light_base].texture_matrix[0], light_array.data[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
+ vec2 tex_uv_atlas = tex_uv * light_array.data[light_base].atlas_rect.zw + light_array.data[light_base].atlas_rect.xy;
- if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) {
- //if outside the light texture, light color is zero
- continue;
- }
+ if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) {
+ //if outside the light texture, light color is zero
+ continue;
+ }
- vec4 light_color = textureLod(sampler2D(atlas_texture, texture_sampler), tex_uv_atlas, 0.0);
- vec4 light_base_color = light_array.data[light_base].color;
+ vec4 light_color = textureLod(sampler2D(atlas_texture, texture_sampler), tex_uv_atlas, 0.0);
+ vec4 light_base_color = light_array.data[light_base].color;
#ifdef LIGHT_CODE_USED
- vec4 shadow_modulate = vec4(1.0);
- vec3 light_position = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
+ vec4 shadow_modulate = vec4(1.0);
+ vec3 light_position = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
- light_color.rgb *= light_base_color.rgb;
- light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, false);
+ light_color.rgb *= light_base_color.rgb;
+ light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, false);
#else
- light_color.rgb *= light_base_color.rgb * light_base_color.a;
+ light_color.rgb *= light_base_color.rgb * light_base_color.a;
- if (normal_used) {
- vec3 light_pos = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
- vec3 pos = light_vertex;
- vec3 light_vec = normalize(light_pos - pos);
+ if (normal_used) {
+ vec3 light_pos = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
+ vec3 pos = light_vertex;
+ vec3 light_vec = normalize(light_pos - pos);
- light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used);
- } else {
- light_color.rgb *= base_color.rgb;
- }
+ light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used);
+ } else {
+ light_color.rgb *= base_color.rgb;
+ }
#endif
- if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
- vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
-
- vec2 pos_norm = normalize(shadow_pos);
- vec2 pos_abs = abs(pos_norm);
- vec2 pos_box = pos_norm / max(pos_abs.x, pos_abs.y);
- vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot?
- float tex_ofs;
- float distance;
- if (pos_rot.y > 0) {
- if (pos_rot.x > 0) {
- tex_ofs = pos_box.y * 0.125 + 0.125;
- distance = shadow_pos.x;
+ if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
+ vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
+
+ vec2 pos_norm = normalize(shadow_pos);
+ vec2 pos_abs = abs(pos_norm);
+ vec2 pos_box = pos_norm / max(pos_abs.x, pos_abs.y);
+ vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot?
+ float tex_ofs;
+ float distance;
+ if (pos_rot.y > 0) {
+ if (pos_rot.x > 0) {
+ tex_ofs = pos_box.y * 0.125 + 0.125;
+ distance = shadow_pos.x;
+ } else {
+ tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125);
+ distance = shadow_pos.y;
+ }
} else {
- tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125);
- distance = shadow_pos.y;
+ if (pos_rot.x < 0) {
+ tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125);
+ distance = -shadow_pos.x;
+ } else {
+ tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125);
+ distance = -shadow_pos.y;
+ }
}
- } else {
- if (pos_rot.x < 0) {
- tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125);
- distance = -shadow_pos.x;
- } else {
- tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125);
- distance = -shadow_pos.y;
- }
- }
- distance *= light_array.data[light_base].shadow_zfar_inv;
+ distance *= light_array.data[light_base].shadow_zfar_inv;
- //float distance = length(shadow_pos);
- vec4 shadow_uv = vec4(tex_ofs, light_array.data[light_base].shadow_y_ofs, distance, 1.0);
+ //float distance = length(shadow_pos);
+ vec4 shadow_uv = vec4(tex_ofs, light_array.data[light_base].shadow_y_ofs, distance, 1.0);
- light_color = light_shadow_compute(light_base, light_color, shadow_uv
+ light_color = light_shadow_compute(light_base, light_color, shadow_uv
#ifdef LIGHT_CODE_USED
- ,
- shadow_modulate.rgb
+ ,
+ shadow_modulate.rgb
#endif
- );
- }
+ );
+ }
- light_blend_compute(light_base, light_color, color.rgb);
+ light_blend_compute(light_base, light_color, color.rgb);
#ifdef MODE_LIGHT_ONLY
- light_only_alpha += light_color.a;
+ light_only_alpha += light_color.a;
#endif
+ }
}
#endif
diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
index fc9b727581..ead8c459a4 100644
--- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
@@ -23,14 +23,14 @@
#define FLAGS_LIGHT_COUNT_SHIFT 20
-#define FLAGS_DEFAULT_NORMAL_MAP_USED (1 << 26)
-#define FLAGS_DEFAULT_SPECULAR_MAP_USED (1 << 27)
+#define FLAGS_DEFAULT_NORMAL_MAP_USED (1 << 24)
+#define FLAGS_DEFAULT_SPECULAR_MAP_USED (1 << 25)
-#define FLAGS_USE_MSDF (1 << 28)
-#define FLAGS_USE_LCD (1 << 29)
+#define FLAGS_USE_MSDF (1 << 26)
+#define FLAGS_USE_LCD (1 << 27)
-#define FLAGS_FLIP_H (1 << 30)
-#define FLAGS_FLIP_V (1 << 31)
+#define FLAGS_FLIP_H (1 << 28)
+#define FLAGS_FLIP_V (1 << 29)
struct InstanceData {
vec2 world_x;
@@ -56,12 +56,36 @@ struct InstanceData {
layout(push_constant, std430) uniform Params {
uint base_instance_index; // base index to instance data
- uint pad1;
+ uint sc_packed_0;
uint pad2;
uint pad3;
}
params;
+// Specialization constants.
+
+#ifdef UBERSHADER
+
+// Pull the constants from the draw call's push constants.
+uint sc_packed_0() {
+ return draw_call.sc_packed_0;
+}
+
+#else
+
+// Pull the constants from the pipeline's specialization constants.
+layout(constant_id = 0) const uint pso_sc_packed_0 = 0;
+
+uint sc_packed_0() {
+ return pso_sc_packed_0;
+}
+
+#endif
+
+bool sc_use_lighting() {
+ return ((sc_packed_0() >> 0) & 1U) != 0;
+}
+
// In vulkan, sets should always be ordered using the following logic:
// Lower Sets: Sets that change format and layout less often
// Higher sets: Sets that change format and layout very often
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 3cbe1427b6..72236dcd9f 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
@@ -804,29 +804,6 @@ void main() {
#define SHADER_IS_SRGB false
#define SHADER_SPACE_FAR 0.0
-/* Specialization Constants (Toggles) */
-
-layout(constant_id = 0) const bool sc_use_forward_gi = false;
-layout(constant_id = 1) const bool sc_use_light_projector = false;
-layout(constant_id = 2) const bool sc_use_light_soft_shadows = false;
-layout(constant_id = 3) const bool sc_use_directional_soft_shadows = false;
-
-/* Specialization Constants (Values) */
-
-layout(constant_id = 6) const uint sc_soft_shadow_samples = 4;
-layout(constant_id = 7) const uint sc_penumbra_shadow_samples = 4;
-
-layout(constant_id = 8) const uint sc_directional_soft_shadow_samples = 4;
-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;
-
#include "scene_forward_clustered_inc.glsl"
/* Varyings */
@@ -1081,7 +1058,7 @@ vec4 fog_process(vec3 vertex) {
float fog_amount = 0.0;
- if (sc_use_depth_fog) {
+ if (sc_use_depth_fog()) {
float fog_z = smoothstep(scene_data_block.data.fog_depth_begin, scene_data_block.data.fog_depth_end, length(vertex));
float fog_quad_amount = pow(fog_z, scene_data_block.data.fog_depth_curve) * scene_data_block.data.fog_density;
fog_amount = fog_quad_amount;
@@ -1469,7 +1446,7 @@ void fragment_shader(in SceneData scene_data) {
if (decals.data[decal_index].albedo_rect != vec4(0.0)) {
//has albedo
vec4 decal_albedo;
- if (sc_decal_use_mipmaps) {
+ if (sc_decal_use_mipmaps()) {
decal_albedo = textureGrad(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].albedo_rect.zw + decals.data[decal_index].albedo_rect.xy, ddx * decals.data[decal_index].albedo_rect.zw, ddy * decals.data[decal_index].albedo_rect.zw);
} else {
decal_albedo = textureLod(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].albedo_rect.zw + decals.data[decal_index].albedo_rect.xy, 0.0);
@@ -1480,7 +1457,7 @@ void fragment_shader(in SceneData scene_data) {
if (decals.data[decal_index].normal_rect != vec4(0.0)) {
vec3 decal_normal;
- if (sc_decal_use_mipmaps) {
+ if (sc_decal_use_mipmaps()) {
decal_normal = textureGrad(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].normal_rect.zw + decals.data[decal_index].normal_rect.xy, ddx * decals.data[decal_index].normal_rect.zw, ddy * decals.data[decal_index].normal_rect.zw).xyz;
} else {
decal_normal = textureLod(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].normal_rect.zw + decals.data[decal_index].normal_rect.xy, 0.0).xyz;
@@ -1495,7 +1472,7 @@ void fragment_shader(in SceneData scene_data) {
if (decals.data[decal_index].orm_rect != vec4(0.0)) {
vec3 decal_orm;
- if (sc_decal_use_mipmaps) {
+ if (sc_decal_use_mipmaps()) {
decal_orm = textureGrad(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].orm_rect.zw + decals.data[decal_index].orm_rect.xy, ddx * decals.data[decal_index].orm_rect.zw, ddy * decals.data[decal_index].orm_rect.zw).xyz;
} else {
decal_orm = textureLod(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].orm_rect.zw + decals.data[decal_index].orm_rect.xy, 0.0).xyz;
@@ -1508,7 +1485,7 @@ void fragment_shader(in SceneData scene_data) {
if (decals.data[decal_index].emission_rect != vec4(0.0)) {
//emission is additive, so its independent from albedo
- if (sc_decal_use_mipmaps) {
+ if (sc_decal_use_mipmaps()) {
emission += textureGrad(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].emission_rect.zw + decals.data[decal_index].emission_rect.xy, ddx * decals.data[decal_index].emission_rect.zw, ddy * decals.data[decal_index].emission_rect.zw).xyz * decals.data[decal_index].modulate.rgb * decals.data[decal_index].emission_energy * fade;
} else {
emission += textureLod(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].emission_rect.zw + decals.data[decal_index].emission_rect.xy, 0.0).xyz * decals.data[decal_index].modulate.rgb * decals.data[decal_index].emission_energy * fade;
@@ -1683,7 +1660,7 @@ void fragment_shader(in SceneData scene_data) {
vec3 lm_light_l1_0;
vec3 lm_light_l1p1;
- if (sc_use_lightmap_bicubic_filter) {
+ 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 - vec3(0.5)) * 2.0;
lm_light_l1_0 = (textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 2.0), lightmaps.data[ofs].light_texture_size).rgb - vec3(0.5)) * 2.0;
@@ -1704,7 +1681,7 @@ void fragment_shader(in SceneData scene_data) {
ambient_light += lm_light_l1p1 * n.x * (lm_light_l0 * en * 4.0);
} else {
- if (sc_use_lightmap_bicubic_filter) {
+ 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;
@@ -1713,7 +1690,7 @@ void fragment_shader(in SceneData scene_data) {
}
#else
- if (sc_use_forward_gi && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_SDFGI)) { //has lightmap capture
+ if (sc_use_forward_gi() && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_SDFGI)) { //has lightmap capture
//make vertex orientation the world one, but still align to camera
vec3 cam_pos = mat3(scene_data.inv_view_matrix) * vertex;
@@ -1785,7 +1762,7 @@ void fragment_shader(in SceneData scene_data) {
}
}
- if (sc_use_forward_gi && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_VOXEL_GI)) { // process voxel_gi_instances
+ if (sc_use_forward_gi() && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_VOXEL_GI)) { // process voxel_gi_instances
uint index1 = instances.data[instance_index].gi_offset & 0xFFFF;
// Make vertex orientation the world one, but still align to camera.
vec3 cam_pos = mat3(scene_data.inv_view_matrix) * vertex;
@@ -1820,7 +1797,7 @@ void fragment_shader(in SceneData scene_data) {
ambient_light = amb_accum.rgb;
}
- if (!sc_use_forward_gi && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_GI_BUFFERS)) { //use GI buffers
+ if (!sc_use_forward_gi() && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_GI_BUFFERS)) { //use GI buffers
vec2 coord;
@@ -2043,7 +2020,7 @@ void fragment_shader(in SceneData scene_data) {
m_var.xyz += normal_bias;
//version with soft shadows, more expensive
- if (sc_use_directional_soft_shadows && directional_lights.data[i].softshadow_angle > 0) {
+ if (sc_use_directional_soft_shadows() && directional_lights.data[i].softshadow_angle > 0) {
uint blend_count = 0;
const uint blend_max = directional_lights.data[i].blend_splits ? 2 : 1;
@@ -2324,7 +2301,7 @@ void fragment_shader(in SceneData scene_data) {
shadow = 1.0;
#endif
- float size_A = sc_use_directional_soft_shadows ? directional_lights.data[i].size : 0.0;
+ float size_A = sc_use_directional_soft_shadows() ? directional_lights.data[i].size : 0.0;
light_compute(normal, directional_lights.data[i].direction, normalize(view), size_A,
#ifndef DEBUG_DRAW_PSSM_SPLITS
@@ -2734,6 +2711,14 @@ void fragment_shader(in SceneData scene_data) {
}
void main() {
+#ifdef UBERSHADER
+ bool front_facing = gl_FrontFacing;
+ if (uc_cull_mode() == POLYGON_CULL_BACK && !front_facing) {
+ discard;
+ } else if (uc_cull_mode() == POLYGON_CULL_FRONT && front_facing) {
+ discard;
+ }
+#endif
#ifdef MODE_DUAL_PARABOLOID
if (dp_clip > 0.0)
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 03511aa3a8..9f68d59be2 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
@@ -37,9 +37,96 @@ layout(push_constant, std430) uniform DrawCall {
uint uv_offset;
uint multimesh_motion_vectors_current_offset;
uint multimesh_motion_vectors_previous_offset;
+#ifdef UBERSHADER
+ uint sc_packed_0;
+ uint sc_packed_1;
+ uint sc_packed_2;
+ uint uc_packed_0;
+#endif
}
draw_call;
+/* Specialization Constants */
+
+#ifdef UBERSHADER
+
+#define POLYGON_CULL_DISABLED 0
+#define POLYGON_CULL_FRONT 1
+#define POLYGON_CULL_BACK 2
+
+// Pull the constants from the draw call's push constants.
+uint sc_packed_0() {
+ return draw_call.sc_packed_0;
+}
+
+uint uc_cull_mode() {
+ return (draw_call.uc_packed_0 >> 0) & 3U;
+}
+
+#else
+
+// Pull the constants from the pipeline's specialization constants.
+layout(constant_id = 0) const uint pso_sc_packed_0 = 0;
+
+uint sc_packed_0() {
+ return pso_sc_packed_0;
+}
+
+#endif
+
+bool sc_use_forward_gi() {
+ return ((sc_packed_0() >> 0) & 1U) != 0;
+}
+
+bool sc_use_light_projector() {
+ return ((sc_packed_0() >> 1) & 1U) != 0;
+}
+
+bool sc_use_light_soft_shadows() {
+ return ((sc_packed_0() >> 2) & 1U) != 0;
+}
+
+bool sc_use_directional_soft_shadows() {
+ return ((sc_packed_0() >> 3) & 1U) != 0;
+}
+
+bool sc_decal_use_mipmaps() {
+ return ((sc_packed_0() >> 4) & 1U) != 0;
+}
+
+bool sc_projector_use_mipmaps() {
+ return ((sc_packed_0() >> 5) & 1U) != 0;
+}
+
+bool sc_use_depth_fog() {
+ return ((sc_packed_0() >> 6) & 1U) != 0;
+}
+
+bool sc_use_lightmap_bicubic_filter() {
+ return ((sc_packed_0() >> 7) & 1U) != 0;
+}
+
+uint sc_soft_shadow_samples() {
+ return (sc_packed_0() >> 8) & 15U;
+}
+
+uint sc_penumbra_shadow_samples() {
+ return (sc_packed_0() >> 12) & 15U;
+}
+
+uint sc_directional_soft_shadow_samples() {
+ return (sc_packed_0() >> 16) & 15U;
+}
+
+uint sc_directional_penumbra_shadow_samples() {
+ return (sc_packed_0() >> 20) & 15U;
+}
+
+float sc_luminance_multiplier() {
+ // Not used in clustered renderer but we share some code with the mobile renderer that requires this.
+ return 1.0;
+}
+
#define SDFGI_MAX_CASCADES 8
/* Set 0: Base Pass (never changes) */
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 e7ce44bce2..2f0e4e0bea 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
@@ -77,10 +77,6 @@ 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;
@@ -109,10 +105,6 @@ layout(location = 6) mediump out vec3 binormal_interp;
layout(location = 7) highp out vec4 diffuse_light_interp;
layout(location = 8) highp out vec4 specular_light_interp;
-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 = 12) const bool sc_disable_directional_lights = false;
-
#include "../scene_forward_vertex_lights_inc.glsl"
#endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
#ifdef MATERIAL_UNIFORMS_USED
@@ -216,7 +208,7 @@ void main() {
mat4 matrix;
mat4 read_model_matrix = model_matrix;
- if (sc_is_multimesh) {
+ if (sc_is_multimesh()) {
//multimesh, instances are for it
#ifdef USE_PARTICLE_TRAILS
@@ -412,7 +404,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 (sc_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);
}
@@ -469,7 +461,7 @@ void main() {
diffuse_light_interp = vec4(0.0);
specular_light_interp = vec4(0.0);
- if (!sc_disable_omni_lights) {
+ if (!sc_disable_omni_lights()) {
uint light_indices = instances.data[draw_call.instance_index].omni_lights.x;
for (uint i = 0; i < 8; i++) {
uint light_index = light_indices & 0xFF;
@@ -488,7 +480,7 @@ void main() {
}
}
- if (!sc_disable_spot_lights) {
+ if (!sc_disable_spot_lights()) {
uint light_indices = instances.data[draw_call.instance_index].spot_lights.x;
for (uint i = 0; i < 8; i++) {
uint light_index = light_indices & 0xFF;
@@ -507,7 +499,7 @@ void main() {
}
}
- if (!sc_disable_directional_lights) {
+ if (!sc_disable_directional_lights()) {
// We process the first directional light separately as it may have shadows.
vec3 directional_diffuse = vec3(0.0);
vec3 directional_specular = vec3(0.0);
@@ -612,41 +604,6 @@ void main() {
#define SHADER_IS_SRGB false
#define SHADER_SPACE_FAR 0.0
-/* Specialization Constants */
-
-#if !defined(MODE_RENDER_DEPTH)
-
-#if !defined(MODE_UNSHADED)
-
-layout(constant_id = 0) const bool sc_use_light_projector = false;
-layout(constant_id = 1) const bool sc_use_light_soft_shadows = false;
-layout(constant_id = 2) const bool sc_use_directional_soft_shadows = false;
-
-layout(constant_id = 3) const uint sc_soft_shadow_samples = 4;
-layout(constant_id = 4) const uint sc_penumbra_shadow_samples = 4;
-
-layout(constant_id = 5) const uint sc_directional_soft_shadow_samples = 4;
-layout(constant_id = 6) const uint sc_directional_penumbra_shadow_samples = 4;
-
-layout(constant_id = 8) const bool sc_projector_use_mipmaps = true;
-
-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
-
-layout(constant_id = 7) const bool sc_decal_use_mipmaps = true;
-layout(constant_id = 13) const bool sc_disable_decals = false;
-layout(constant_id = 14) const bool sc_disable_fog = false;
-layout(constant_id = 16) const bool sc_use_depth_fog = false;
-
-#endif //!MODE_RENDER_DEPTH
-
-layout(constant_id = 15) const float sc_luminance_multiplier = 2.0;
-
/* Include our forward mobile UBOs definitions etc. */
#include "scene_forward_mobile_inc.glsl"
@@ -875,7 +832,7 @@ vec4 fog_process(vec3 vertex) {
float fog_amount = 0.0;
- if (sc_use_depth_fog) {
+ if (sc_use_depth_fog()) {
float fog_z = smoothstep(scene_data_block.data.fog_depth_begin, scene_data_block.data.fog_depth_end, length(vertex));
float fog_quad_amount = pow(fog_z, scene_data_block.data.fog_depth_curve) * scene_data_block.data.fog_density;
fog_amount = fog_quad_amount;
@@ -901,6 +858,14 @@ vec4 fog_process(vec3 vertex) {
#define scene_data scene_data_block.data
void main() {
+#ifdef UBERSHADER
+ bool front_facing = gl_FrontFacing;
+ if (uc_cull_mode() == POLYGON_CULL_BACK && !front_facing) {
+ discard;
+ } else if (uc_cull_mode() == POLYGON_CULL_FRONT && front_facing) {
+ discard;
+ }
+#endif
#ifdef MODE_DUAL_PARABOLOID
if (dp_clip > 0.0)
@@ -1126,7 +1091,7 @@ void main() {
// to maximize VGPR usage
// Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky.
- if (!sc_disable_fog && scene_data.fog_enabled) {
+ if (!sc_disable_fog() && scene_data.fog_enabled) {
fog = fog_process(vertex);
}
@@ -1145,7 +1110,7 @@ void main() {
vec3 vertex_ddx = dFdx(vertex);
vec3 vertex_ddy = dFdy(vertex);
- if (!sc_disable_decals) { //Decals
+ if (!sc_disable_decals()) { //Decals
// must implement
uint decal_indices = instances.data[draw_call.instance_index].decals.x;
@@ -1183,7 +1148,7 @@ void main() {
if (decals.data[decal_index].albedo_rect != vec4(0.0)) {
//has albedo
vec4 decal_albedo;
- if (sc_decal_use_mipmaps) {
+ if (sc_decal_use_mipmaps()) {
decal_albedo = textureGrad(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].albedo_rect.zw + decals.data[decal_index].albedo_rect.xy, ddx * decals.data[decal_index].albedo_rect.zw, ddy * decals.data[decal_index].albedo_rect.zw);
} else {
decal_albedo = textureLod(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].albedo_rect.zw + decals.data[decal_index].albedo_rect.xy, 0.0);
@@ -1194,7 +1159,7 @@ void main() {
if (decals.data[decal_index].normal_rect != vec4(0.0)) {
vec3 decal_normal;
- if (sc_decal_use_mipmaps) {
+ if (sc_decal_use_mipmaps()) {
decal_normal = textureGrad(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].normal_rect.zw + decals.data[decal_index].normal_rect.xy, ddx * decals.data[decal_index].normal_rect.zw, ddy * decals.data[decal_index].normal_rect.zw).xyz;
} else {
decal_normal = textureLod(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].normal_rect.zw + decals.data[decal_index].normal_rect.xy, 0.0).xyz;
@@ -1209,7 +1174,7 @@ void main() {
if (decals.data[decal_index].orm_rect != vec4(0.0)) {
vec3 decal_orm;
- if (sc_decal_use_mipmaps) {
+ if (sc_decal_use_mipmaps()) {
decal_orm = textureGrad(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].orm_rect.zw + decals.data[decal_index].orm_rect.xy, ddx * decals.data[decal_index].orm_rect.zw, ddy * decals.data[decal_index].orm_rect.zw).xyz;
} else {
decal_orm = textureLod(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].orm_rect.zw + decals.data[decal_index].orm_rect.xy, 0.0).xyz;
@@ -1222,7 +1187,7 @@ void main() {
if (decals.data[decal_index].emission_rect != vec4(0.0)) {
//emission is additive, so its independent from albedo
- if (sc_decal_use_mipmaps) {
+ if (sc_decal_use_mipmaps()) {
emission += textureGrad(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].emission_rect.zw + decals.data[decal_index].emission_rect.xy, ddx * decals.data[decal_index].emission_rect.zw, ddy * decals.data[decal_index].emission_rect.zw).xyz * decals.data[decal_index].emission_energy * fade;
} else {
emission += textureLod(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].emission_rect.zw + decals.data[decal_index].emission_rect.xy, 0.0).xyz * decals.data[decal_index].emission_energy * fade;
@@ -1286,7 +1251,7 @@ void main() {
specular_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
- specular_light *= sc_luminance_multiplier;
+ specular_light *= sc_luminance_multiplier();
specular_light *= scene_data.IBL_exposure_normalization;
specular_light *= horizon * horizon;
specular_light *= scene_data.ambient_light_color_energy.a;
@@ -1308,7 +1273,7 @@ void main() {
#else
vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
- cubemap_ambient *= sc_luminance_multiplier;
+ cubemap_ambient *= sc_luminance_multiplier();
cubemap_ambient *= scene_data.IBL_exposure_normalization;
ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix);
}
@@ -1394,7 +1359,7 @@ void main() {
vec3 lm_light_l1_0;
vec3 lm_light_l1p1;
- if (sc_use_lightmap_bicubic_filter) {
+ 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 - vec3(0.5)) * 2.0;
lm_light_l1_0 = (textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 2.0), lightmaps.data[ofs].light_texture_size).rgb - vec3(0.5)) * 2.0;
@@ -1414,7 +1379,7 @@ void main() {
ambient_light += lm_light_l1_0 * n.z * (lm_light_l0 * exposure_normalization * 4.0);
ambient_light += lm_light_l1p1 * n.x * (lm_light_l0 * exposure_normalization * 4.0);
} else {
- if (sc_use_lightmap_bicubic_filter) {
+ 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;
@@ -1428,7 +1393,7 @@ void main() {
// skipping ssao, do we remove ssao totally?
- if (!sc_disable_reflection_probes) { //Reflection probes
+ if (!sc_disable_reflection_probes()) { //Reflection probes
vec4 reflection_accum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 ambient_accum = vec4(0.0, 0.0, 0.0, 0.0);
@@ -1522,7 +1487,7 @@ void main() {
specular_light += specular_light_interp.rgb * f0;
#endif
- if (!sc_disable_directional_lights) { //directional light
+ if (!sc_disable_directional_lights()) { //directional light
#ifndef SHADOWS_DISABLED
// Do shadow and lighting in two passes to reduce register pressure
uint shadow0 = 0;
@@ -1637,7 +1602,6 @@ void main() {
#endif
#undef BIAS_FUNC
}
-#endif
if (i < 4) {
shadow0 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << (i * 8);
@@ -1645,6 +1609,7 @@ void main() {
shadow1 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << ((i - 4) * 8);
}
}
+#endif // SHADOWS_DISABLED
#ifndef USE_VERTEX_LIGHTING
for (uint i = 0; i < scene_data.directional_light_count; i++) {
@@ -1681,7 +1646,7 @@ void main() {
shadow = 1.0;
#endif
- float size_A = sc_use_light_soft_shadows ? directional_lights.data[i].size : 0.0;
+ float size_A = sc_use_light_soft_shadows() ? directional_lights.data[i].size : 0.0;
light_compute(normal, directional_lights.data[i].direction, view, size_A,
directional_lights.data[i].color * directional_lights.data[i].energy * tint,
@@ -1713,7 +1678,7 @@ void main() {
} //directional light
#ifndef USE_VERTEX_LIGHTING
- if (!sc_disable_omni_lights) { //omni lights
+ if (!sc_disable_omni_lights()) { //omni lights
uint light_indices = instances.data[draw_call.instance_index].omni_lights.x;
for (uint i = 0; i < 8; i++) {
uint light_index = light_indices & 0xFF;
@@ -1758,7 +1723,7 @@ void main() {
}
} //omni lights
- if (!sc_disable_spot_lights) { //spot lights
+ if (!sc_disable_spot_lights()) { //spot lights
uint light_indices = instances.data[draw_call.instance_index].spot_lights.x;
for (uint i = 0; i < 8; i++) {
@@ -1894,7 +1859,7 @@ void main() {
// On mobile we use a UNORM buffer with 10bpp which results in a range from 0.0 - 1.0 resulting in HDR breaking
// We divide by sc_luminance_multiplier to support a range from 0.0 - 2.0 both increasing precision on bright and darker images
- frag_color.rgb = frag_color.rgb / sc_luminance_multiplier;
+ frag_color.rgb = frag_color.rgb / sc_luminance_multiplier();
#ifdef PREMUL_ALPHA_USED
frag_color.rgb *= premul_alpha;
#endif
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 d971ff04c5..495e52a29e 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
@@ -20,9 +20,128 @@ layout(push_constant, std430) uniform DrawCall {
vec2 uv_offset;
uint instance_index;
uint pad;
+#ifdef UBERSHADER
+ uint sc_packed_0;
+ float sc_packed_1;
+ uint sc_packed_2;
+ uint uc_packed_0;
+#endif
}
draw_call;
+/* Specialization Constants */
+
+#ifdef UBERSHADER
+
+#define POLYGON_CULL_DISABLED 0
+#define POLYGON_CULL_FRONT 1
+#define POLYGON_CULL_BACK 2
+
+// Pull the constants from the draw call's push constants.
+uint sc_packed_0() {
+ return draw_call.sc_packed_0;
+}
+
+float sc_packed_1() {
+ return draw_call.sc_packed_1;
+}
+
+uint uc_cull_mode() {
+ return (draw_call.uc_packed_0 >> 0) & 3U;
+}
+
+#else
+
+// Pull the constants from the pipeline's specialization constants.
+layout(constant_id = 0) const uint pso_sc_packed_0 = 0;
+layout(constant_id = 1) const float pso_sc_packed_1 = 2.0;
+
+uint sc_packed_0() {
+ return pso_sc_packed_0;
+}
+
+float sc_packed_1() {
+ return pso_sc_packed_1;
+}
+
+#endif
+
+bool sc_use_light_projector() {
+ return ((sc_packed_0() >> 0) & 1U) != 0;
+}
+
+bool sc_use_light_soft_shadows() {
+ return ((sc_packed_0() >> 1) & 1U) != 0;
+}
+
+bool sc_use_directional_soft_shadows() {
+ return ((sc_packed_0() >> 2) & 1U) != 0;
+}
+
+bool sc_decal_use_mipmaps() {
+ return ((sc_packed_0() >> 3) & 1U) != 0;
+}
+
+bool sc_projector_use_mipmaps() {
+ return ((sc_packed_0() >> 4) & 1U) != 0;
+}
+
+bool sc_disable_omni_lights() {
+ return ((sc_packed_0() >> 5) & 1U) != 0;
+}
+
+bool sc_disable_spot_lights() {
+ return ((sc_packed_0() >> 6) & 1U) != 0;
+}
+
+bool sc_disable_reflection_probes() {
+ return ((sc_packed_0() >> 7) & 1U) != 0;
+}
+
+bool sc_disable_directional_lights() {
+ return ((sc_packed_0() >> 8) & 1U) != 0;
+}
+
+bool sc_disable_decals() {
+ return ((sc_packed_0() >> 9) & 1U) != 0;
+}
+
+bool sc_disable_fog() {
+ return ((sc_packed_0() >> 10) & 1U) != 0;
+}
+
+bool sc_use_depth_fog() {
+ return ((sc_packed_0() >> 11) & 1U) != 0;
+}
+
+bool sc_is_multimesh() {
+ return ((sc_packed_0() >> 12) & 1U) != 0;
+}
+
+bool sc_use_lightmap_bicubic_filter() {
+ return ((sc_packed_0() >> 13) & 1U) != 0;
+}
+
+uint sc_soft_shadow_samples() {
+ return (sc_packed_0() >> 16) & 15U;
+}
+
+uint sc_penumbra_shadow_samples() {
+ return (sc_packed_0() >> 20) & 15U;
+}
+
+uint sc_directional_soft_shadow_samples() {
+ return (sc_packed_0() >> 24) & 15U;
+}
+
+uint sc_directional_penumbra_shadow_samples() {
+ return (sc_packed_0() >> 28) & 15U;
+}
+
+float sc_luminance_multiplier() {
+ return sc_packed_1();
+}
+
/* Set 0: Base Pass (never changes) */
#include "../light_data_inc.glsl"
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
index 14a4dc7089..a1a185d0fd 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
@@ -269,7 +269,7 @@ float sample_directional_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, ve
float depth = coord.z;
//if only one sample is taken, take it from the center
- if (sc_directional_soft_shadow_samples == 0) {
+ if (sc_directional_soft_shadow_samples() == 0) {
return textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos, depth, 1.0));
}
@@ -283,11 +283,11 @@ float sample_directional_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, ve
float avg = 0.0;
- for (uint i = 0; i < sc_directional_soft_shadow_samples; i++) {
+ for (uint i = 0; i < sc_directional_soft_shadow_samples(); i++) {
avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + shadow_pixel_size * (disk_rotation * scene_data_block.data.directional_soft_shadow_kernel[i].xy), depth, 1.0));
}
- return avg * (1.0 / float(sc_directional_soft_shadow_samples));
+ return avg * (1.0 / float(sc_directional_soft_shadow_samples()));
}
float sample_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec3 coord, float taa_frame_count) {
@@ -295,7 +295,7 @@ float sample_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec3 coord, fl
float depth = coord.z;
//if only one sample is taken, take it from the center
- if (sc_soft_shadow_samples == 0) {
+ if (sc_soft_shadow_samples() == 0) {
return textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos, depth, 1.0));
}
@@ -309,16 +309,16 @@ float sample_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec3 coord, fl
float avg = 0.0;
- for (uint i = 0; i < sc_soft_shadow_samples; i++) {
+ for (uint i = 0; i < sc_soft_shadow_samples(); i++) {
avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos + shadow_pixel_size * (disk_rotation * scene_data_block.data.soft_shadow_kernel[i].xy), depth, 1.0));
}
- return avg * (1.0 / float(sc_soft_shadow_samples));
+ return avg * (1.0 / float(sc_soft_shadow_samples()));
}
float sample_omni_pcf_shadow(texture2D shadow, float blur_scale, vec2 coord, vec4 uv_rect, vec2 flip_offset, float depth, float taa_frame_count) {
//if only one sample is taken, take it from the center
- if (sc_soft_shadow_samples == 0) {
+ if (sc_soft_shadow_samples() == 0) {
vec2 pos = coord * 0.5 + 0.5;
pos = uv_rect.xy + pos * uv_rect.zw;
return textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos, depth, 1.0));
@@ -335,7 +335,7 @@ float sample_omni_pcf_shadow(texture2D shadow, float blur_scale, vec2 coord, vec
float avg = 0.0;
vec2 offset_scale = blur_scale * 2.0 * scene_data_block.data.shadow_atlas_pixel_size / uv_rect.zw;
- for (uint i = 0; i < sc_soft_shadow_samples; i++) {
+ for (uint i = 0; i < sc_soft_shadow_samples(); i++) {
vec2 offset = offset_scale * (disk_rotation * scene_data_block.data.soft_shadow_kernel[i].xy);
vec2 sample_coord = coord + offset;
@@ -356,7 +356,7 @@ float sample_omni_pcf_shadow(texture2D shadow, float blur_scale, vec2 coord, vec
avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(sample_coord, depth, 1.0));
}
- return avg * (1.0 / float(sc_soft_shadow_samples));
+ return avg * (1.0 / float(sc_soft_shadow_samples()));
}
float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex_scale, float taa_frame_count) {
@@ -372,7 +372,7 @@ float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex
disk_rotation = mat2(vec2(cr, -sr), vec2(sr, cr));
}
- for (uint i = 0; i < sc_directional_penumbra_shadow_samples; i++) {
+ for (uint i = 0; i < sc_directional_penumbra_shadow_samples(); i++) {
vec2 suv = pssm_coord.xy + (disk_rotation * scene_data_block.data.directional_penumbra_shadow_kernel[i].xy) * tex_scale;
float d = textureLod(sampler2D(shadow, SAMPLER_LINEAR_CLAMP), suv, 0.0).r;
if (d > pssm_coord.z) {
@@ -388,12 +388,12 @@ float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex
tex_scale *= penumbra;
float s = 0.0;
- for (uint i = 0; i < sc_directional_penumbra_shadow_samples; i++) {
+ for (uint i = 0; i < sc_directional_penumbra_shadow_samples(); i++) {
vec2 suv = pssm_coord.xy + (disk_rotation * scene_data_block.data.directional_penumbra_shadow_kernel[i].xy) * tex_scale;
s += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(suv, pssm_coord.z, 1.0));
}
- return s / float(sc_directional_penumbra_shadow_samples);
+ return s / float(sc_directional_penumbra_shadow_samples());
} else {
//no blockers found, so no shadow
@@ -434,7 +434,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal, float taa_fr
float shadow;
- if (sc_use_light_soft_shadows && omni_lights.data[idx].soft_shadow_size > 0.0) {
+ if (sc_use_light_soft_shadows() && omni_lights.data[idx].soft_shadow_size > 0.0) {
//soft shadow
//find blocker
@@ -459,7 +459,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal, float taa_fr
tangent *= omni_lights.data[idx].soft_shadow_size * omni_lights.data[idx].soft_shadow_scale;
bitangent *= omni_lights.data[idx].soft_shadow_size * omni_lights.data[idx].soft_shadow_scale;
- for (uint i = 0; i < sc_penumbra_shadow_samples; i++) {
+ for (uint i = 0; i < sc_penumbra_shadow_samples(); i++) {
vec2 disk = disk_rotation * scene_data_block.data.penumbra_shadow_kernel[i].xy;
vec3 pos = local_vert + tangent * disk.x + bitangent * disk.y;
@@ -495,7 +495,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal, float taa_fr
z_norm += omni_lights.data[idx].inv_radius * omni_lights.data[idx].shadow_bias;
shadow = 0.0;
- for (uint i = 0; i < sc_penumbra_shadow_samples; i++) {
+ for (uint i = 0; i < sc_penumbra_shadow_samples(); i++) {
vec2 disk = disk_rotation * scene_data_block.data.penumbra_shadow_kernel[i].xy;
vec3 pos = local_vert + tangent * disk.x + bitangent * disk.y;
@@ -516,7 +516,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal, float taa_fr
shadow += textureProj(sampler2DShadow(shadow_atlas, shadow_sampler), vec4(pos.xy, z_norm, 1.0));
}
- shadow /= float(sc_penumbra_shadow_samples);
+ shadow /= float(sc_penumbra_shadow_samples());
shadow = mix(1.0, shadow, omni_lights.data[idx].shadow_opacity);
} else {
@@ -574,7 +574,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
float size_A = 0.0;
- if (sc_use_light_soft_shadows && omni_lights.data[idx].size > 0.0) {
+ if (sc_use_light_soft_shadows() && omni_lights.data[idx].size > 0.0) {
float t = omni_lights.data[idx].size / max(0.001, light_length);
size_A = max(0.0, 1.0 - 1 / sqrt(1 + t * t));
}
@@ -616,7 +616,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
#endif // !SHADOWS_DISABLED
#endif // LIGHT_TRANSMITTANCE_USED
- if (sc_use_light_projector && omni_lights.data[idx].projector_rect != vec4(0.0)) {
+ if (sc_use_light_projector() && omni_lights.data[idx].projector_rect != vec4(0.0)) {
vec3 local_v = (omni_lights.data[idx].shadow_matrix * vec4(vertex, 1.0)).xyz;
local_v = normalize(local_v);
@@ -632,7 +632,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
local_v.xy = local_v.xy * 0.5 + 0.5;
vec2 proj_uv = local_v.xy * atlas_rect.zw;
- if (sc_projector_use_mipmaps) {
+ if (sc_projector_use_mipmaps()) {
vec2 proj_uv_ddx;
vec2 proj_uv_ddy;
{
@@ -716,7 +716,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal, float taa_fr
splane /= splane.w;
float shadow;
- if (sc_use_light_soft_shadows && spot_lights.data[idx].soft_shadow_size > 0.0) {
+ if (sc_use_light_soft_shadows() && spot_lights.data[idx].soft_shadow_size > 0.0) {
//soft shadow
//find blocker
@@ -737,7 +737,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal, float taa_fr
float uv_size = spot_lights.data[idx].soft_shadow_size * z_norm * spot_lights.data[idx].soft_shadow_scale;
vec2 clamp_max = spot_lights.data[idx].atlas_rect.xy + spot_lights.data[idx].atlas_rect.zw;
- for (uint i = 0; i < sc_penumbra_shadow_samples; i++) {
+ for (uint i = 0; i < sc_penumbra_shadow_samples(); i++) {
vec2 suv = shadow_uv + (disk_rotation * scene_data_block.data.penumbra_shadow_kernel[i].xy) * uv_size;
suv = clamp(suv, spot_lights.data[idx].atlas_rect.xy, clamp_max);
float d = textureLod(sampler2D(shadow_atlas, SAMPLER_LINEAR_CLAMP), suv, 0.0).r;
@@ -754,13 +754,13 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal, float taa_fr
uv_size *= penumbra;
shadow = 0.0;
- for (uint i = 0; i < sc_penumbra_shadow_samples; i++) {
+ for (uint i = 0; i < sc_penumbra_shadow_samples(); i++) {
vec2 suv = shadow_uv + (disk_rotation * scene_data_block.data.penumbra_shadow_kernel[i].xy) * uv_size;
suv = clamp(suv, spot_lights.data[idx].atlas_rect.xy, clamp_max);
shadow += textureProj(sampler2DShadow(shadow_atlas, shadow_sampler), vec4(suv, splane.z, 1.0));
}
- shadow /= float(sc_penumbra_shadow_samples);
+ shadow /= float(sc_penumbra_shadow_samples());
shadow = mix(1.0, shadow, spot_lights.data[idx].shadow_opacity);
} else {
@@ -831,7 +831,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
float size_A = 0.0;
- if (sc_use_light_soft_shadows && spot_lights.data[idx].size > 0.0) {
+ if (sc_use_light_soft_shadows() && spot_lights.data[idx].size > 0.0) {
float t = spot_lights.data[idx].size / max(0.001, light_length);
size_A = max(0.0, 1.0 - 1 / sqrt(1 + t * t));
}
@@ -859,13 +859,13 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
#endif // !SHADOWS_DISABLED
#endif // LIGHT_TRANSMITTANCE_USED
- if (sc_use_light_projector && spot_lights.data[idx].projector_rect != vec4(0.0)) {
+ if (sc_use_light_projector() && spot_lights.data[idx].projector_rect != vec4(0.0)) {
vec4 splane = (spot_lights.data[idx].shadow_matrix * vec4(vertex, 1.0));
splane /= splane.w;
vec2 proj_uv = splane.xy * spot_lights.data[idx].projector_rect.zw;
- if (sc_projector_use_mipmaps) {
+ if (sc_projector_use_mipmaps()) {
//ensure we have proper mipmaps
vec4 splane_ddx = (spot_lights.data[idx].shadow_matrix * vec4(vertex + vertex_ddx, 1.0));
splane_ddx /= splane_ddx.w;
@@ -940,7 +940,7 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 ref_vec, vec3 normal,
vec4 reflection;
- reflection.rgb = textureLod(samplerCubeArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_ref_vec, reflections.data[ref_index].index), sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb * sc_luminance_multiplier;
+ reflection.rgb = textureLod(samplerCubeArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_ref_vec, reflections.data[ref_index].index), sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb * sc_luminance_multiplier();
reflection.rgb *= reflections.data[ref_index].exposure_normalization;
if (reflections.data[ref_index].exterior) {
reflection.rgb = mix(specular_light, reflection.rgb, blend);
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
index b07063cfda..8f71909154 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
@@ -313,6 +313,12 @@ void LightStorage::light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMo
light->version++;
light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT);
+
+ if (p_mode == RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID) {
+ shadow_dual_paraboloid_used = true;
+ } else if (p_mode == RS::LIGHT_OMNI_SHADOW_CUBE) {
+ shadow_cubemaps_used = true;
+ }
}
RS::LightOmniShadowMode LightStorage::light_omni_get_shadow_mode(RID p_light) {
@@ -1478,21 +1484,20 @@ bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_
//reflection atlas was unused, create:
RD::TextureFormat tf;
tf.array_layers = 6 * atlas->count;
- tf.format = RendererSceneRenderRD::get_singleton()->_render_buffers_get_color_format();
+ tf.format = get_reflection_probe_color_format();
tf.texture_type = RD::TEXTURE_TYPE_CUBE_ARRAY;
tf.mipmaps = mipmaps;
tf.width = atlas->size;
tf.height = atlas->size;
- tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage() ? RD::TEXTURE_USAGE_STORAGE_BIT : 0);
-
+ tf.usage_bits = get_reflection_probe_color_usage_bits();
atlas->reflection = RD::get_singleton()->texture_create(tf, RD::TextureView());
}
{
RD::TextureFormat tf;
- tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D32_SFLOAT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D32_SFLOAT : RD::DATA_FORMAT_X8_D24_UNORM_PACK32;
+ tf.format = get_reflection_probe_depth_format();
tf.width = atlas->size;
tf.height = atlas->size;
- tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
+ tf.usage_bits = get_reflection_probe_depth_usage_bits();
atlas->depth_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
}
atlas->reflections.resize(atlas->count);
@@ -1763,6 +1768,22 @@ void LightStorage::update_reflection_probe_buffer(RenderDataRD *p_render_data, c
}
}
+RD::DataFormat LightStorage::get_reflection_probe_color_format() {
+ return RendererSceneRenderRD::get_singleton()->_render_buffers_get_color_format();
+}
+
+uint32_t LightStorage::get_reflection_probe_color_usage_bits() {
+ return RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | (RendererSceneRenderRD::get_singleton()->_render_buffers_can_be_storage() ? RD::TEXTURE_USAGE_STORAGE_BIT : 0);
+}
+
+RD::DataFormat LightStorage::get_reflection_probe_depth_format() {
+ return RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D32_SFLOAT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D32_SFLOAT : RD::DATA_FORMAT_X8_D24_UNORM_PACK32;
+}
+
+uint32_t LightStorage::get_reflection_probe_depth_usage_bits() {
+ return RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
+}
+
/* LIGHTMAP API */
RID LightStorage::lightmap_allocate() {
@@ -1996,10 +2017,10 @@ void LightStorage::shadow_atlas_free(RID p_atlas) {
void LightStorage::_update_shadow_atlas(ShadowAtlas *shadow_atlas) {
if (shadow_atlas->size > 0 && shadow_atlas->depth.is_null()) {
RD::TextureFormat tf;
- tf.format = shadow_atlas->use_16_bits ? RD::DATA_FORMAT_D16_UNORM : RD::DATA_FORMAT_D32_SFLOAT;
+ tf.format = get_shadow_atlas_depth_format(shadow_atlas->use_16_bits);
tf.width = shadow_atlas->size;
tf.height = shadow_atlas->size;
- tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ tf.usage_bits = get_shadow_atlas_depth_usage_bits();
shadow_atlas->depth = RD::get_singleton()->texture_create(tf, RD::TextureView());
Vector<RID> fb_tex;
@@ -2384,15 +2405,23 @@ void LightStorage::shadow_atlas_update(RID p_atlas) {
_update_shadow_atlas(shadow_atlas);
}
+RD::DataFormat LightStorage::get_shadow_atlas_depth_format(bool p_16_bits) {
+ return p_16_bits ? RD::DATA_FORMAT_D16_UNORM : RD::DATA_FORMAT_D32_SFLOAT;
+}
+
+uint32_t LightStorage::get_shadow_atlas_depth_usage_bits() {
+ return RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+}
+
/* DIRECTIONAL SHADOW */
void LightStorage::update_directional_shadow_atlas() {
if (directional_shadow.depth.is_null() && directional_shadow.size > 0) {
RD::TextureFormat tf;
- tf.format = directional_shadow.use_16_bits ? RD::DATA_FORMAT_D16_UNORM : RD::DATA_FORMAT_D32_SFLOAT;
+ tf.format = get_shadow_atlas_depth_format(directional_shadow.use_16_bits);
tf.width = directional_shadow.size;
tf.height = directional_shadow.size;
- tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ tf.usage_bits = get_shadow_atlas_depth_usage_bits();
directional_shadow.depth = RD::get_singleton()->texture_create(tf, RD::TextureView());
Vector<RID> fb_tex;
@@ -2477,12 +2506,12 @@ LightStorage::ShadowCubemap *LightStorage::_get_shadow_cubemap(int p_size) {
ShadowCubemap sc;
{
RD::TextureFormat tf;
- tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D32_SFLOAT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D32_SFLOAT : RD::DATA_FORMAT_X8_D24_UNORM_PACK32;
+ tf.format = get_cubemap_depth_format();
tf.width = p_size;
tf.height = p_size;
tf.texture_type = RD::TEXTURE_TYPE_CUBE;
tf.array_layers = 6;
- tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
+ tf.usage_bits = get_cubemap_depth_usage_bits();
sc.cubemap = RD::get_singleton()->texture_create(tf, RD::TextureView());
}
@@ -2510,3 +2539,19 @@ RID LightStorage::get_cubemap_fb(int p_size, int p_pass) {
return cubemap->side_fb[p_pass];
}
+
+RD::DataFormat LightStorage::get_cubemap_depth_format() {
+ return RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D32_SFLOAT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D32_SFLOAT : RD::DATA_FORMAT_X8_D24_UNORM_PACK32;
+}
+
+uint32_t LightStorage::get_cubemap_depth_usage_bits() {
+ return RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
+}
+
+bool LightStorage::get_shadow_cubemaps_used() const {
+ return shadow_cubemaps_used;
+}
+
+bool LightStorage::get_shadow_dual_paraboloid_used() const {
+ return shadow_dual_paraboloid_used;
+}
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h
index 1db58d72f9..59303e8a73 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h
@@ -434,6 +434,11 @@ private:
HashMap<int, ShadowCubemap> shadow_cubemaps;
ShadowCubemap *_get_shadow_cubemap(int p_size);
+ /* PIPELINE HINTS */
+
+ bool shadow_cubemaps_used = false;
+ bool shadow_dual_paraboloid_used = false;
+
public:
static LightStorage *get_singleton();
@@ -938,6 +943,10 @@ public:
void set_max_reflection_probes(const uint32_t p_max_reflection_probes);
RID get_reflection_probe_buffer() { return reflection_buffer; }
void update_reflection_probe_buffer(RenderDataRD *p_render_data, const PagedArray<RID> &p_reflections, const Transform3D &p_camera_inverse_transform, RID p_environment);
+ static RD::DataFormat get_reflection_probe_color_format();
+ static uint32_t get_reflection_probe_color_usage_bits();
+ static RD::DataFormat get_reflection_probe_depth_format();
+ static uint32_t get_reflection_probe_depth_usage_bits();
/* LIGHTMAP */
@@ -1079,6 +1088,8 @@ public:
}
virtual void shadow_atlas_update(RID p_atlas) override;
+ static RD::DataFormat get_shadow_atlas_depth_format(bool p_16_bits);
+ static uint32_t get_shadow_atlas_depth_usage_bits();
/* DIRECTIONAL SHADOW */
@@ -1109,6 +1120,13 @@ public:
RID get_cubemap(int p_size);
RID get_cubemap_fb(int p_size, int p_pass);
+ static RD::DataFormat get_cubemap_depth_format();
+ static uint32_t get_cubemap_depth_usage_bits();
+
+ /* PIPELINE HINTS */
+
+ bool get_shadow_cubemaps_used() const;
+ bool get_shadow_dual_paraboloid_used() const;
};
} // namespace RendererRD
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index 3bfc1bd15c..17a18b2766 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -633,6 +633,93 @@ bool MaterialStorage::ShaderData::is_parameter_texture(const StringName &p_param
return uniforms[p_param].is_texture();
}
+RD::PipelineColorBlendState::Attachment MaterialStorage::ShaderData::blend_mode_to_blend_attachment(BlendMode p_mode) {
+ RD::PipelineColorBlendState::Attachment attachment;
+
+ switch (p_mode) {
+ case BLEND_MODE_MIX: {
+ attachment.enable_blend = true;
+ attachment.alpha_blend_op = RD::BLEND_OP_ADD;
+ attachment.color_blend_op = RD::BLEND_OP_ADD;
+ attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
+ attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ } break;
+ case BLEND_MODE_ADD: {
+ attachment.enable_blend = true;
+ attachment.alpha_blend_op = RD::BLEND_OP_ADD;
+ attachment.color_blend_op = RD::BLEND_OP_ADD;
+ attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
+ attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
+ attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+ } break;
+ case BLEND_MODE_SUB: {
+ attachment.enable_blend = true;
+ attachment.alpha_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
+ attachment.color_blend_op = RD::BLEND_OP_REVERSE_SUBTRACT;
+ attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
+ attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
+ attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+ } break;
+ case BLEND_MODE_MUL: {
+ attachment.enable_blend = true;
+ attachment.alpha_blend_op = RD::BLEND_OP_ADD;
+ attachment.color_blend_op = RD::BLEND_OP_ADD;
+ attachment.src_color_blend_factor = RD::BLEND_FACTOR_DST_COLOR;
+ attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ZERO;
+ attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_DST_ALPHA;
+ attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
+ } break;
+ case BLEND_MODE_ALPHA_TO_COVERAGE: {
+ attachment.enable_blend = true;
+ attachment.alpha_blend_op = RD::BLEND_OP_ADD;
+ attachment.color_blend_op = RD::BLEND_OP_ADD;
+ attachment.src_color_blend_factor = RD::BLEND_FACTOR_SRC_ALPHA;
+ attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ZERO;
+ } break;
+ case BLEND_MODE_PREMULTIPLIED_ALPHA: {
+ attachment.enable_blend = true;
+ attachment.alpha_blend_op = RD::BLEND_OP_ADD;
+ attachment.color_blend_op = RD::BLEND_OP_ADD;
+ attachment.src_color_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.dst_color_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ attachment.src_alpha_blend_factor = RD::BLEND_FACTOR_ONE;
+ attachment.dst_alpha_blend_factor = RD::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ } break;
+ case BLEND_MODE_DISABLED:
+ default: {
+ // Use default attachment values.
+ } break;
+ }
+
+ return attachment;
+}
+
+bool MaterialStorage::ShaderData::blend_mode_uses_blend_alpha(BlendMode p_mode) {
+ switch (p_mode) {
+ case BLEND_MODE_MIX:
+ return false;
+ case BLEND_MODE_ADD:
+ return true;
+ case BLEND_MODE_SUB:
+ return true;
+ case BLEND_MODE_MUL:
+ return true;
+ case BLEND_MODE_ALPHA_TO_COVERAGE:
+ return false;
+ case BLEND_MODE_PREMULTIPLIED_ALPHA:
+ return true;
+ case BLEND_MODE_DISABLED:
+ default:
+ return false;
+ }
+}
+
///////////////////////////////////////////////////////////////////////////
// MaterialStorage::MaterialData
@@ -2021,6 +2108,7 @@ void MaterialStorage::_material_uniform_set_erased(void *p_material) {
}
void MaterialStorage::_material_queue_update(Material *material, bool p_uniform, bool p_texture) {
+ MutexLock lock(material_update_list_mutex);
material->uniform_dirty = material->uniform_dirty || p_uniform;
material->texture_dirty = material->texture_dirty || p_texture;
@@ -2032,6 +2120,7 @@ void MaterialStorage::_material_queue_update(Material *material, bool p_uniform,
}
void MaterialStorage::_update_queued_materials() {
+ MutexLock lock(material_update_list_mutex);
while (material_update_list.first()) {
Material *material = material_update_list.first()->self();
bool uniforms_changed = false;
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.h b/servers/rendering/renderer_rd/storage_rd/material_storage.h
index 9c53450462..08c1064dcb 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.h
@@ -56,6 +56,16 @@ public:
};
struct ShaderData {
+ enum BlendMode {
+ BLEND_MODE_MIX,
+ BLEND_MODE_ADD,
+ BLEND_MODE_SUB,
+ BLEND_MODE_MUL,
+ BLEND_MODE_ALPHA_TO_COVERAGE,
+ BLEND_MODE_PREMULTIPLIED_ALPHA,
+ BLEND_MODE_DISABLED
+ };
+
String path;
HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms;
HashMap<StringName, HashMap<int, RID>> default_texture_params;
@@ -73,6 +83,9 @@ public:
virtual RS::ShaderNativeSourceCode get_native_source_code() const { return RS::ShaderNativeSourceCode(); }
virtual ~ShaderData() {}
+
+ static RD::PipelineColorBlendState::Attachment blend_mode_to_blend_attachment(BlendMode p_mode);
+ static bool blend_mode_uses_blend_alpha(BlendMode p_mode);
};
struct MaterialData {
@@ -244,6 +257,7 @@ private:
Material *get_material(RID p_rid) { return material_owner.get_or_null(p_rid); };
SelfList<Material>::List material_update_list;
+ Mutex material_update_list_mutex;
static void _material_uniform_set_erased(void *p_material);
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
index 9ae39691dc..0d468ad1e3 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
@@ -1142,97 +1142,71 @@ void MeshStorage::update_mesh_instances() {
RD::get_singleton()->compute_list_end();
}
-void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis, uint32_t p_current_buffer, uint32_t p_previous_buffer) {
+RD::VertexFormatID MeshStorage::_mesh_surface_generate_vertex_format(uint64_t p_surface_format, uint64_t p_input_mask, bool p_instanced_surface, bool p_input_motion_vectors, uint32_t &r_position_stride) {
Vector<RD::VertexAttribute> attributes;
- Vector<RID> buffers;
- Vector<uint64_t> offsets;
-
- uint32_t position_stride = 0;
uint32_t normal_tangent_stride = 0;
uint32_t attribute_stride = 0;
uint32_t skin_stride = 0;
+ r_position_stride = 0;
+
for (int i = 0; i < RS::ARRAY_INDEX; i++) {
RD::VertexAttribute vd;
- RID buffer;
vd.location = i;
- uint64_t offset = 0;
- if (!(s->format & (1ULL << i))) {
- // Not supplied by surface, use default value
- buffer = mesh_default_rd_buffers[i];
+ if (!(p_surface_format & (1ULL << i))) {
vd.stride = 0;
switch (i) {
- case RS::ARRAY_VERTEX: {
- vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT;
-
- } break;
- case RS::ARRAY_NORMAL: {
+ case RS::ARRAY_VERTEX:
+ case RS::ARRAY_NORMAL:
vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT;
- } break;
- case RS::ARRAY_TANGENT: {
- vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
- } break;
- case RS::ARRAY_COLOR: {
- vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
-
- } break;
- case RS::ARRAY_TEX_UV: {
+ break;
+ case RS::ARRAY_TEX_UV:
+ case RS::ARRAY_TEX_UV2:
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
-
- } break;
- case RS::ARRAY_TEX_UV2: {
- vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
- } break;
+ break;
+ case RS::ARRAY_BONES:
+ vd.format = RD::DATA_FORMAT_R32G32B32A32_UINT;
+ break;
+ case RS::ARRAY_TANGENT:
+ case RS::ARRAY_COLOR:
case RS::ARRAY_CUSTOM0:
case RS::ARRAY_CUSTOM1:
case RS::ARRAY_CUSTOM2:
- case RS::ARRAY_CUSTOM3: {
- //assumed weights too
- vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
- } break;
- case RS::ARRAY_BONES: {
- //assumed weights too
- vd.format = RD::DATA_FORMAT_R32G32B32A32_UINT;
- } break;
- case RS::ARRAY_WEIGHTS: {
- //assumed weights too
+ case RS::ARRAY_CUSTOM3:
+ case RS::ARRAY_WEIGHTS:
vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
- } break;
+ break;
+ default:
+ DEV_ASSERT(false && "Unknown vertex format element.");
+ break;
}
} else {
- //Supplied, use it
-
- vd.stride = 1; //mark that it needs a stride set (default uses 0)
+ // Mark that it needs a stride set (default uses 0).
+ vd.stride = 1;
switch (i) {
case RS::ARRAY_VERTEX: {
- vd.offset = position_stride;
+ vd.offset = r_position_stride;
- if (s->format & RS::ARRAY_FLAG_USE_2D_VERTICES) {
+ if (p_surface_format & RS::ARRAY_FLAG_USE_2D_VERTICES) {
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
- position_stride = sizeof(float) * 2;
+ r_position_stride = sizeof(float) * 2;
} else {
- if (!mis && (s->format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
+ if (!p_instanced_surface && (p_surface_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
vd.format = RD::DATA_FORMAT_R16G16B16A16_UNORM;
- position_stride = sizeof(uint16_t) * 4;
+ r_position_stride = sizeof(uint16_t) * 4;
} else {
vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT;
- position_stride = sizeof(float) * 3;
+ r_position_stride = sizeof(float) * 3;
}
}
- if (mis) {
- buffer = mis->vertex_buffer[p_current_buffer];
- } else {
- buffer = s->vertex_buffer;
- }
-
} break;
case RS::ARRAY_NORMAL: {
vd.offset = 0;
- offset = position_stride * s->vertex_count;
- if (!mis && (s->format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
+
+ if (!p_instanced_surface && (p_surface_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
vd.format = RD::DATA_FORMAT_R16G16_UNORM;
normal_tangent_stride += sizeof(uint16_t) * 2;
} else {
@@ -1240,20 +1214,14 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
// A small trick here: if we are uncompressed and we have normals, but no tangents. We need
// the shader to think there are 4 components to "axis_tangent_attrib". So we give a size of 4,
// but a stride based on only having 2 elements.
- if (!(s->format & RS::ARRAY_FORMAT_TANGENT)) {
+ if (!(p_surface_format & RS::ARRAY_FORMAT_TANGENT)) {
normal_tangent_stride += sizeof(uint16_t) * 2;
} else {
normal_tangent_stride += sizeof(uint16_t) * 4;
}
}
- if (mis) {
- buffer = mis->vertex_buffer[p_current_buffer];
- } else {
- buffer = s->vertex_buffer;
- }
} break;
case RS::ARRAY_TANGENT: {
- buffer = mesh_default_rd_buffers[i];
vd.stride = 0;
vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
} break;
@@ -1262,30 +1230,27 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
vd.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
attribute_stride += sizeof(int8_t) * 4;
- buffer = s->attribute_buffer;
} break;
case RS::ARRAY_TEX_UV: {
vd.offset = attribute_stride;
- if (s->format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
+ if (p_surface_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
vd.format = RD::DATA_FORMAT_R16G16_UNORM;
attribute_stride += sizeof(uint16_t) * 2;
} else {
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
attribute_stride += sizeof(float) * 2;
}
- buffer = s->attribute_buffer;
} break;
case RS::ARRAY_TEX_UV2: {
vd.offset = attribute_stride;
- if (s->format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
+ if (p_surface_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
vd.format = RD::DATA_FORMAT_R16G16_UNORM;
attribute_stride += sizeof(uint16_t) * 2;
} else {
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
attribute_stride += sizeof(float) * 2;
}
- buffer = s->attribute_buffer;
} break;
case RS::ARRAY_CUSTOM0:
case RS::ARRAY_CUSTOM1:
@@ -1295,26 +1260,23 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
int idx = i - RS::ARRAY_CUSTOM0;
const uint32_t fmt_shift[RS::ARRAY_CUSTOM_COUNT] = { RS::ARRAY_FORMAT_CUSTOM0_SHIFT, RS::ARRAY_FORMAT_CUSTOM1_SHIFT, RS::ARRAY_FORMAT_CUSTOM2_SHIFT, RS::ARRAY_FORMAT_CUSTOM3_SHIFT };
- uint32_t fmt = (s->format >> fmt_shift[idx]) & RS::ARRAY_FORMAT_CUSTOM_MASK;
+ uint32_t fmt = (p_surface_format >> fmt_shift[idx]) & RS::ARRAY_FORMAT_CUSTOM_MASK;
const uint32_t fmtsize[RS::ARRAY_CUSTOM_MAX] = { 4, 4, 4, 8, 4, 8, 12, 16 };
const RD::DataFormat fmtrd[RS::ARRAY_CUSTOM_MAX] = { RD::DATA_FORMAT_R8G8B8A8_UNORM, RD::DATA_FORMAT_R8G8B8A8_SNORM, RD::DATA_FORMAT_R16G16_SFLOAT, RD::DATA_FORMAT_R16G16B16A16_SFLOAT, RD::DATA_FORMAT_R32_SFLOAT, RD::DATA_FORMAT_R32G32_SFLOAT, RD::DATA_FORMAT_R32G32B32_SFLOAT, RD::DATA_FORMAT_R32G32B32A32_SFLOAT };
vd.format = fmtrd[fmt];
attribute_stride += fmtsize[fmt];
- buffer = s->attribute_buffer;
} break;
case RS::ARRAY_BONES: {
vd.offset = skin_stride;
vd.format = RD::DATA_FORMAT_R16G16B16A16_UINT;
skin_stride += sizeof(int16_t) * 4;
- buffer = s->skin_buffer;
} break;
case RS::ARRAY_WEIGHTS: {
vd.offset = skin_stride;
vd.format = RD::DATA_FORMAT_R16G16B16A16_UNORM;
skin_stride += sizeof(int16_t) * 4;
- buffer = s->skin_buffer;
} break;
}
}
@@ -1324,13 +1286,10 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
}
attributes.push_back(vd);
- buffers.push_back(buffer);
- offsets.push_back(offset);
if (p_input_motion_vectors) {
- // Since the previous vertex, normal and tangent can't be part of the vertex format but they are required when motion
- // vectors are enabled, we opt to push a copy of the vertex attribute with a different location and buffer (if it's
- // part of an instance that has one).
+ // Since the previous vertex, normal and tangent can't be part of the vertex format but they are required when
+ // motion vectors are enabled, we opt to push a copy of the vertex attribute with a different location.
switch (i) {
case RS::ARRAY_VERTEX: {
vd.location = ATTRIBUTE_LOCATION_PREV_VERTEX;
@@ -1344,25 +1303,21 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
}
if (int(vd.location) != i) {
- if (mis && buffer != mesh_default_rd_buffers[i]) {
- buffer = mis->vertex_buffer[p_previous_buffer];
- }
-
attributes.push_back(vd);
- buffers.push_back(buffer);
- offsets.push_back(offset);
}
}
}
- //update final stride
+ // Update final stride.
for (int i = 0; i < attributes.size(); i++) {
if (attributes[i].stride == 0) {
- continue; //default location
+ // Default location.
+ continue;
}
+
int loc = attributes[i].location;
if (loc == RS::ARRAY_VERTEX || loc == ATTRIBUTE_LOCATION_PREV_VERTEX) {
- attributes.write[i].stride = position_stride;
+ attributes.write[i].stride = r_position_stride;
} else if ((loc < RS::ARRAY_COLOR) || ((loc >= ATTRIBUTE_LOCATION_PREV_NORMAL) && (loc <= ATTRIBUTE_LOCATION_PREV_TANGENT))) {
attributes.write[i].stride = normal_tangent_stride;
} else if (loc < RS::ARRAY_BONES) {
@@ -1372,11 +1327,75 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
}
}
+ return RD::get_singleton()->vertex_format_create(attributes);
+}
+
+void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis, uint32_t p_current_buffer, uint32_t p_previous_buffer) {
+ uint32_t position_stride = 0;
+ v.vertex_format = _mesh_surface_generate_vertex_format(s->format, p_input_mask, mis != nullptr, p_input_motion_vectors, position_stride);
+
+ Vector<RID> buffers;
+ Vector<uint64_t> offsets;
+ RID buffer;
+ uint64_t offset = 0;
+ for (int i = 0; i < RS::ARRAY_INDEX; i++) {
+ offset = 0;
+
+ if (!(s->format & (1ULL << i))) {
+ // Not supplied by surface, use default buffers.
+ buffer = mesh_default_rd_buffers[i];
+ } else {
+ // Supplied by surface, use buffer.
+ switch (i) {
+ case RS::ARRAY_VERTEX:
+ case RS::ARRAY_NORMAL:
+ offset = i == RS::ARRAY_NORMAL ? position_stride * s->vertex_count : 0;
+ buffer = mis != nullptr ? mis->vertex_buffer[p_current_buffer] : s->vertex_buffer;
+ break;
+ case RS::ARRAY_TANGENT:
+ buffer = mesh_default_rd_buffers[i];
+ break;
+ case RS::ARRAY_COLOR:
+ case RS::ARRAY_TEX_UV:
+ case RS::ARRAY_TEX_UV2:
+ case RS::ARRAY_CUSTOM0:
+ case RS::ARRAY_CUSTOM1:
+ case RS::ARRAY_CUSTOM2:
+ case RS::ARRAY_CUSTOM3:
+ buffer = s->attribute_buffer;
+ break;
+ case RS::ARRAY_BONES:
+ case RS::ARRAY_WEIGHTS:
+ buffer = s->skin_buffer;
+ break;
+ }
+ }
+
+ if (!(p_input_mask & (1ULL << i))) {
+ continue; // Shader does not need this, skip it (but computing stride was important anyway)
+ }
+
+ buffers.push_back(buffer);
+ offsets.push_back(offset);
+
+ if (p_input_motion_vectors) {
+ // Push the buffer for motion vector inputs.
+ if (i == RS::ARRAY_VERTEX || i == RS::ARRAY_NORMAL || i == RS::ARRAY_TANGENT) {
+ if (mis && buffer != mesh_default_rd_buffers[i]) {
+ buffers.push_back(mis->vertex_buffer[p_previous_buffer]);
+ } else {
+ buffers.push_back(buffer);
+ }
+
+ offsets.push_back(offset);
+ }
+ }
+ }
+
v.input_mask = p_input_mask;
v.current_buffer = p_current_buffer;
v.previous_buffer = p_previous_buffer;
v.input_motion_vectors = p_input_motion_vectors;
- v.vertex_format = RD::get_singleton()->vertex_format_create(attributes);
v.vertex_array = RD::get_singleton()->vertex_array_create(s->vertex_count, v.vertex_format, buffers, offsets);
}
@@ -2166,7 +2185,7 @@ void MeshStorage::skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_
if (skeleton->size) {
skeleton->data.resize(skeleton->size * (skeleton->use_2d ? 8 : 12));
skeleton->buffer = RD::get_singleton()->storage_buffer_create(skeleton->data.size() * sizeof(float));
- memset(skeleton->data.ptrw(), 0, skeleton->data.size() * sizeof(float));
+ memset(skeleton->data.ptr(), 0, skeleton->data.size() * sizeof(float));
_skeleton_make_dirty(skeleton);
@@ -2200,7 +2219,7 @@ void MeshStorage::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const
ERR_FAIL_INDEX(p_bone, skeleton->size);
ERR_FAIL_COND(skeleton->use_2d);
- float *dataptr = skeleton->data.ptrw() + p_bone * 12;
+ float *dataptr = skeleton->data.ptr() + p_bone * 12;
dataptr[0] = p_transform.basis.rows[0][0];
dataptr[1] = p_transform.basis.rows[0][1];
@@ -2251,8 +2270,7 @@ void MeshStorage::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, con
ERR_FAIL_NULL(skeleton);
ERR_FAIL_INDEX(p_bone, skeleton->size);
ERR_FAIL_COND(!skeleton->use_2d);
-
- float *dataptr = skeleton->data.ptrw() + p_bone * 8;
+ float *dataptr = skeleton->data.ptr() + p_bone * 8;
dataptr[0] = p_transform.columns[0][0];
dataptr[1] = p_transform.columns[1][0];
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
index f811314fb6..322f3cc6f4 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
@@ -199,6 +199,7 @@ private:
weight_update_list(this), array_update_list(this) {}
};
+ RD::VertexFormatID _mesh_surface_generate_vertex_format(uint64_t p_surface_format, uint64_t p_input_mask, bool p_instanced_surface, bool p_input_motion_vectors, uint32_t &r_position_stride);
void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis = nullptr, uint32_t p_current_buffer = 0, uint32_t p_previous_buffer = 0);
void _mesh_instance_clear(MeshInstance *mi);
@@ -311,7 +312,7 @@ private:
struct Skeleton {
bool use_2d = false;
int size = 0;
- Vector<float> data;
+ LocalVector<float> data;
RID buffer;
bool dirty = false;
@@ -605,6 +606,12 @@ public:
return s->particles_render_index;
}
+ _FORCE_INLINE_ RD::VertexFormatID mesh_surface_get_vertex_format(void *p_surface, uint64_t p_input_mask, bool p_instanced_surface, bool p_input_motion_vectors) {
+ Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
+ uint32_t position_stride = 0;
+ return _mesh_surface_generate_vertex_format(s->format, p_input_mask, p_instanced_surface, p_input_motion_vectors, position_stride);
+ }
+
Dependency *mesh_get_dependency(RID p_mesh) const;
/* MESH INSTANCE API */
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 2f44096dc8..ca44f4dd7e 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
@@ -167,76 +167,27 @@ void RenderSceneBuffersRD::configure(const RenderSceneBuffersConfiguration *p_co
// cleanout any old buffers we had.
cleanup();
- // At least one of these is required to be supported.
- RenderingDeviceCommons::DataFormat preferred_format[2] = { RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::DATA_FORMAT_D32_SFLOAT_S8_UINT };
- if (can_be_storage) {
- // Prefer higher precision on desktop.
- preferred_format[0] = RD::DATA_FORMAT_D32_SFLOAT_S8_UINT;
- preferred_format[1] = RD::DATA_FORMAT_D24_UNORM_S8_UINT;
- }
-
- // create our 3D render buffers
- {
- // Create our color buffer(s)
- uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | (can_be_storage ? RD::TEXTURE_USAGE_STORAGE_BIT : 0) | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
- usage_bits |= RD::TEXTURE_USAGE_INPUT_ATTACHMENT_BIT; // only needed when using subpasses in the mobile renderer
-
- // our internal texture should have MSAA support if applicable
- if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) {
- usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
- }
-
- create_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR, base_data_format, usage_bits);
- }
-
- // Create our depth buffer
- {
- // TODO Lazy create this in case we've got an external depth buffer
-
- RD::DataFormat format;
- uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT;
-
- if (msaa_3d == RS::VIEWPORT_MSAA_DISABLED) {
- usage_bits |= RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
- format = RD::get_singleton()->texture_is_format_supported_for_usage(preferred_format[0], usage_bits) ? preferred_format[0] : preferred_format[1];
- } else {
- format = RD::DATA_FORMAT_R32_SFLOAT;
- usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | (can_be_storage ? RD::TEXTURE_USAGE_STORAGE_BIT : 0);
- }
+ // Create our color buffer.
+ const bool resolve_target = msaa_3d != RS::VIEWPORT_MSAA_DISABLED;
+ create_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR, base_data_format, get_color_usage_bits(resolve_target, false, can_be_storage));
- create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH, format, usage_bits);
- }
+ // Create our depth buffer.
+ create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH, get_depth_format(resolve_target, false, can_be_storage), get_depth_usage_bits(resolve_target, false, can_be_storage));
- // Create our MSAA buffers
+ // Create our MSAA buffers.
if (msaa_3d == RS::VIEWPORT_MSAA_DISABLED) {
texture_samples = RD::TEXTURE_SAMPLES_1;
} else {
- RD::DataFormat format = base_data_format;
- uint32_t usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
-
- const RD::TextureSamples ts[RS::VIEWPORT_MSAA_MAX] = {
- RD::TEXTURE_SAMPLES_1,
- RD::TEXTURE_SAMPLES_2,
- RD::TEXTURE_SAMPLES_4,
- RD::TEXTURE_SAMPLES_8,
- };
-
- texture_samples = ts[msaa_3d];
-
- create_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA, format, usage_bits, texture_samples);
-
- usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
- format = RD::get_singleton()->texture_is_format_supported_for_usage(preferred_format[0], usage_bits) ? preferred_format[0] : preferred_format[1];
-
- create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA, format, usage_bits, texture_samples);
+ texture_samples = msaa_to_samples(msaa_3d);
+ create_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA, base_data_format, get_color_usage_bits(false, true, can_be_storage), texture_samples);
+ create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA, get_depth_format(false, true, can_be_storage), get_depth_usage_bits(false, true, can_be_storage), texture_samples);
}
// VRS (note, our vrs object will only be set if VRS is supported)
RID vrs_texture;
RS::ViewportVRSMode vrs_mode = texture_storage->render_target_get_vrs_mode(render_target);
if (vrs && vrs_mode != RS::VIEWPORT_VRS_DISABLED) {
- uint32_t usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
- vrs_texture = create_texture(RB_SCOPE_VRS, RB_TEXTURE, RD::DATA_FORMAT_R8_UINT, usage_bits, RD::TEXTURE_SAMPLES_1, vrs->get_vrs_texture_size(internal_size));
+ vrs_texture = create_texture(RB_SCOPE_VRS, RB_TEXTURE, get_vrs_format(), get_vrs_usage_bits(), RD::TEXTURE_SAMPLES_1, vrs->get_vrs_texture_size(internal_size));
}
// (re-)configure any named buffers
@@ -664,16 +615,12 @@ void RenderSceneBuffersRD::ensure_upscaled() {
void RenderSceneBuffersRD::ensure_velocity() {
if (!has_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY)) {
- uint32_t usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
-
- if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) {
- uint32_t msaa_usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
- usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
+ const bool msaa = msaa_3d != RS::VIEWPORT_MSAA_DISABLED;
+ create_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY, get_velocity_format(), get_velocity_usage_bits(msaa, false, can_be_storage));
- create_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA, RD::DATA_FORMAT_R16G16_SFLOAT, msaa_usage_bits, texture_samples);
+ if (msaa) {
+ create_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA, get_velocity_format(), get_velocity_usage_bits(false, msaa, can_be_storage), texture_samples);
}
-
- create_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY, RD::DATA_FORMAT_R16G16_SFLOAT, usage_bits);
}
}
@@ -724,3 +671,62 @@ RID RenderSceneBuffersRD::get_velocity_buffer(bool p_get_msaa, uint32_t p_layer)
}
}
}
+
+uint32_t RenderSceneBuffersRD::get_color_usage_bits(bool p_resolve, bool p_msaa, bool p_storage) {
+ DEV_ASSERT((!p_resolve && !p_msaa) || (p_resolve != p_msaa));
+
+ uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_INPUT_ATTACHMENT_BIT;
+ if (p_msaa) {
+ usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
+ } else if (p_resolve) {
+ usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | (p_storage ? RD::TEXTURE_USAGE_STORAGE_BIT : 0);
+ } else {
+ usage_bits |= (p_storage ? RD::TEXTURE_USAGE_STORAGE_BIT : 0);
+ }
+
+ return usage_bits;
+}
+
+RD::DataFormat RenderSceneBuffersRD::get_depth_format(bool p_resolve, bool p_msaa, bool p_storage) {
+ if (p_resolve) {
+ return RD::DATA_FORMAT_R32_SFLOAT;
+ } else {
+ const RenderingDeviceCommons::DataFormat preferred_formats[2] = {
+ p_storage ? RD::DATA_FORMAT_D32_SFLOAT_S8_UINT : RD::DATA_FORMAT_D24_UNORM_S8_UINT,
+ p_storage ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT
+ };
+
+ return RD::get_singleton()->texture_is_format_supported_for_usage(preferred_formats[0], get_depth_usage_bits(p_resolve, p_msaa, p_storage)) ? preferred_formats[0] : preferred_formats[1];
+ }
+}
+
+uint32_t RenderSceneBuffersRD::get_depth_usage_bits(bool p_resolve, bool p_msaa, bool p_storage) {
+ DEV_ASSERT((!p_resolve && !p_msaa) || (p_resolve != p_msaa));
+
+ uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT;
+ if (p_msaa) {
+ usage_bits |= RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
+ } else if (p_resolve) {
+ usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | (p_storage ? RD::TEXTURE_USAGE_STORAGE_BIT : 0);
+ } else {
+ usage_bits |= RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ }
+
+ return usage_bits;
+}
+
+RD::DataFormat RenderSceneBuffersRD::get_velocity_format() {
+ return RD::DATA_FORMAT_R16G16_SFLOAT;
+}
+
+uint32_t RenderSceneBuffersRD::get_velocity_usage_bits(bool p_resolve, bool p_msaa, bool p_storage) {
+ return get_color_usage_bits(p_resolve, p_msaa, p_storage);
+}
+
+RD::DataFormat RenderSceneBuffersRD::get_vrs_format() {
+ return RD::DATA_FORMAT_R8_UINT;
+}
+
+uint32_t RenderSceneBuffersRD::get_vrs_usage_bits() {
+ return RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+}
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
index 0025fc5ab7..187dbab445 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
@@ -178,6 +178,7 @@ public:
// info from our renderer
void set_can_be_storage(const bool p_can_be_storage) { can_be_storage = p_can_be_storage; }
+ bool get_can_be_storage() const { return can_be_storage; }
void set_max_cluster_elements(const uint32_t p_max_elements) { max_cluster_elements = p_max_elements; }
uint32_t get_max_cluster_elements() { return max_cluster_elements; }
void set_base_data_format(const RD::DataFormat p_base_data_format) { base_data_format = p_base_data_format; }
@@ -305,6 +306,30 @@ public:
return samplers;
}
+ _FORCE_INLINE_ static RD::TextureSamples msaa_to_samples(RS::ViewportMSAA p_msaa) {
+ switch (p_msaa) {
+ case RS::VIEWPORT_MSAA_DISABLED:
+ return RD::TEXTURE_SAMPLES_1;
+ case RS::VIEWPORT_MSAA_2X:
+ return RD::TEXTURE_SAMPLES_2;
+ case RS::VIEWPORT_MSAA_4X:
+ return RD::TEXTURE_SAMPLES_4;
+ case RS::VIEWPORT_MSAA_8X:
+ return RD::TEXTURE_SAMPLES_8;
+ default:
+ DEV_ASSERT(false && "Unknown MSAA option.");
+ return RD::TEXTURE_SAMPLES_1;
+ }
+ }
+
+ static uint32_t get_color_usage_bits(bool p_resolve, bool p_msaa, bool p_storage);
+ static RD::DataFormat get_depth_format(bool p_resolve, bool p_msaa, bool p_storage);
+ static uint32_t get_depth_usage_bits(bool p_resolve, bool p_msaa, bool p_storage);
+ static RD::DataFormat get_velocity_format();
+ static uint32_t get_velocity_usage_bits(bool p_resolve, bool p_msaa, bool p_storage);
+ static RD::DataFormat get_vrs_format();
+ static uint32_t get_vrs_usage_bits();
+
private:
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Our classDB doesn't support calling our normal exposed functions
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index 84e8d1d671..9dc606620b 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -172,9 +172,8 @@ TextureStorage::TextureStorage() {
ptr[i] = Math::make_half_float(1.0f);
}
- Vector<Vector<uint8_t>> vpv;
- vpv.push_back(sv);
- default_rd_textures[DEFAULT_RD_TEXTURE_DEPTH] = RD::get_singleton()->texture_create(tf, RD::TextureView(), vpv);
+ default_rd_textures[DEFAULT_RD_TEXTURE_DEPTH] = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->texture_update(default_rd_textures[DEFAULT_RD_TEXTURE_DEPTH], 0, sv);
}
for (int i = 0; i < 16; i++) {
@@ -447,9 +446,8 @@ TextureStorage::TextureStorage() {
}
{
- Vector<Vector<uint8_t>> vsv;
- vsv.push_back(sv);
- default_rd_textures[DEFAULT_RD_TEXTURE_2D_ARRAY_DEPTH] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vsv);
+ default_rd_textures[DEFAULT_RD_TEXTURE_2D_ARRAY_DEPTH] = RD::get_singleton()->texture_create(tformat, RD::TextureView());
+ RD::get_singleton()->texture_update(default_rd_textures[DEFAULT_RD_TEXTURE_2D_ARRAY_DEPTH], 0, sv);
}
}
@@ -566,10 +564,6 @@ bool TextureStorage::free(RID p_rid) {
return false;
}
-bool TextureStorage::can_create_resources_async() const {
- return true;
-}
-
/* Canvas Texture API */
RID TextureStorage::canvas_texture_allocate() {
@@ -3237,13 +3231,13 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
if (rt->size.width == 0 || rt->size.height == 0) {
return;
}
+
+ rt->color_format = render_target_get_color_format(rt->use_hdr, false);
+ rt->color_format_srgb = render_target_get_color_format(rt->use_hdr, true);
+
if (rt->use_hdr) {
- rt->color_format = RendererSceneRenderRD::get_singleton()->_render_buffers_get_color_format();
- rt->color_format_srgb = rt->color_format;
rt->image_format = rt->is_transparent ? Image::FORMAT_RGBAH : Image::FORMAT_RGBH;
} else {
- rt->color_format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
- rt->color_format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
rt->image_format = rt->is_transparent ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8;
}
@@ -3262,8 +3256,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
rd_color_attachment_format.texture_type = RD::TEXTURE_TYPE_2D;
}
rd_color_attachment_format.samples = RD::TEXTURE_SAMPLES_1;
- rd_color_attachment_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
- rd_color_attachment_format.usage_bits |= RD::TEXTURE_USAGE_STORAGE_BIT; // FIXME we need this only when FSR is enabled
+ rd_color_attachment_format.usage_bits = render_target_get_color_usage_bits(false);
rd_color_attachment_format.shareable_formats.push_back(rt->color_format);
rd_color_attachment_format.shareable_formats.push_back(rt->color_format_srgb);
if (rt->msaa != RS::VIEWPORT_MSAA_DISABLED) {
@@ -3285,7 +3278,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
RD::TEXTURE_SAMPLES_8,
};
rd_color_multisample_format.samples = texture_samples[rt->msaa];
- rd_color_multisample_format.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+ rd_color_multisample_format.usage_bits = render_target_get_color_usage_bits(true);
RD::TextureView rd_view_multisample;
rd_color_multisample_format.is_resolve_buffer = false;
rt->color_multisample = RD::get_singleton()->texture_create(rd_color_multisample_format, rd_view_multisample);
@@ -4175,3 +4168,20 @@ RID TextureStorage::render_target_get_vrs_texture(RID p_render_target) const {
return rt->vrs_texture;
}
+
+RD::DataFormat TextureStorage::render_target_get_color_format(bool p_use_hdr, bool p_srgb) {
+ if (p_use_hdr) {
+ return RendererSceneRenderRD::get_singleton()->_render_buffers_get_color_format();
+ } else {
+ return p_srgb ? RD::DATA_FORMAT_R8G8B8A8_SRGB : RD::DATA_FORMAT_R8G8B8A8_UNORM;
+ }
+}
+
+uint32_t TextureStorage::render_target_get_color_usage_bits(bool p_msaa) {
+ if (p_msaa) {
+ return RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+ } else {
+ // FIXME: Storage bit should only be requested when FSR is required.
+ return RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+ }
+}
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
index 8aecc78f07..2135ee3e3b 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
@@ -504,8 +504,6 @@ public:
bool owns_texture(RID p_rid) const { return texture_owner.owns(p_rid); };
- virtual bool can_create_resources_async() const override;
-
virtual RID texture_allocate() override;
virtual void texture_free(RID p_rid) override;
@@ -798,6 +796,9 @@ public:
void render_target_set_framebuffer_uniform_set(RID p_render_target, RID p_uniform_set);
void render_target_set_backbuffer_uniform_set(RID p_render_target, RID p_uniform_set);
+
+ static RD::DataFormat render_target_get_color_format(bool p_use_hdr, bool p_srgb);
+ static uint32_t render_target_get_color_usage_bits(bool p_msaa);
};
} // namespace RendererRD
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 43abb22e3d..ca07444465 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -1690,6 +1690,14 @@ Variant RendererSceneCull::instance_geometry_get_shader_parameter_default_value(
return Variant();
}
+void RendererSceneCull::mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) {
+ scene_render->mesh_generate_pipelines(p_mesh, p_background_compilation);
+}
+
+uint32_t RendererSceneCull::get_pipeline_compilations(RS::PipelineSource p_source) {
+ return scene_render->get_pipeline_compilations(p_source);
+}
+
void RendererSceneCull::instance_geometry_get_shader_parameter_list(RID p_instance, List<PropertyInfo> *p_parameters) const {
const Instance *instance = const_cast<RendererSceneCull *>(this)->instance_owner.get_or_null(p_instance);
ERR_FAIL_NULL(instance);
@@ -1990,6 +1998,9 @@ void RendererSceneCull::_update_instance(Instance *p_instance) {
pair.bvh2 = &p_instance->scenario->indexers[Scenario::INDEXER_VOLUMES];
}
pair.cull_mask = RSG::light_storage->light_get_cull_mask(p_instance->base);
+ } else if (p_instance->base_type == RS::INSTANCE_LIGHTMAP) {
+ pair.pair_mask = RS::INSTANCE_GEOMETRY_MASK;
+ pair.bvh = &p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY];
} else if (geometry_instance_pair_mask & (1 << RS::INSTANCE_REFLECTION_PROBE) && (p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE)) {
pair.pair_mask = RS::INSTANCE_GEOMETRY_MASK;
pair.bvh = &p_instance->scenario->indexers[Scenario::INDEXER_GEOMETRY];
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index 972f66d325..5aae59eb51 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -1102,6 +1102,9 @@ public:
virtual Variant instance_geometry_get_shader_parameter(RID p_instance, const StringName &p_parameter) const;
virtual Variant instance_geometry_get_shader_parameter_default_value(RID p_instance, const StringName &p_parameter) const;
+ virtual void mesh_generate_pipelines(RID p_mesh, bool p_background_compilation);
+ virtual uint32_t get_pipeline_compilations(RS::PipelineSource p_source);
+
_FORCE_INLINE_ void _update_instance(Instance *p_instance);
_FORCE_INLINE_ void _update_instance_aabb(Instance *p_instance);
_FORCE_INLINE_ void _update_dirty_instance(Instance *p_instance);
diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h
index 72ccbcdf11..4d81a9b6a3 100644
--- a/servers/rendering/renderer_scene_render.h
+++ b/servers/rendering/renderer_scene_render.h
@@ -58,6 +58,11 @@ public:
virtual void geometry_instance_free(RenderGeometryInstance *p_geometry_instance) = 0;
virtual uint32_t geometry_instance_get_pair_mask() = 0;
+ /* PIPELINES */
+
+ virtual void mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) = 0;
+ virtual uint32_t get_pipeline_compilations(RS::PipelineSource p_source) = 0;
+
/* SDFGI UPDATE */
virtual void sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) = 0;
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index e322bba768..942d5631e4 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -36,7 +36,15 @@
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
+// TODO: Thread safety
+// - Roll back thread safe attribute for RID_Owner members after the read-only/atomic update scheme is implemented.
+
#define FORCE_SEPARATE_PRESENT_QUEUE 0
+#define PRINT_FRAMEBUFFER_FORMAT 0
+
+#define ERR_RENDER_THREAD_MSG String("This function (") + String(__func__) + String(") can only be called from the render thread. ")
+#define ERR_RENDER_THREAD_GUARD() ERR_FAIL_COND_MSG(render_thread_id != Thread::get_caller_id(), ERR_RENDER_THREAD_MSG);
+#define ERR_RENDER_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(render_thread_id != Thread::get_caller_id(), (m_ret), ERR_RENDER_THREAD_MSG);
/**************************/
/**** HELPER FUNCTIONS ****/
@@ -138,6 +146,8 @@ RenderingDevice::ShaderSPIRVGetCacheKeyFunction RenderingDevice::get_spirv_cache
/***************************/
void RenderingDevice::_add_dependency(RID p_id, RID p_depends_on) {
+ _THREAD_SAFE_METHOD_
+
HashSet<RID> *set = dependency_map.getptr(p_depends_on);
if (set == nullptr) {
set = &dependency_map.insert(p_depends_on, HashSet<RID>())->value;
@@ -152,6 +162,8 @@ void RenderingDevice::_add_dependency(RID p_id, RID p_depends_on) {
}
void RenderingDevice::_free_dependencies(RID p_id) {
+ _THREAD_SAFE_METHOD_
+
// Direct dependencies must be freed.
HashMap<RID, HashSet<RID>>::Iterator E = dependency_map.find(p_id);
@@ -236,6 +248,35 @@ RenderingDevice::Buffer *RenderingDevice::_get_buffer_from_owner(RID p_buffer) {
return buffer;
}
+Error RenderingDevice::_buffer_initialize(Buffer *p_buffer, const uint8_t *p_data, size_t p_data_size, uint32_t p_required_align) {
+ uint32_t transfer_worker_offset;
+ TransferWorker *transfer_worker = _acquire_transfer_worker(p_data_size, p_required_align, transfer_worker_offset);
+ p_buffer->transfer_worker_index = transfer_worker->index;
+
+ {
+ MutexLock lock(transfer_worker->operations_mutex);
+ p_buffer->transfer_worker_operation = ++transfer_worker->operations_counter;
+ }
+
+ // Copy to the worker's staging buffer.
+ uint8_t *data_ptr = driver->buffer_map(transfer_worker->staging_buffer);
+ ERR_FAIL_NULL_V(data_ptr, ERR_CANT_CREATE);
+
+ memcpy(data_ptr + transfer_worker_offset, p_data, p_data_size);
+ driver->buffer_unmap(transfer_worker->staging_buffer);
+
+ // Copy from the staging buffer to the real buffer.
+ RDD::BufferCopyRegion region;
+ region.src_offset = transfer_worker_offset;
+ region.dst_offset = 0;
+ region.size = p_data_size;
+ driver->command_copy_buffer(transfer_worker->command_buffer, transfer_worker->staging_buffer, p_buffer->driver_id, region);
+
+ _release_transfer_worker(transfer_worker);
+
+ return OK;
+}
+
Error RenderingDevice::_insert_staging_block() {
StagingBufferBlock block;
@@ -386,32 +427,89 @@ void RenderingDevice::_staging_buffer_execute_required_action(StagingRequiredAct
}
}
-Error RenderingDevice::_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, uint32_t p_required_align) {
+Error RenderingDevice::buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size) {
+ ERR_RENDER_THREAD_GUARD_V(ERR_UNAVAILABLE);
+
+ ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER,
+ "Copying buffers is forbidden during creation of a draw list");
+ ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER,
+ "Copying buffers is forbidden during creation of a compute list");
+
+ Buffer *src_buffer = _get_buffer_from_owner(p_src_buffer);
+ if (!src_buffer) {
+ ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Source buffer argument is not a valid buffer of any type.");
+ }
+
+ Buffer *dst_buffer = _get_buffer_from_owner(p_dst_buffer);
+ if (!dst_buffer) {
+ ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Destination buffer argument is not a valid buffer of any type.");
+ }
+
+ // Validate the copy's dimensions for both buffers.
+ ERR_FAIL_COND_V_MSG((p_size + p_src_offset) > src_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the source buffer.");
+ ERR_FAIL_COND_V_MSG((p_size + p_dst_offset) > dst_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the destination buffer.");
+
+ _check_transfer_worker_buffer(src_buffer);
+ _check_transfer_worker_buffer(dst_buffer);
+
+ // Perform the copy.
+ RDD::BufferCopyRegion region;
+ region.src_offset = p_src_offset;
+ region.dst_offset = p_dst_offset;
+ region.size = p_size;
+
+ if (_buffer_make_mutable(dst_buffer, p_dst_buffer)) {
+ // The destination buffer must be mutable to be used as a copy destination.
+ draw_graph.add_synchronization();
+ }
+
+ draw_graph.add_buffer_copy(src_buffer->driver_id, src_buffer->draw_tracker, dst_buffer->driver_id, dst_buffer->draw_tracker, region);
+
+ return OK;
+}
+
+Error RenderingDevice::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data) {
+ ERR_RENDER_THREAD_GUARD_V(ERR_UNAVAILABLE);
+
+ 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,
+ "Updating buffers is forbidden during creation of a compute list");
+
+ Buffer *buffer = _get_buffer_from_owner(p_buffer);
+ ERR_FAIL_NULL_V_MSG(buffer, ERR_INVALID_PARAMETER, "Buffer argument is not a valid buffer of any type.");
+ 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.");
+
+ _check_transfer_worker_buffer(buffer);
+
// Submitting may get chunked for various reasons, so convert this to a task.
- size_t to_submit = p_data_size;
+ size_t to_submit = p_size;
size_t submit_from = 0;
thread_local LocalVector<RDG::RecordedBufferCopy> command_buffer_copies_vector;
command_buffer_copies_vector.clear();
+ const uint8_t *src_data = reinterpret_cast<const uint8_t *>(p_data);
+ const uint32_t required_align = 32;
while (to_submit > 0) {
uint32_t block_write_offset;
uint32_t block_write_amount;
StagingRequiredAction required_action;
- Error err = _staging_buffer_allocate(MIN(to_submit, staging_buffer_block_size), p_required_align, block_write_offset, block_write_amount, required_action);
+ Error err = _staging_buffer_allocate(MIN(to_submit, staging_buffer_block_size), required_align, block_write_offset, block_write_amount, required_action);
if (err) {
return err;
}
- if (p_use_draw_queue && !command_buffer_copies_vector.is_empty() && required_action == STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL) {
- if (_buffer_make_mutable(p_buffer, p_buffer_id)) {
+ if (!command_buffer_copies_vector.is_empty() && required_action == STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL) {
+ if (_buffer_make_mutable(buffer, p_buffer)) {
// The buffer must be mutable to be used as a copy destination.
draw_graph.add_synchronization();
}
- // If we're using the draw queue and the staging buffer requires flushing everything, we submit the command early and clear the current vector.
- draw_graph.add_buffer_update(p_buffer->driver_id, p_buffer->draw_tracker, command_buffer_copies_vector);
+ draw_graph.add_buffer_update(buffer->driver_id, buffer->draw_tracker, command_buffer_copies_vector);
command_buffer_copies_vector.clear();
}
@@ -422,7 +520,7 @@ Error RenderingDevice::_buffer_update(Buffer *p_buffer, RID p_buffer_id, size_t
ERR_FAIL_NULL_V(data_ptr, ERR_CANT_CREATE);
// Copy to staging buffer.
- memcpy(data_ptr + block_write_offset, p_data + submit_from, block_write_amount);
+ memcpy(data_ptr + block_write_offset, src_data + submit_from, block_write_amount);
// Unmap.
driver->buffer_unmap(staging_buffer_blocks[staging_buffer_current].driver_id);
@@ -433,14 +531,10 @@ Error RenderingDevice::_buffer_update(Buffer *p_buffer, RID p_buffer_id, size_t
region.dst_offset = submit_from + p_offset;
region.size = block_write_amount;
- if (p_use_draw_queue) {
- RDG::RecordedBufferCopy buffer_copy;
- buffer_copy.source = staging_buffer_blocks[staging_buffer_current].driver_id;
- buffer_copy.region = region;
- command_buffer_copies_vector.push_back(buffer_copy);
- } else {
- driver->command_copy_buffer(frames[frame].setup_command_buffer, staging_buffer_blocks[staging_buffer_current].driver_id, p_buffer->driver_id, region);
- }
+ RDG::RecordedBufferCopy buffer_copy;
+ buffer_copy.source = staging_buffer_blocks[staging_buffer_current].driver_id;
+ buffer_copy.region = region;
+ command_buffer_copies_vector.push_back(buffer_copy);
staging_buffer_blocks.write[staging_buffer_current].fill_amount = block_write_offset + block_write_amount;
@@ -448,77 +542,18 @@ Error RenderingDevice::_buffer_update(Buffer *p_buffer, RID p_buffer_id, size_t
submit_from += block_write_amount;
}
- if (p_use_draw_queue && !command_buffer_copies_vector.is_empty()) {
- if (_buffer_make_mutable(p_buffer, p_buffer_id)) {
+ if (!command_buffer_copies_vector.is_empty()) {
+ if (_buffer_make_mutable(buffer, p_buffer)) {
// The buffer must be mutable to be used as a copy destination.
draw_graph.add_synchronization();
}
- draw_graph.add_buffer_update(p_buffer->driver_id, p_buffer->draw_tracker, command_buffer_copies_vector);
- }
-
- return OK;
-}
-
-Error RenderingDevice::buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size) {
- _THREAD_SAFE_METHOD_
-
- ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER,
- "Copying buffers is forbidden during creation of a draw list");
- ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER,
- "Copying buffers is forbidden during creation of a compute list");
-
- Buffer *src_buffer = _get_buffer_from_owner(p_src_buffer);
- if (!src_buffer) {
- ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Source buffer argument is not a valid buffer of any type.");
- }
-
- Buffer *dst_buffer = _get_buffer_from_owner(p_dst_buffer);
- if (!dst_buffer) {
- ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Destination buffer argument is not a valid buffer of any type.");
+ draw_graph.add_buffer_update(buffer->driver_id, buffer->draw_tracker, command_buffer_copies_vector);
}
- // Validate the copy's dimensions for both buffers.
- ERR_FAIL_COND_V_MSG((p_size + p_src_offset) > src_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the source buffer.");
- ERR_FAIL_COND_V_MSG((p_size + p_dst_offset) > dst_buffer->size, ERR_INVALID_PARAMETER, "Size is larger than the destination buffer.");
-
- // Perform the copy.
- RDD::BufferCopyRegion region;
- region.src_offset = p_src_offset;
- region.dst_offset = p_dst_offset;
- region.size = p_size;
-
- if (_buffer_make_mutable(dst_buffer, p_dst_buffer)) {
- // The destination buffer must be mutable to be used as a copy destination.
- draw_graph.add_synchronization();
- }
-
- draw_graph.add_buffer_copy(src_buffer->driver_id, src_buffer->draw_tracker, dst_buffer->driver_id, dst_buffer->draw_tracker, region);
-
- return OK;
-}
-
-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,
- "Updating buffers is forbidden during creation of a compute list");
-
- Buffer *buffer = _get_buffer_from_owner(p_buffer);
- if (!buffer) {
- ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Buffer argument is not a valid buffer of any type.");
- }
-
- 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);
+ return OK;
}
String RenderingDevice::get_perf_report() const {
@@ -534,7 +569,7 @@ void RenderingDevice::update_perf_report() {
}
Error RenderingDevice::buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size) {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD_V(ERR_UNAVAILABLE);
ERR_FAIL_COND_V_MSG((p_size % 4) != 0, ERR_INVALID_PARAMETER,
"Size must be a multiple of four");
@@ -551,6 +586,8 @@ Error RenderingDevice::buffer_clear(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.");
+ _check_transfer_worker_buffer(buffer);
+
if (_buffer_make_mutable(buffer, p_buffer)) {
// The destination buffer must be mutable to be used as a clear destination.
draw_graph.add_synchronization();
@@ -562,7 +599,7 @@ Error RenderingDevice::buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_
}
Vector<uint8_t> RenderingDevice::buffer_get_data(RID p_buffer, uint32_t p_offset, uint32_t p_size) {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD_V(Vector<uint8_t>());
Buffer *buffer = _get_buffer_from_owner(p_buffer);
if (!buffer) {
@@ -577,6 +614,8 @@ Vector<uint8_t> RenderingDevice::buffer_get_data(RID p_buffer, uint32_t p_offset
"Size is larger than the buffer.");
}
+ _check_transfer_worker_buffer(buffer);
+
RDD::BufferID tmp_buffer = driver->buffer_create(buffer->size, RDD::BUFFER_USAGE_TRANSFER_TO_BIT, RDD::MEMORY_ALLOCATION_TYPE_CPU);
ERR_FAIL_COND_V(!tmp_buffer, Vector<uint8_t>());
@@ -607,8 +646,6 @@ Vector<uint8_t> RenderingDevice::buffer_get_data(RID p_buffer, uint32_t p_offset
}
RID RenderingDevice::storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, BitField<StorageBufferUsage> p_usage) {
- _THREAD_SAFE_METHOD_
-
ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID());
Buffer buffer;
@@ -625,10 +662,12 @@ RID RenderingDevice::storage_buffer_create(uint32_t p_size_bytes, const Vector<u
buffer.draw_tracker->buffer_driver_id = buffer.driver_id;
if (p_data.size()) {
- _buffer_update(&buffer, RID(), 0, p_data.ptr(), p_data.size());
+ _buffer_initialize(&buffer, p_data.ptr(), p_data.size());
}
+ _THREAD_SAFE_LOCK_
buffer_memory += buffer.size;
+ _THREAD_SAFE_UNLOCK_
RID id = storage_buffer_owner.make_rid(buffer);
#ifdef DEV_ENABLED
@@ -638,8 +677,6 @@ RID RenderingDevice::storage_buffer_create(uint32_t p_size_bytes, const Vector<u
}
RID RenderingDevice::texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data) {
- _THREAD_SAFE_METHOD_
-
uint32_t element_size = get_format_vertex_size(p_format);
ERR_FAIL_COND_V_MSG(element_size == 0, RID(), "Format requested is not supported for texture buffers");
uint64_t size_bytes = uint64_t(element_size) * p_size_elements;
@@ -665,10 +702,12 @@ RID RenderingDevice::texture_buffer_create(uint32_t p_size_elements, DataFormat
}
if (p_data.size()) {
- _buffer_update(&texture_buffer, RID(), 0, p_data.ptr(), p_data.size());
+ _buffer_initialize(&texture_buffer, p_data.ptr(), p_data.size());
}
+ _THREAD_SAFE_LOCK_
buffer_memory += size_bytes;
+ _THREAD_SAFE_UNLOCK_
RID id = texture_buffer_owner.make_rid(texture_buffer);
#ifdef DEV_ENABLED
@@ -682,8 +721,6 @@ RID RenderingDevice::texture_buffer_create(uint32_t p_size_elements, DataFormat
/*****************/
RID RenderingDevice::texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data) {
- _THREAD_SAFE_METHOD_
-
// Some adjustments will happen.
TextureFormat format = p_format;
@@ -740,6 +777,9 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture
"Data for slice index " + itos(i) + " (mapped to layer " + itos(i) + ") differs in size (supplied: " + itos(p_data[i].size()) + ") than what is required by the format (" + itos(required_size) + ").");
}
+ ERR_FAIL_COND_V_MSG(format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, RID(),
+ "Textures created as depth attachments can't be initialized with data directly. Use RenderingDevice::texture_update() instead.");
+
if (!(format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT)) {
forced_usage_bits = TEXTURE_USAGE_CAN_UPDATE_BIT;
}
@@ -839,7 +879,7 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture
if (p_data.size()) {
for (uint32_t i = 0; i < p_format.array_layers; i++) {
- _texture_update(id, i, p_data[i], true, false);
+ _texture_initialize(id, i, p_data[i]);
}
if (texture.draw_tracker != nullptr) {
@@ -852,8 +892,6 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture
}
RID RenderingDevice::texture_create_shared(const TextureView &p_view, RID p_with_texture) {
- _THREAD_SAFE_METHOD_
-
Texture *src_texture = texture_owner.get_or_null(p_with_texture);
ERR_FAIL_NULL_V(src_texture, RID());
@@ -939,7 +977,6 @@ RID RenderingDevice::texture_create_shared(const TextureView &p_view, RID p_with
}
RID RenderingDevice::texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, BitField<RenderingDevice::TextureUsageBits> p_usage, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers) {
- _THREAD_SAFE_METHOD_
// This method creates a texture object using a VkImage created by an extension, module or other external source (OpenXR uses this).
Texture texture;
@@ -982,8 +1019,6 @@ RID RenderingDevice::texture_create_from_extension(TextureType p_type, DataForma
}
RID RenderingDevice::texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps, TextureSliceType p_slice_type, uint32_t p_layers) {
- _THREAD_SAFE_METHOD_
-
Texture *src_texture = texture_owner.get_or_null(p_with_texture);
ERR_FAIL_NULL_V(src_texture, RID());
@@ -1124,10 +1159,6 @@ RID RenderingDevice::texture_create_shared_from_slice(const TextureView &p_view,
return id;
}
-Error RenderingDevice::texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data) {
- return _texture_update(p_texture, p_layer, p_data, false, true);
-}
-
static _ALWAYS_INLINE_ void _copy_region(uint8_t const *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_x, uint32_t p_src_y, uint32_t p_src_w, uint32_t p_src_h, uint32_t p_src_full_w, uint32_t p_dst_pitch, uint32_t p_unit_size) {
uint32_t src_offset = (p_src_y * p_src_full_w + p_src_x) * p_unit_size;
uint32_t dst_offset = 0;
@@ -1144,11 +1175,184 @@ static _ALWAYS_INLINE_ void _copy_region(uint8_t const *__restrict p_src, uint8_
}
}
-Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, bool p_use_setup_queue, bool p_validate_can_update) {
- _THREAD_SAFE_METHOD_
+static _ALWAYS_INLINE_ void _copy_region_block_or_regular(const uint8_t *p_read_ptr, uint8_t *p_write_ptr, uint32_t p_x, uint32_t p_y, uint32_t p_width, uint32_t p_region_w, uint32_t p_region_h, uint32_t p_block_w, uint32_t p_block_h, uint32_t p_dst_pitch, uint32_t p_pixel_size, uint32_t p_block_size) {
+ if (p_block_w != 1 || p_block_h != 1) {
+ // Block format.
+ uint32_t xb = p_x / p_block_w;
+ uint32_t yb = p_y / p_block_h;
+ uint32_t wb = p_width / p_block_w;
+ uint32_t region_wb = p_region_w / p_block_w;
+ uint32_t region_hb = p_region_h / p_block_h;
+ _copy_region(p_read_ptr, p_write_ptr, xb, yb, region_wb, region_hb, wb, p_dst_pitch, p_block_size);
+ } else {
+ // Regular format.
+ _copy_region(p_read_ptr, p_write_ptr, p_x, p_y, p_region_w, p_region_h, p_width, p_dst_pitch, p_pixel_size);
+ }
+}
- ERR_FAIL_COND_V_MSG((draw_list || compute_list) && !p_use_setup_queue, ERR_INVALID_PARAMETER,
- "Updating textures is forbidden during creation of a draw or compute list");
+uint32_t RenderingDevice::_texture_layer_count(Texture *p_texture) const {
+ switch (p_texture->type) {
+ case TEXTURE_TYPE_CUBE:
+ case TEXTURE_TYPE_CUBE_ARRAY:
+ return p_texture->layers * 6;
+ default:
+ return p_texture->layers;
+ }
+}
+
+uint32_t RenderingDevice::_texture_alignment(Texture *p_texture) const {
+ uint32_t alignment = get_compressed_image_format_block_byte_size(p_texture->format);
+ if (alignment == 1) {
+ alignment = get_image_format_pixel_size(p_texture->format);
+ }
+
+ return STEPIFY(alignment, driver->api_trait_get(RDD::API_TRAIT_TEXTURE_TRANSFER_ALIGNMENT));
+}
+
+Error RenderingDevice::_texture_initialize(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data) {
+ Texture *texture = texture_owner.get_or_null(p_texture);
+ ERR_FAIL_NULL_V(texture, ERR_INVALID_PARAMETER);
+
+ if (texture->owner != RID()) {
+ p_texture = texture->owner;
+ texture = texture_owner.get_or_null(texture->owner);
+ ERR_FAIL_NULL_V(texture, ERR_BUG); // This is a bug.
+ }
+
+ uint32_t layer_count = _texture_layer_count(texture);
+ ERR_FAIL_COND_V(p_layer >= layer_count, 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);
+ uint32_t required_size = tight_mip_size;
+ uint32_t required_align = _texture_alignment(texture);
+
+ ERR_FAIL_COND_V_MSG(required_size != (uint32_t)p_data.size(), ERR_INVALID_PARAMETER,
+ "Required size for texture update (" + itos(required_size) + ") does not match data supplied size (" + itos(p_data.size()) + ").");
+
+ uint32_t block_w, block_h;
+ get_compressed_image_format_block_dimensions(texture->format, block_w, block_h);
+
+ uint32_t pixel_size = get_image_format_pixel_size(texture->format);
+ uint32_t pixel_rshift = get_compressed_image_format_pixel_rshift(texture->format);
+ uint32_t block_size = get_compressed_image_format_block_byte_size(texture->format);
+
+ // The algorithm operates on two passes, one to figure out the total size the staging buffer will require to allocate and another one where the copy is actually performed.
+ uint32_t staging_worker_offset = 0;
+ uint32_t staging_local_offset = 0;
+ TransferWorker *transfer_worker = nullptr;
+ const uint8_t *read_ptr = p_data.ptr();
+ uint8_t *write_ptr = nullptr;
+ const RDD::TextureLayout copy_dst_layout = driver->api_trait_get(RDD::API_TRAIT_USE_GENERAL_IN_COPY_QUEUES) ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_COPY_DST_OPTIMAL;
+ for (uint32_t pass = 0; pass < 2; pass++) {
+ const bool copy_pass = (pass == 1);
+ if (copy_pass) {
+ transfer_worker = _acquire_transfer_worker(staging_local_offset, required_align, staging_worker_offset);
+ texture->transfer_worker_index = transfer_worker->index;
+
+ {
+ MutexLock lock(transfer_worker->operations_mutex);
+ texture->transfer_worker_operation = ++transfer_worker->operations_counter;
+ }
+
+ staging_local_offset = 0;
+
+ write_ptr = driver->buffer_map(transfer_worker->staging_buffer);
+ ERR_FAIL_NULL_V(write_ptr, ERR_CANT_CREATE);
+
+ write_ptr += staging_worker_offset;
+
+ if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) {
+ // Transition the texture to the optimal layout.
+ RDD::TextureBarrier tb;
+ tb.texture = texture->driver_id;
+ tb.dst_access = RDD::BARRIER_ACCESS_COPY_WRITE_BIT;
+ tb.prev_layout = RDD::TEXTURE_LAYOUT_UNDEFINED;
+ tb.next_layout = copy_dst_layout;
+ tb.subresources.aspect = texture->barrier_aspect_flags;
+ tb.subresources.mipmap_count = texture->mipmaps;
+ tb.subresources.base_layer = p_layer;
+ tb.subresources.layer_count = 1;
+ driver->command_pipeline_barrier(transfer_worker->command_buffer, RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, RDD::PIPELINE_STAGE_COPY_BIT, {}, {}, tb);
+ }
+ }
+
+ uint32_t mipmap_offset = 0;
+ uint32_t logic_width = texture->width;
+ uint32_t logic_height = texture->height;
+ for (uint32_t mm_i = 0; mm_i < texture->mipmaps; mm_i++) {
+ uint32_t depth = 0;
+ uint32_t image_total = get_image_format_required_size(texture->format, texture->width, texture->height, texture->depth, mm_i + 1, &width, &height, &depth);
+
+ const uint8_t *read_ptr_mipmap = read_ptr + mipmap_offset;
+ tight_mip_size = image_total - mipmap_offset;
+
+ for (uint32_t z = 0; z < depth; z++) {
+ if (required_align > 0) {
+ uint32_t align_offset = staging_local_offset % required_align;
+ if (align_offset != 0) {
+ staging_local_offset += required_align - align_offset;
+ }
+ }
+
+ uint32_t pitch = (width * pixel_size * block_w) >> pixel_rshift;
+ uint32_t pitch_step = driver->api_trait_get(RDD::API_TRAIT_TEXTURE_DATA_ROW_PITCH_STEP);
+ pitch = STEPIFY(pitch, pitch_step);
+ uint32_t to_allocate = pitch * height;
+ to_allocate >>= pixel_rshift;
+
+ if (copy_pass) {
+ const uint8_t *read_ptr_mipmap_layer = read_ptr_mipmap + (tight_mip_size / depth) * z;
+ _copy_region_block_or_regular(read_ptr_mipmap_layer, write_ptr, 0, 0, width, width, height, block_w, block_h, pitch, pixel_size, block_size);
+ write_ptr += to_allocate;
+
+ RDD::BufferTextureCopyRegion copy_region;
+ copy_region.buffer_offset = staging_worker_offset + staging_local_offset;
+ copy_region.texture_subresources.aspect = texture->read_aspect_flags;
+ copy_region.texture_subresources.mipmap = mm_i;
+ copy_region.texture_subresources.base_layer = p_layer;
+ copy_region.texture_subresources.layer_count = 1;
+ copy_region.texture_offset = Vector3i(0, 0, z);
+ copy_region.texture_region_size = Vector3i(logic_width, logic_height, 1);
+ driver->command_copy_buffer_to_texture(transfer_worker->command_buffer, transfer_worker->staging_buffer, texture->driver_id, copy_dst_layout, copy_region);
+ }
+
+ staging_local_offset += to_allocate;
+ }
+
+ mipmap_offset = image_total;
+ logic_width = MAX(1u, logic_width >> 1);
+ logic_height = MAX(1u, logic_height >> 1);
+ }
+
+ if (copy_pass) {
+ driver->buffer_unmap(transfer_worker->staging_buffer);
+
+ // If the texture does not have a tracker, it means it must be transitioned to the sampling state.
+ if (texture->draw_tracker == nullptr && driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) {
+ RDD::TextureBarrier tb;
+ tb.texture = texture->driver_id;
+ tb.src_access = RDD::BARRIER_ACCESS_COPY_WRITE_BIT;
+ tb.prev_layout = copy_dst_layout;
+ tb.next_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ tb.subresources.aspect = texture->barrier_aspect_flags;
+ tb.subresources.mipmap_count = texture->mipmaps;
+ tb.subresources.base_layer = p_layer;
+ tb.subresources.layer_count = 1;
+ transfer_worker->texture_barriers.push_back(tb);
+ }
+
+ _release_transfer_worker(transfer_worker);
+ }
+ }
+
+ return OK;
+}
+
+Error RenderingDevice::texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data) {
+ ERR_RENDER_THREAD_GUARD_V(ERR_UNAVAILABLE);
+
+ ERR_FAIL_COND_V_MSG(draw_list || compute_list, ERR_INVALID_PARAMETER, "Updating textures is forbidden during creation of a draw or compute list");
Texture *texture = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL_V(texture, ERR_INVALID_PARAMETER);
@@ -1162,47 +1366,37 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve
ERR_FAIL_COND_V_MSG(texture->bound, ERR_CANT_ACQUIRE_RESOURCE,
"Texture can't be updated while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to update this texture.");
- 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.");
+ ERR_FAIL_COND_V_MSG(!(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.");
- ERR_FAIL_COND_V(p_layer >= texture->layers, ERR_INVALID_PARAMETER);
+ uint32_t layer_count = _texture_layer_count(texture);
+ ERR_FAIL_COND_V(p_layer >= layer_count, 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);
uint32_t required_size = tight_mip_size;
- uint32_t required_align = get_compressed_image_format_block_byte_size(texture->format);
- if (required_align == 1) {
- required_align = get_image_format_pixel_size(texture->format);
- }
- required_align = STEPIFY(required_align, driver->api_trait_get(RDD::API_TRAIT_TEXTURE_TRANSFER_ALIGNMENT));
+ uint32_t required_align = _texture_alignment(texture);
ERR_FAIL_COND_V_MSG(required_size != (uint32_t)p_data.size(), ERR_INVALID_PARAMETER,
"Required size for texture update (" + itos(required_size) + ") does not match data supplied size (" + itos(p_data.size()) + ").");
+ _check_transfer_worker_texture(texture);
+
+ uint32_t block_w, block_h;
+ get_compressed_image_format_block_dimensions(texture->format, block_w, block_h);
+
+ uint32_t pixel_size = get_image_format_pixel_size(texture->format);
+ uint32_t pixel_rshift = get_compressed_image_format_pixel_rshift(texture->format);
+ uint32_t block_size = get_compressed_image_format_block_byte_size(texture->format);
+
uint32_t region_size = texture_upload_region_size_px;
- const uint8_t *r = p_data.ptr();
+ const uint8_t *read_ptr = p_data.ptr();
thread_local LocalVector<RDG::RecordedBufferToTextureCopy> command_buffer_to_texture_copies_vector;
command_buffer_to_texture_copies_vector.clear();
- if (p_use_setup_queue && driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) {
- // When using the setup queue directly, we transition the texture to the optimal layout.
- RDD::TextureBarrier tb;
- tb.texture = texture->driver_id;
- tb.dst_access = RDD::BARRIER_ACCESS_COPY_WRITE_BIT;
- tb.prev_layout = RDD::TEXTURE_LAYOUT_UNDEFINED;
- tb.next_layout = RDD::TEXTURE_LAYOUT_COPY_DST_OPTIMAL;
- tb.subresources.aspect = texture->barrier_aspect_flags;
- tb.subresources.mipmap_count = texture->mipmaps;
- tb.subresources.base_layer = p_layer;
- tb.subresources.layer_count = 1;
-
- driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, RDD::PIPELINE_STAGE_COPY_BIT, {}, {}, tb);
- } else if (!p_use_setup_queue) {
- // Indicate the texture will get modified for the shared texture fallback.
- _texture_update_shared_fallback(p_texture, texture, true);
- }
+ // Indicate the texture will get modified for the shared texture fallback.
+ _texture_update_shared_fallback(p_texture, texture, true);
uint32_t mipmap_offset = 0;
@@ -1213,13 +1407,11 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve
uint32_t depth = 0;
uint32_t image_total = get_image_format_required_size(texture->format, texture->width, texture->height, texture->depth, mm_i + 1, &width, &height, &depth);
- const uint8_t *read_ptr_mipmap = r + mipmap_offset;
+ const uint8_t *read_ptr_mipmap = read_ptr + mipmap_offset;
tight_mip_size = image_total - mipmap_offset;
- for (uint32_t z = 0; z < depth; z++) { // For 3D textures, depth may be > 0.
-
- const uint8_t *read_ptr = read_ptr_mipmap + (tight_mip_size / depth) * z;
-
+ for (uint32_t z = 0; z < depth; z++) {
+ const uint8_t *read_ptr_mipmap_layer = read_ptr_mipmap + (tight_mip_size / depth) * z;
for (uint32_t y = 0; y < height; y += region_size) {
for (uint32_t x = 0; x < width; x += region_size) {
uint32_t region_w = MIN(region_size, width - x);
@@ -1228,11 +1420,7 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve
uint32_t region_logic_w = MIN(region_size, logic_width - x);
uint32_t region_logic_h = MIN(region_size, logic_height - y);
- uint32_t pixel_size = get_image_format_pixel_size(texture->format);
- uint32_t block_w = 0, block_h = 0;
- get_compressed_image_format_block_dimensions(texture->format, block_w, block_h);
-
- uint32_t region_pitch = (region_w * pixel_size * block_w) >> get_compressed_image_format_pixel_rshift(texture->format);
+ uint32_t region_pitch = (region_w * pixel_size * block_w) >> pixel_rshift;
uint32_t pitch_step = driver->api_trait_get(RDD::API_TRAIT_TEXTURE_DATA_ROW_PITCH_STEP);
region_pitch = STEPIFY(region_pitch, pitch_step);
uint32_t to_allocate = region_pitch * region_h;
@@ -1241,13 +1429,13 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve
Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, required_action, false);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
- if (!p_use_setup_queue && !command_buffer_to_texture_copies_vector.is_empty() && required_action == STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL) {
+ if (!command_buffer_to_texture_copies_vector.is_empty() && required_action == STAGING_REQUIRED_ACTION_FLUSH_AND_STALL_ALL) {
if (_texture_make_mutable(texture, p_texture)) {
// The texture must be mutable to be used as a copy destination.
draw_graph.add_synchronization();
}
- // If we're using the draw queue and the staging buffer requires flushing everything, we submit the command early and clear the current vector.
+ // If the staging buffer requires flushing everything, we submit the command early and clear the current vector.
draw_graph.add_texture_update(texture->driver_id, texture->draw_tracker, command_buffer_to_texture_copies_vector);
command_buffer_to_texture_copies_vector.clear();
}
@@ -1266,24 +1454,7 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve
ERR_FAIL_COND_V(region_w % block_w, ERR_BUG);
ERR_FAIL_COND_V(region_h % block_h, ERR_BUG);
- if (block_w != 1 || block_h != 1) {
- // Compressed image (blocks).
- // Must copy a block region.
-
- uint32_t block_size = get_compressed_image_format_block_byte_size(texture->format);
- // Re-create current variables in blocky format.
- uint32_t xb = x / block_w;
- uint32_t yb = y / block_h;
- uint32_t wb = width / block_w;
- //uint32_t hb = height / block_h;
- uint32_t region_wb = region_w / block_w;
- uint32_t region_hb = region_h / block_h;
- _copy_region(read_ptr, write_ptr, xb, yb, region_wb, region_hb, wb, region_pitch, block_size);
- } else {
- // Regular image (pixels).
- // Must copy a pixel region.
- _copy_region(read_ptr, write_ptr, x, y, region_w, region_h, width, region_pitch, pixel_size);
- }
+ _copy_region_block_or_regular(read_ptr_mipmap_layer, write_ptr, x, y, width, region_w, region_h, block_w, block_h, region_pitch, pixel_size, block_size);
{ // Unmap.
driver->buffer_unmap(staging_buffer_blocks[staging_buffer_current].driver_id);
@@ -1298,14 +1469,10 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve
copy_region.texture_offset = Vector3i(x, y, z);
copy_region.texture_region_size = Vector3i(region_logic_w, region_logic_h, 1);
- if (p_use_setup_queue) {
- driver->command_copy_buffer_to_texture(frames[frame].setup_command_buffer, staging_buffer_blocks[staging_buffer_current].driver_id, texture->driver_id, RDD::TEXTURE_LAYOUT_COPY_DST_OPTIMAL, copy_region);
- } else {
- RDG::RecordedBufferToTextureCopy buffer_to_texture_copy;
- buffer_to_texture_copy.from_buffer = staging_buffer_blocks[staging_buffer_current].driver_id;
- buffer_to_texture_copy.region = copy_region;
- command_buffer_to_texture_copies_vector.push_back(buffer_to_texture_copy);
- }
+ RDG::RecordedBufferToTextureCopy buffer_to_texture_copy;
+ buffer_to_texture_copy.from_buffer = staging_buffer_blocks[staging_buffer_current].driver_id;
+ buffer_to_texture_copy.region = copy_region;
+ command_buffer_to_texture_copies_vector.push_back(buffer_to_texture_copy);
staging_buffer_blocks.write[staging_buffer_current].fill_amount = alloc_offset + alloc_size;
}
@@ -1317,27 +1484,13 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve
logic_height = MAX(1u, logic_height >> 1);
}
- if (p_use_setup_queue && (texture->draw_tracker == nullptr) && driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) {
- // If the texture does not have a tracker, it means it must be transitioned to the sampling state.
- RDD::TextureBarrier tb;
- tb.texture = texture->driver_id;
- tb.src_access = RDD::BARRIER_ACCESS_COPY_WRITE_BIT;
- tb.prev_layout = RDD::TEXTURE_LAYOUT_COPY_DST_OPTIMAL;
- tb.next_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- tb.subresources.aspect = texture->barrier_aspect_flags;
- tb.subresources.mipmap_count = texture->mipmaps;
- tb.subresources.base_layer = p_layer;
- tb.subresources.layer_count = 1;
- driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_COPY_BIT, RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, {}, {}, tb);
- } else if (!p_use_setup_queue && !command_buffer_to_texture_copies_vector.is_empty()) {
- if (_texture_make_mutable(texture, p_texture)) {
- // The texture must be mutable to be used as a copy destination.
- draw_graph.add_synchronization();
- }
-
- draw_graph.add_texture_update(texture->driver_id, texture->draw_tracker, command_buffer_to_texture_copies_vector);
+ if (_texture_make_mutable(texture, p_texture)) {
+ // The texture must be mutable to be used as a copy destination.
+ draw_graph.add_synchronization();
}
+ draw_graph.add_texture_update(texture->driver_id, texture->draw_tracker, command_buffer_to_texture_copies_vector);
+
return OK;
}
@@ -1587,7 +1740,7 @@ Vector<uint8_t> RenderingDevice::_texture_get_data(Texture *tex, uint32_t p_laye
}
Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_layer) {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD_V(Vector<uint8_t>());
Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL_V(tex, Vector<uint8_t>());
@@ -1599,7 +1752,9 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye
ERR_FAIL_COND_V(p_layer >= tex->layers, Vector<uint8_t>());
- if ((tex->usage_flags & TEXTURE_USAGE_CPU_READ_BIT)) {
+ _check_transfer_worker_texture(tex);
+
+ if (tex->usage_flags & TEXTURE_USAGE_CPU_READ_BIT) {
// Does not need anything fancy, map and read.
return _texture_get_data(tex, p_layer);
} else {
@@ -1701,7 +1856,7 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye
}
bool RenderingDevice::texture_is_shared(RID p_texture) {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD_V(false);
Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL_V(tex, false);
@@ -1709,11 +1864,13 @@ bool RenderingDevice::texture_is_shared(RID p_texture) {
}
bool RenderingDevice::texture_is_valid(RID p_texture) {
+ ERR_RENDER_THREAD_GUARD_V(false);
+
return texture_owner.owns(p_texture);
}
RD::TextureFormat RenderingDevice::texture_get_format(RID p_texture) {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD_V(TextureFormat());
Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL_V(tex, TextureFormat());
@@ -1736,7 +1893,7 @@ RD::TextureFormat RenderingDevice::texture_get_format(RID p_texture) {
}
Size2i RenderingDevice::texture_size(RID p_texture) {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD_V(Size2i());
Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL_V(tex, Size2i());
@@ -1750,7 +1907,7 @@ uint64_t RenderingDevice::texture_get_native_handle(RID p_texture) {
#endif
Error RenderingDevice::texture_copy(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) {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD_V(ERR_UNAVAILABLE);
Texture *src_tex = texture_owner.get_or_null(p_from_texture);
ERR_FAIL_NULL_V(src_tex, ERR_INVALID_PARAMETER);
@@ -1789,6 +1946,9 @@ Error RenderingDevice::texture_copy(RID p_from_texture, RID p_to_texture, const
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).");
+ _check_transfer_worker_texture(src_tex);
+ _check_transfer_worker_texture(dst_tex);
+
RDD::TextureCopyRegion copy_region;
copy_region.src_subresources.aspect = src_tex->read_aspect_flags;
copy_region.src_subresources.mipmap = p_src_mipmap;
@@ -1820,7 +1980,7 @@ Error RenderingDevice::texture_copy(RID p_from_texture, RID p_to_texture, const
}
Error RenderingDevice::texture_resolve_multisample(RID p_from_texture, RID p_to_texture) {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD_V(ERR_UNAVAILABLE);
Texture *src_tex = texture_owner.get_or_null(p_from_texture);
ERR_FAIL_NULL_V(src_tex, ERR_INVALID_PARAMETER);
@@ -1853,6 +2013,9 @@ Error RenderingDevice::texture_resolve_multisample(RID p_from_texture, RID p_to_
// Indicate the texture will get modified for the shared texture fallback.
_texture_update_shared_fallback(p_to_texture, dst_tex, true);
+ _check_transfer_worker_texture(src_tex);
+ _check_transfer_worker_texture(dst_tex);
+
// The textures must be mutable to be used in the resolve operation.
bool src_made_mutable = _texture_make_mutable(src_tex, p_from_texture);
bool dst_made_mutable = _texture_make_mutable(dst_tex, p_to_texture);
@@ -1866,7 +2029,7 @@ Error RenderingDevice::texture_resolve_multisample(RID p_from_texture, RID p_to_
}
Error RenderingDevice::texture_clear(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) {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD_V(ERR_UNAVAILABLE);
Texture *src_tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL_V(src_tex, ERR_INVALID_PARAMETER);
@@ -1883,6 +2046,8 @@ Error RenderingDevice::texture_clear(RID p_texture, const Color &p_color, uint32
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_tex->layers, ERR_INVALID_PARAMETER);
+ _check_transfer_worker_texture(src_tex);
+
RDD::TextureSubresourceRange range;
range.aspect = src_tex->read_aspect_flags;
range.base_mipmap = src_tex->base_mipmap + p_base_mipmap;
@@ -1906,8 +2071,6 @@ Error RenderingDevice::texture_clear(RID p_texture, const Color &p_color, uint32
bool RenderingDevice::texture_is_format_supported_for_usage(DataFormat p_format, BitField<RenderingDevice::TextureUsageBits> p_usage) const {
ERR_FAIL_INDEX_V(p_format, DATA_FORMAT_MAX, false);
- _THREAD_SAFE_METHOD_
-
bool cpu_readable = (p_usage & RDD::TEXTURE_USAGE_CPU_READ_BIT);
BitField<TextureUsageBits> supported = driver->texture_get_usages_supported_by_format(p_format, cpu_readable);
bool any_unsupported = (((int64_t)supported) | ((int64_t)p_usage)) != ((int64_t)supported);
@@ -2081,6 +2244,8 @@ RDD::RenderPassID RenderingDevice::_render_pass_create(const Vector<AttachmentFo
}
for (int j = 0; j < pass->resolve_attachments.size(); j++) {
int32_t attachment = pass->resolve_attachments[j];
+ attachments[attachment].load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE;
+
RDD::AttachmentReference reference;
if (attachment == ATTACHMENT_UNUSED) {
reference.attachment = RDD::AttachmentReference::UNUSED;
@@ -2212,10 +2377,20 @@ RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create_
fb_format.pass_samples = samples;
fb_format.view_count = p_view_count;
framebuffer_formats[id] = fb_format;
+
+#if PRINT_FRAMEBUFFER_FORMAT
+ print_line("FRAMEBUFFER FORMAT:", id, "ATTACHMENTS:", p_attachments.size(), "PASSES:", p_passes.size());
+ for (RD::AttachmentFormat attachment : p_attachments) {
+ print_line("FORMAT:", attachment.format, "SAMPLES:", attachment.samples, "USAGE FLAGS:", attachment.usage_flags);
+ }
+#endif
+
return id;
}
RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create_empty(TextureSamples p_samples) {
+ _THREAD_SAFE_METHOD_
+
FramebufferFormatKey key;
key.passes.push_back(FramebufferPass());
@@ -2240,10 +2415,17 @@ RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create_
fb_format.render_pass = render_pass;
fb_format.pass_samples.push_back(p_samples);
framebuffer_formats[id] = fb_format;
+
+#if PRINT_FRAMEBUFFER_FORMAT
+ print_line("FRAMEBUFFER FORMAT:", id, "ATTACHMENTS: EMPTY");
+#endif
+
return id;
}
RenderingDevice::TextureSamples RenderingDevice::framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass) {
+ _THREAD_SAFE_METHOD_
+
HashMap<FramebufferFormatID, FramebufferFormat>::Iterator E = framebuffer_formats.find(p_format);
ERR_FAIL_COND_V(!E, TEXTURE_SAMPLES_1);
ERR_FAIL_COND_V(p_pass >= uint32_t(E->value.pass_samples.size()), TEXTURE_SAMPLES_1);
@@ -2253,6 +2435,7 @@ RenderingDevice::TextureSamples RenderingDevice::framebuffer_format_get_texture_
RID RenderingDevice::framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples, FramebufferFormatID p_format_check) {
_THREAD_SAFE_METHOD_
+
Framebuffer framebuffer;
framebuffer.format_id = framebuffer_format_create_empty(p_samples);
ERR_FAIL_COND_V(p_format_check != INVALID_FORMAT_ID && framebuffer.format_id != p_format_check, RID());
@@ -2276,6 +2459,10 @@ RID RenderingDevice::framebuffer_create(const Vector<RID> &p_texture_attachments
ERR_FAIL_COND_V_MSG(texture && texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer");
+ if (texture != nullptr) {
+ _check_transfer_worker_texture(texture);
+ }
+
if (texture && texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
pass.depth_attachment = i;
} else if (texture && texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) {
@@ -2310,6 +2497,8 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector<RID> &p_texture_a
} else {
ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer");
+ _check_transfer_worker_texture(texture);
+
if (!size_set) {
size.width = texture->width;
size.height = texture->height;
@@ -2409,10 +2598,10 @@ RID RenderingDevice::sampler_create(const SamplerState &p_state) {
}
bool RenderingDevice::sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_sampler_filter) const {
- ERR_FAIL_INDEX_V(p_format, DATA_FORMAT_MAX, false);
-
_THREAD_SAFE_METHOD_
+ ERR_FAIL_INDEX_V(p_format, DATA_FORMAT_MAX, false);
+
return driver->sampler_is_format_supported_for_filter(p_format, p_sampler_filter);
}
@@ -2421,8 +2610,6 @@ bool RenderingDevice::sampler_is_format_supported_for_filter(DataFormat p_format
/***********************/
RID RenderingDevice::vertex_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, bool p_use_as_storage) {
- _THREAD_SAFE_METHOD_
-
ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID());
Buffer buffer;
@@ -2441,10 +2628,12 @@ RID RenderingDevice::vertex_buffer_create(uint32_t p_size_bytes, const Vector<ui
}
if (p_data.size()) {
- _buffer_update(&buffer, RID(), 0, p_data.ptr(), p_data.size());
+ _buffer_initialize(&buffer, p_data.ptr(), p_data.size());
}
+ _THREAD_SAFE_LOCK_
buffer_memory += buffer.size;
+ _THREAD_SAFE_UNLOCK_
RID id = vertex_buffer_owner.make_rid(buffer);
#ifdef DEV_ENABLED
@@ -2544,6 +2733,11 @@ RID RenderingDevice::vertex_array_create(uint32_t p_vertex_count, VertexFormatID
} else {
vertex_array.untracked_buffers.insert(p_src_buffers[i]);
}
+
+ if (buffer->transfer_worker_index >= 0) {
+ vertex_array.transfer_worker_indices.push_back(buffer->transfer_worker_index);
+ vertex_array.transfer_worker_operations.push_back(buffer->transfer_worker_operation);
+ }
}
RID id = vertex_array_owner.make_rid(vertex_array);
@@ -2555,8 +2749,6 @@ RID RenderingDevice::vertex_array_create(uint32_t p_vertex_count, VertexFormatID
}
RID RenderingDevice::index_buffer_create(uint32_t p_index_count, IndexBufferFormat p_format, const Vector<uint8_t> &p_data, bool p_use_restart_indices) {
- _THREAD_SAFE_METHOD_
-
ERR_FAIL_COND_V(p_index_count == 0, RID());
IndexBuffer index_buffer;
@@ -2605,10 +2797,12 @@ RID RenderingDevice::index_buffer_create(uint32_t p_index_count, IndexBufferForm
}
if (p_data.size()) {
- _buffer_update(&index_buffer, RID(), 0, p_data.ptr(), p_data.size());
+ _buffer_initialize(&index_buffer, p_data.ptr(), p_data.size());
}
+ _THREAD_SAFE_LOCK_
buffer_memory += index_buffer.size;
+ _THREAD_SAFE_UNLOCK_
RID id = index_buffer_owner.make_rid(index_buffer);
#ifdef DEV_ENABLED
@@ -2635,6 +2829,8 @@ RID RenderingDevice::index_array_create(RID p_index_buffer, uint32_t p_index_off
index_array.indices = p_index_count;
index_array.format = index_buffer->format;
index_array.supports_restart_indices = index_buffer->supports_restart_indices;
+ index_array.transfer_worker_index = index_buffer->transfer_worker_index;
+ index_array.transfer_worker_operation = index_buffer->transfer_worker_operation;
RID id = index_array_owner.make_rid(index_array);
_add_dependency(id, p_index_buffer);
@@ -2753,6 +2949,8 @@ RID RenderingDevice::shader_create_from_bytecode(const Vector<uint8_t> &p_shader
}
RID RenderingDevice::shader_create_placeholder() {
+ _THREAD_SAFE_METHOD_
+
Shader shader;
return shader_owner.make_rid(shader);
}
@@ -2770,8 +2968,6 @@ uint64_t RenderingDevice::shader_get_vertex_input_attribute_mask(RID p_shader) {
/******************/
RID RenderingDevice::uniform_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data) {
- _THREAD_SAFE_METHOD_
-
ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID());
Buffer buffer;
@@ -2787,10 +2983,12 @@ RID RenderingDevice::uniform_buffer_create(uint32_t p_size_bytes, const Vector<u
}
if (p_data.size()) {
- _buffer_update(&buffer, RID(), 0, p_data.ptr(), p_data.size());
+ _buffer_initialize(&buffer, p_data.ptr(), p_data.size());
}
+ _THREAD_SAFE_LOCK_
buffer_memory += buffer.size;
+ _THREAD_SAFE_UNLOCK_
RID id = uniform_buffer_owner.make_rid(buffer);
#ifdef DEV_ENABLED
@@ -2921,6 +3119,7 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p
driver_uniform.ids.push_back(*sampler_driver_id);
driver_uniform.ids.push_back(driver_id);
+ _check_transfer_worker_texture(texture);
}
} break;
case UNIFORM_TYPE_TEXTURE: {
@@ -2965,6 +3164,7 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p
DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner));
driver_uniform.ids.push_back(driver_id);
+ _check_transfer_worker_texture(texture);
}
} break;
case UNIFORM_TYPE_IMAGE: {
@@ -3008,6 +3208,7 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p
DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner));
driver_uniform.ids.push_back(texture->driver_id);
+ _check_transfer_worker_texture(texture);
}
} break;
case UNIFORM_TYPE_TEXTURE_BUFFER: {
@@ -3042,6 +3243,7 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p
}
driver_uniform.ids.push_back(buffer->driver_id);
+ _check_transfer_worker_buffer(buffer);
}
} break;
case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER: {
@@ -3070,6 +3272,7 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p
driver_uniform.ids.push_back(*sampler_driver_id);
driver_uniform.ids.push_back(buffer->driver_id);
+ _check_transfer_worker_buffer(buffer);
}
} break;
case UNIFORM_TYPE_IMAGE_BUFFER: {
@@ -3094,6 +3297,7 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p
}
driver_uniform.ids.push_back(buffer->driver_id);
+ _check_transfer_worker_buffer(buffer);
} break;
case UNIFORM_TYPE_STORAGE_BUFFER: {
ERR_FAIL_COND_V_MSG(uniform.get_id_count() != 1, RID(),
@@ -3133,6 +3337,7 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p
}
driver_uniform.ids.push_back(buffer->driver_id);
+ _check_transfer_worker_buffer(buffer);
} break;
case UNIFORM_TYPE_INPUT_ATTACHMENT: {
ERR_FAIL_COND_V_MSG(shader->is_compute, RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") supplied for compute shader (this is not allowed).");
@@ -3158,6 +3363,7 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p
DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner));
driver_uniform.ids.push_back(texture->driver_id);
+ _check_transfer_worker_texture(texture);
}
} break;
default: {
@@ -3197,10 +3403,14 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p
}
bool RenderingDevice::uniform_set_is_valid(RID p_uniform_set) {
+ _THREAD_SAFE_METHOD_
+
return uniform_set_owner.owns(p_uniform_set);
}
void RenderingDevice::uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata) {
+ _THREAD_SAFE_METHOD_
+
UniformSet *us = uniform_set_owner.get_or_null(p_uniform_set);
ERR_FAIL_NULL(us);
us->invalidated_callback = p_callback;
@@ -3212,21 +3422,22 @@ void RenderingDevice::uniform_set_set_invalidation_callback(RID p_uniform_set, I
/*******************/
RID RenderingDevice::render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, BitField<PipelineDynamicStateFlags> p_dynamic_state_flags, uint32_t p_for_render_pass, const Vector<PipelineSpecializationConstant> &p_specialization_constants) {
- _THREAD_SAFE_METHOD_
-
// Needs a shader.
Shader *shader = shader_owner.get_or_null(p_shader);
ERR_FAIL_NULL_V(shader, RID());
+ ERR_FAIL_COND_V_MSG(shader->is_compute, RID(), "Compute shaders can't be used in render pipelines");
- ERR_FAIL_COND_V_MSG(shader->is_compute, RID(),
- "Compute shaders can't be used in render pipelines");
+ FramebufferFormat fb_format;
+ {
+ _THREAD_SAFE_METHOD_
- if (p_framebuffer_format == INVALID_ID) {
- // If nothing provided, use an empty one (no attachments).
- p_framebuffer_format = framebuffer_format_create(Vector<AttachmentFormat>());
+ if (p_framebuffer_format == INVALID_ID) {
+ // If nothing provided, use an empty one (no attachments).
+ p_framebuffer_format = framebuffer_format_create(Vector<AttachmentFormat>());
+ }
+ ERR_FAIL_COND_V(!framebuffer_formats.has(p_framebuffer_format), RID());
+ fb_format = framebuffer_formats[p_framebuffer_format];
}
- ERR_FAIL_COND_V(!framebuffer_formats.has(p_framebuffer_format), RID());
- const FramebufferFormat &fb_format = framebuffer_formats[p_framebuffer_format];
// Validate shader vs. framebuffer.
{
@@ -3372,30 +3583,41 @@ RID RenderingDevice::render_pipeline_create(RID p_shader, FramebufferFormatID p_
};
pipeline.validation.primitive_minimum = primitive_minimum[p_render_primitive];
#endif
+
// Create ID to associate with this pipeline.
RID id = render_pipeline_owner.make_rid(pipeline);
+ {
+ _THREAD_SAFE_METHOD_
+
#ifdef DEV_ENABLED
- set_resource_name(id, "RID:" + itos(id.get_id()));
+ set_resource_name(id, "RID:" + itos(id.get_id()));
#endif
- // Now add all the dependencies.
- _add_dependency(id, p_shader);
+ // Now add all the dependencies.
+ _add_dependency(id, p_shader);
+ }
+
return id;
}
bool RenderingDevice::render_pipeline_is_valid(RID p_pipeline) {
_THREAD_SAFE_METHOD_
+
return render_pipeline_owner.owns(p_pipeline);
}
RID RenderingDevice::compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants) {
- _THREAD_SAFE_METHOD_
+ Shader *shader;
- // Needs a shader.
- Shader *shader = shader_owner.get_or_null(p_shader);
- ERR_FAIL_NULL_V(shader, RID());
+ {
+ _THREAD_SAFE_METHOD_
+
+ // Needs a shader.
+ shader = shader_owner.get_or_null(p_shader);
+ ERR_FAIL_NULL_V(shader, RID());
- ERR_FAIL_COND_V_MSG(!shader->is_compute, RID(),
- "Non-compute shaders can't be used in compute pipelines");
+ ERR_FAIL_COND_V_MSG(!shader->is_compute, RID(),
+ "Non-compute shaders can't be used in compute pipelines");
+ }
for (int i = 0; i < shader->specialization_constants.size(); i++) {
const ShaderSpecializationConstant &sc = shader->specialization_constants[i];
@@ -3427,15 +3649,22 @@ RID RenderingDevice::compute_pipeline_create(RID p_shader, const Vector<Pipeline
// Create ID to associate with this pipeline.
RID id = compute_pipeline_owner.make_rid(pipeline);
+ {
+ _THREAD_SAFE_METHOD_
+
#ifdef DEV_ENABLED
- set_resource_name(id, "RID:" + itos(id.get_id()));
+ set_resource_name(id, "RID:" + itos(id.get_id()));
#endif
- // Now add all the dependencies.
- _add_dependency(id, p_shader);
+ // Now add all the dependencies.
+ _add_dependency(id, p_shader);
+ }
+
return id;
}
bool RenderingDevice::compute_pipeline_is_valid(RID p_pipeline) {
+ _THREAD_SAFE_METHOD_
+
return compute_pipeline_owner.owns(p_pipeline);
}
@@ -3515,6 +3744,7 @@ Error RenderingDevice::screen_prepare_for_drawing(DisplayServer::WindowID p_scre
int RenderingDevice::screen_get_width(DisplayServer::WindowID p_screen) const {
_THREAD_SAFE_METHOD_
+
RenderingContextDriver::SurfaceID surface = context->surface_get_from_window(p_screen);
ERR_FAIL_COND_V_MSG(surface == 0, 0, "A surface was not created for the screen.");
return context->surface_get_width(surface);
@@ -3522,6 +3752,7 @@ int RenderingDevice::screen_get_width(DisplayServer::WindowID p_screen) const {
int RenderingDevice::screen_get_height(DisplayServer::WindowID p_screen) const {
_THREAD_SAFE_METHOD_
+
RenderingContextDriver::SurfaceID surface = context->surface_get_from_window(p_screen);
ERR_FAIL_COND_V_MSG(surface == 0, 0, "A surface was not created for the screen.");
return context->surface_get_height(surface);
@@ -3568,7 +3799,7 @@ Error RenderingDevice::screen_free(DisplayServer::WindowID p_screen) {
/*******************/
RenderingDevice::DrawListID RenderingDevice::draw_list_begin_for_screen(DisplayServer::WindowID p_screen, const Color &p_clear_color) {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD_V(INVALID_ID);
ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time.");
ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time.");
@@ -3594,8 +3825,8 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin_for_screen(DisplayS
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, RDD::BreadcrumbMarker::BLIT_PASS);
- _draw_list_set_viewport(viewport);
- _draw_list_set_scissor(viewport);
+ draw_graph.add_draw_list_set_viewport(viewport);
+ draw_graph.add_draw_list_set_scissor(viewport);
return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT;
}
@@ -3707,14 +3938,6 @@ Error RenderingDevice::_draw_list_render_pass_begin(Framebuffer *p_framebuffer,
return OK;
}
-void RenderingDevice::_draw_list_set_viewport(Rect2i p_rect) {
- draw_graph.add_draw_list_set_viewport(p_rect);
-}
-
-void RenderingDevice::_draw_list_set_scissor(Rect2i p_rect) {
- draw_graph.add_draw_list_set_scissor(p_rect);
-}
-
void RenderingDevice::_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) {
LocalVector<RDD::AttachmentClear> clear_attachments;
int color_index = 0;
@@ -3752,7 +3975,7 @@ void RenderingDevice::_draw_list_insert_clear_region(DrawList *p_draw_list, Fram
}
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_RENDER_THREAD_GUARD_V(INVALID_ID);
ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time.");
@@ -3812,8 +4035,9 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer,
#endif
draw_list_current_subpass = 0;
- _draw_list_set_viewport(Rect2i(viewport_offset, viewport_size));
- _draw_list_set_scissor(Rect2i(viewport_offset, viewport_size));
+ Rect2i viewport_rect(viewport_offset, viewport_size);
+ draw_graph.add_draw_list_set_viewport(viewport_rect);
+ draw_graph.add_draw_list_set_scissor(viewport_rect);
return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT;
}
@@ -3839,6 +4063,8 @@ RenderingDevice::DrawList *RenderingDevice::_get_draw_list_ptr(DrawListID p_id)
}
void RenderingDevice::draw_list_set_blend_constants(DrawListID p_list, const Color &p_color) {
+ ERR_RENDER_THREAD_GUARD();
+
DrawList *dl = _get_draw_list_ptr(p_list);
ERR_FAIL_NULL(dl);
#ifdef DEBUG_ENABLED
@@ -3849,6 +4075,8 @@ void RenderingDevice::draw_list_set_blend_constants(DrawListID p_list, const Col
}
void RenderingDevice::draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline) {
+ ERR_RENDER_THREAD_GUARD();
+
DrawList *dl = _get_draw_list_ptr(p_list);
ERR_FAIL_NULL(dl);
#ifdef DEBUG_ENABLED
@@ -3877,23 +4105,35 @@ void RenderingDevice::draw_list_bind_render_pipeline(DrawListID p_list, RID p_re
const uint32_t *pformats = pipeline->set_formats.ptr(); // Pipeline set formats.
uint32_t first_invalid_set = UINT32_MAX; // All valid by default.
- switch (driver->api_trait_get(RDD::API_TRAIT_SHADER_CHANGE_INVALIDATION)) {
- case RDD::SHADER_CHANGE_INVALIDATION_ALL_BOUND_UNIFORM_SETS: {
- first_invalid_set = 0;
- } break;
- case RDD::SHADER_CHANGE_INVALIDATION_INCOMPATIBLE_SETS_PLUS_CASCADE: {
- for (uint32_t i = 0; i < pcount; i++) {
- if (dl->state.sets[i].pipeline_expected_format != pformats[i]) {
- first_invalid_set = i;
- break;
- }
- }
- } break;
- case RDD::SHADER_CHANGE_INVALIDATION_ALL_OR_NONE_ACCORDING_TO_LAYOUT_HASH: {
- if (dl->state.pipeline_shader_layout_hash != pipeline->shader_layout_hash) {
+ if (pipeline->push_constant_size != dl->state.pipeline_push_constant_size) {
+ // All sets must be invalidated as the pipeline layout is not compatible if the push constant range is different.
+ dl->state.pipeline_push_constant_size = pipeline->push_constant_size;
+ first_invalid_set = 0;
+ } else {
+ switch (driver->api_trait_get(RDD::API_TRAIT_SHADER_CHANGE_INVALIDATION)) {
+ case RDD::SHADER_CHANGE_INVALIDATION_ALL_BOUND_UNIFORM_SETS: {
first_invalid_set = 0;
- }
- } break;
+ } break;
+ case RDD::SHADER_CHANGE_INVALIDATION_INCOMPATIBLE_SETS_PLUS_CASCADE: {
+ for (uint32_t i = 0; i < pcount; i++) {
+ if (dl->state.sets[i].pipeline_expected_format != pformats[i]) {
+ first_invalid_set = i;
+ break;
+ }
+ }
+ } break;
+ case RDD::SHADER_CHANGE_INVALIDATION_ALL_OR_NONE_ACCORDING_TO_LAYOUT_HASH: {
+ if (dl->state.pipeline_shader_layout_hash != pipeline->shader_layout_hash) {
+ first_invalid_set = 0;
+ }
+ } break;
+ }
+ }
+
+ if (pipeline->push_constant_size) {
+#ifdef DEBUG_ENABLED
+ dl->validation.pipeline_push_constant_supplied = false;
+#endif
}
for (uint32_t i = 0; i < pcount; i++) {
@@ -3908,12 +4148,6 @@ void RenderingDevice::draw_list_bind_render_pipeline(DrawListID p_list, RID p_re
dl->state.set_count = pcount; // Update set count.
- if (pipeline->push_constant_size) {
-#ifdef DEBUG_ENABLED
- dl->validation.pipeline_push_constant_supplied = false;
-#endif
- }
-
dl->state.pipeline_shader = pipeline->shader;
dl->state.pipeline_shader_driver_id = pipeline->shader_driver_id;
dl->state.pipeline_shader_layout_hash = pipeline->shader_layout_hash;
@@ -3932,6 +4166,8 @@ void RenderingDevice::draw_list_bind_render_pipeline(DrawListID p_list, RID p_re
}
void RenderingDevice::draw_list_bind_uniform_set(DrawListID p_list, RID p_uniform_set, uint32_t p_index) {
+ ERR_RENDER_THREAD_GUARD();
+
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_MSG(p_index >= driver->limit_get(LIMIT_MAX_BOUND_UNIFORM_SETS) || p_index >= MAX_UNIFORM_SETS,
"Attempting to bind a descriptor set (" + itos(p_index) + ") greater than what the hardware supports (" + itos(driver->limit_get(LIMIT_MAX_BOUND_UNIFORM_SETS)) + ").");
@@ -3972,19 +4208,23 @@ void RenderingDevice::draw_list_bind_uniform_set(DrawListID p_list, RID p_unifor
}
void RenderingDevice::draw_list_bind_vertex_array(DrawListID p_list, RID p_vertex_array) {
+ ERR_RENDER_THREAD_GUARD();
+
DrawList *dl = _get_draw_list_ptr(p_list);
ERR_FAIL_NULL(dl);
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified.");
#endif
- const VertexArray *vertex_array = vertex_array_owner.get_or_null(p_vertex_array);
+ VertexArray *vertex_array = vertex_array_owner.get_or_null(p_vertex_array);
ERR_FAIL_NULL(vertex_array);
if (dl->state.vertex_array == p_vertex_array) {
return; // Already set.
}
+ _check_transfer_worker_vertex_array(vertex_array);
+
dl->state.vertex_array = p_vertex_array;
#ifdef DEBUG_ENABLED
@@ -4001,19 +4241,23 @@ void RenderingDevice::draw_list_bind_vertex_array(DrawListID p_list, RID p_verte
}
void RenderingDevice::draw_list_bind_index_array(DrawListID p_list, RID p_index_array) {
+ ERR_RENDER_THREAD_GUARD();
+
DrawList *dl = _get_draw_list_ptr(p_list);
ERR_FAIL_NULL(dl);
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified.");
#endif
- const IndexArray *index_array = index_array_owner.get_or_null(p_index_array);
+ IndexArray *index_array = index_array_owner.get_or_null(p_index_array);
ERR_FAIL_NULL(index_array);
if (dl->state.index_array == p_index_array) {
return; // Already set.
}
+ _check_transfer_worker_index_array(index_array);
+
dl->state.index_array = p_index_array;
#ifdef DEBUG_ENABLED
dl->validation.index_array_max_index = index_array->max_index;
@@ -4029,6 +4273,8 @@ void RenderingDevice::draw_list_bind_index_array(DrawListID p_list, RID p_index_
}
void RenderingDevice::draw_list_set_line_width(DrawListID p_list, float p_width) {
+ ERR_RENDER_THREAD_GUARD();
+
DrawList *dl = _get_draw_list_ptr(p_list);
ERR_FAIL_NULL(dl);
#ifdef DEBUG_ENABLED
@@ -4039,6 +4285,8 @@ void RenderingDevice::draw_list_set_line_width(DrawListID p_list, float p_width)
}
void RenderingDevice::draw_list_set_push_constant(DrawListID p_list, const void *p_data, uint32_t p_data_size) {
+ ERR_RENDER_THREAD_GUARD();
+
DrawList *dl = _get_draw_list_ptr(p_list);
ERR_FAIL_NULL(dl);
@@ -4059,6 +4307,8 @@ void RenderingDevice::draw_list_set_push_constant(DrawListID p_list, const void
}
void RenderingDevice::draw_list_draw(DrawListID p_list, bool p_use_indices, uint32_t p_instances, uint32_t p_procedural_vertices) {
+ ERR_RENDER_THREAD_GUARD();
+
DrawList *dl = _get_draw_list_ptr(p_list);
ERR_FAIL_NULL(dl);
#ifdef DEBUG_ENABLED
@@ -4192,6 +4442,8 @@ void RenderingDevice::draw_list_draw(DrawListID p_list, bool p_use_indices, uint
}
void RenderingDevice::draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect) {
+ ERR_RENDER_THREAD_GUARD();
+
DrawList *dl = _get_draw_list_ptr(p_list);
ERR_FAIL_NULL(dl);
@@ -4207,25 +4459,30 @@ void RenderingDevice::draw_list_enable_scissor(DrawListID p_list, const Rect2 &p
return;
}
- _draw_list_set_scissor(rect);
+ draw_graph.add_draw_list_set_scissor(rect);
}
void RenderingDevice::draw_list_disable_scissor(DrawListID p_list) {
+ ERR_RENDER_THREAD_GUARD();
+
DrawList *dl = _get_draw_list_ptr(p_list);
ERR_FAIL_NULL(dl);
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified.");
#endif
- _draw_list_set_scissor(dl->viewport);
+ draw_graph.add_draw_list_set_scissor(dl->viewport);
}
uint32_t RenderingDevice::draw_list_get_current_pass() {
+ ERR_RENDER_THREAD_GUARD_V(0);
+
return draw_list_current_subpass;
}
RenderingDevice::DrawListID RenderingDevice::draw_list_switch_to_next_pass() {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD_V(INVALID_ID);
+
ERR_FAIL_NULL_V(draw_list, INVALID_ID);
ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, INVALID_FORMAT_ID);
@@ -4248,9 +4505,6 @@ Error RenderingDevice::draw_list_switch_to_next_pass_split(uint32_t p_splits, Dr
#endif
Error RenderingDevice::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_subpass) {
- // Lock while draw_list is active.
- _THREAD_SAFE_LOCK_
-
draw_list = memnew(DrawList);
draw_list->viewport = p_viewport;
@@ -4264,13 +4518,10 @@ void RenderingDevice::_draw_list_free(Rect2i *r_last_viewport) {
// Just end the list.
memdelete(draw_list);
draw_list = nullptr;
-
- // Draw_list is no longer active.
- _THREAD_SAFE_UNLOCK_
}
void RenderingDevice::draw_list_end() {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD();
ERR_FAIL_NULL_MSG(draw_list, "Immediate draw list is already inactive.");
@@ -4297,13 +4548,10 @@ void RenderingDevice::draw_list_end() {
/***********************/
RenderingDevice::ComputeListID RenderingDevice::compute_list_begin() {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD_V(INVALID_ID);
ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time.");
- // Lock while compute_list is active.
- _THREAD_SAFE_LOCK_
-
compute_list = memnew(ComputeList);
draw_graph.add_compute_list_begin();
@@ -4312,7 +4560,7 @@ RenderingDevice::ComputeListID RenderingDevice::compute_list_begin() {
}
void RenderingDevice::compute_list_bind_compute_pipeline(ComputeListID p_list, RID p_compute_pipeline) {
- // Must be called within a compute list, the class mutex is locked during that time
+ ERR_RENDER_THREAD_GUARD();
ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST);
ERR_FAIL_NULL(compute_list);
@@ -4391,7 +4639,7 @@ void RenderingDevice::compute_list_bind_compute_pipeline(ComputeListID p_list, R
}
void RenderingDevice::compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index) {
- // Must be called within a compute list, the class mutex is locked during that time
+ ERR_RENDER_THREAD_GUARD();
ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST);
ERR_FAIL_NULL(compute_list);
@@ -4436,6 +4684,8 @@ void RenderingDevice::compute_list_bind_uniform_set(ComputeListID p_list, RID p_
}
void RenderingDevice::compute_list_set_push_constant(ComputeListID p_list, const void *p_data, uint32_t p_data_size) {
+ ERR_RENDER_THREAD_GUARD();
+
ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST);
ERR_FAIL_NULL(compute_list);
ERR_FAIL_COND_MSG(p_data_size > MAX_PUSH_CONSTANT_SIZE, "Push constants can't be bigger than 128 bytes to maintain compatibility.");
@@ -4463,7 +4713,7 @@ void RenderingDevice::compute_list_set_push_constant(ComputeListID p_list, const
}
void RenderingDevice::compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) {
- // Must be called within a compute list, the class mutex is locked during that time
+ ERR_RENDER_THREAD_GUARD();
ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST);
ERR_FAIL_NULL(compute_list);
@@ -4551,6 +4801,8 @@ void RenderingDevice::compute_list_dispatch(ComputeListID p_list, uint32_t p_x_g
}
void RenderingDevice::compute_list_dispatch_threads(ComputeListID p_list, uint32_t p_x_threads, uint32_t p_y_threads, uint32_t p_z_threads) {
+ ERR_RENDER_THREAD_GUARD();
+
ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST);
ERR_FAIL_NULL(compute_list);
@@ -4578,6 +4830,8 @@ void RenderingDevice::compute_list_dispatch_threads(ComputeListID p_list, uint32
}
void RenderingDevice::compute_list_dispatch_indirect(ComputeListID p_list, RID p_buffer, uint32_t p_offset) {
+ ERR_RENDER_THREAD_GUARD();
+
ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST);
ERR_FAIL_NULL(compute_list);
@@ -4661,10 +4915,12 @@ void RenderingDevice::compute_list_dispatch_indirect(ComputeListID p_list, RID p
if (buffer->draw_tracker != nullptr) {
draw_graph.add_compute_list_usage(buffer->draw_tracker, RDG::RESOURCE_USAGE_INDIRECT_BUFFER_READ);
}
+
+ _check_transfer_worker_buffer(buffer);
}
void RenderingDevice::compute_list_add_barrier(ComputeListID p_list) {
- // Must be called within a compute list, the class mutex is locked during that time
+ ERR_RENDER_THREAD_GUARD();
compute_list_barrier_state = compute_list->state;
compute_list_end();
@@ -4686,15 +4942,14 @@ void RenderingDevice::compute_list_add_barrier(ComputeListID p_list) {
}
void RenderingDevice::compute_list_end() {
+ ERR_RENDER_THREAD_GUARD();
+
ERR_FAIL_NULL(compute_list);
draw_graph.add_compute_list_end();
memdelete(compute_list);
compute_list = nullptr;
-
- // Compute_list is no longer active.
- _THREAD_SAFE_UNLOCK_
}
#ifndef DISABLE_DEPRECATED
@@ -4707,6 +4962,306 @@ void RenderingDevice::full_barrier() {
}
#endif
+/*************************/
+/**** TRANSFER WORKER ****/
+/*************************/
+
+static uint32_t _get_alignment_offset(uint32_t p_offset, uint32_t p_required_align) {
+ uint32_t alignment_offset = (p_required_align > 0) ? (p_offset % p_required_align) : 0;
+ if (alignment_offset != 0) {
+ // If a particular alignment is required, add the offset as part of the required size.
+ alignment_offset = p_required_align - alignment_offset;
+ }
+
+ return alignment_offset;
+}
+
+RenderingDevice::TransferWorker *RenderingDevice::_acquire_transfer_worker(uint32_t p_transfer_size, uint32_t p_required_align, uint32_t &r_staging_offset) {
+ // Find the first worker that is not currently executing anything and has enough size for the transfer.
+ // If no workers are available, we make a new one. If we're not allowed to make new ones, we wait until one of them is available.
+ TransferWorker *transfer_worker = nullptr;
+ uint32_t available_list_index = 0;
+ bool transfer_worker_busy = true;
+ bool transfer_worker_full = true;
+ {
+ MutexLock pool_lock(transfer_worker_pool_mutex);
+
+ // If no workers are available and we've reached the max pool capacity, wait until one of them becomes available.
+ bool transfer_worker_pool_full = transfer_worker_pool.size() >= transfer_worker_pool_max_size;
+ while (transfer_worker_pool_available_list.is_empty() && transfer_worker_pool_full) {
+ transfer_worker_pool_condition.wait(pool_lock);
+ }
+
+ // Look at all available workers first.
+ for (uint32_t i = 0; i < transfer_worker_pool_available_list.size(); i++) {
+ uint32_t worker_index = transfer_worker_pool_available_list[i];
+ TransferWorker *candidate_worker = transfer_worker_pool[worker_index];
+ candidate_worker->thread_mutex.lock();
+
+ // Figure out if the worker can fit the transfer.
+ uint32_t alignment_offset = _get_alignment_offset(candidate_worker->staging_buffer_size_in_use, p_required_align);
+ uint32_t required_size = candidate_worker->staging_buffer_size_in_use + p_transfer_size + alignment_offset;
+ bool candidate_worker_busy = candidate_worker->submitted;
+ bool candidate_worker_full = required_size > candidate_worker->staging_buffer_size_allocated;
+ bool pick_candidate = false;
+ if (!candidate_worker_busy && !candidate_worker_full) {
+ // A worker that can fit the transfer and is not waiting for a previous execution is the best possible candidate.
+ pick_candidate = true;
+ } else if (!candidate_worker_busy) {
+ // The worker can't fit the transfer but it's not currently doing anything.
+ // We pick it as a possible candidate if the current one is busy.
+ pick_candidate = transfer_worker_busy;
+ } else if (!candidate_worker_full) {
+ // The worker can fit the transfer but it's currently executing previous work.
+ // We pick it as a possible candidate if the current one is both busy and full.
+ pick_candidate = transfer_worker_busy && transfer_worker_full;
+ } else if (transfer_worker == nullptr) {
+ // The worker can't fit the transfer and it's currently executing work, so it's the worst candidate.
+ // We only pick if no candidate has been picked yet.
+ pick_candidate = true;
+ }
+
+ if (pick_candidate) {
+ if (transfer_worker != nullptr) {
+ // Release the lock for the worker that was picked previously.
+ transfer_worker->thread_mutex.unlock();
+ }
+
+ // Keep the lock active for this worker.
+ transfer_worker = candidate_worker;
+ transfer_worker_busy = candidate_worker_busy;
+ transfer_worker_full = candidate_worker_full;
+ available_list_index = i;
+
+ if (!transfer_worker_busy && !transfer_worker_full) {
+ // Best possible candidate, stop searching early.
+ break;
+ }
+ } else {
+ // Release the lock for the candidate.
+ candidate_worker->thread_mutex.unlock();
+ }
+ }
+
+ if (transfer_worker != nullptr) {
+ // A worker was picked, remove it from the available list.
+ transfer_worker_pool_available_list.remove_at(available_list_index);
+ } else {
+ DEV_ASSERT(!transfer_worker_pool_full && "A transfer worker should never be created when the pool is full.");
+
+ // No existing worker was picked, we create a new one.
+ transfer_worker = memnew(TransferWorker);
+ transfer_worker->command_fence = driver->fence_create();
+ transfer_worker->command_pool = driver->command_pool_create(transfer_queue_family, RDD::COMMAND_BUFFER_TYPE_PRIMARY);
+ transfer_worker->command_buffer = driver->command_buffer_create(transfer_worker->command_pool);
+ transfer_worker->index = transfer_worker_pool.size();
+ transfer_worker_pool.push_back(transfer_worker);
+ transfer_worker_operation_used_by_draw.push_back(0);
+ transfer_worker->thread_mutex.lock();
+ }
+ }
+
+ if (transfer_worker->submitted) {
+ // Wait for the worker if the command buffer was submitted but it hasn't finished processing yet.
+ _wait_for_transfer_worker(transfer_worker);
+ }
+
+ uint32_t alignment_offset = _get_alignment_offset(transfer_worker->staging_buffer_size_in_use, p_required_align);
+ transfer_worker->max_transfer_size = MAX(transfer_worker->max_transfer_size, p_transfer_size);
+
+ uint32_t required_size = transfer_worker->staging_buffer_size_in_use + p_transfer_size + alignment_offset;
+ if (required_size > transfer_worker->staging_buffer_size_allocated) {
+ // If there's not enough bytes to use on the staging buffer, we submit everything pending from the worker and wait for the work to be finished.
+ if (transfer_worker->recording) {
+ _end_transfer_worker(transfer_worker);
+ _submit_transfer_worker(transfer_worker);
+ }
+
+ if (transfer_worker->submitted) {
+ _wait_for_transfer_worker(transfer_worker);
+ }
+
+ alignment_offset = 0;
+
+ // If the staging buffer can't fit the transfer, we recreate the buffer.
+ const uint32_t expected_buffer_size_minimum = 16 * 1024;
+ uint32_t expected_buffer_size = MAX(transfer_worker->max_transfer_size, expected_buffer_size_minimum);
+ if (expected_buffer_size > transfer_worker->staging_buffer_size_allocated) {
+ if (transfer_worker->staging_buffer.id != 0) {
+ driver->buffer_free(transfer_worker->staging_buffer);
+ }
+
+ uint32_t new_staging_buffer_size = next_power_of_2(expected_buffer_size);
+ transfer_worker->staging_buffer_size_allocated = new_staging_buffer_size;
+ transfer_worker->staging_buffer = driver->buffer_create(new_staging_buffer_size, RDD::BUFFER_USAGE_TRANSFER_FROM_BIT, RDD::MEMORY_ALLOCATION_TYPE_CPU);
+ }
+ }
+
+ // Add the alignment before storing the offset that will be returned.
+ transfer_worker->staging_buffer_size_in_use += alignment_offset;
+
+ // Store the offset to return and increment the current size.
+ r_staging_offset = transfer_worker->staging_buffer_size_in_use;
+ transfer_worker->staging_buffer_size_in_use += p_transfer_size;
+
+ if (!transfer_worker->recording) {
+ // Begin the command buffer if the worker wasn't recording yet.
+ driver->command_buffer_begin(transfer_worker->command_buffer);
+ transfer_worker->recording = true;
+ }
+
+ return transfer_worker;
+}
+
+void RenderingDevice::_release_transfer_worker(TransferWorker *p_transfer_worker) {
+ p_transfer_worker->thread_mutex.unlock();
+
+ transfer_worker_pool_mutex.lock();
+ transfer_worker_pool_available_list.push_back(p_transfer_worker->index);
+ transfer_worker_pool_mutex.unlock();
+ transfer_worker_pool_condition.notify_one();
+}
+
+void RenderingDevice::_end_transfer_worker(TransferWorker *p_transfer_worker) {
+ driver->command_buffer_end(p_transfer_worker->command_buffer);
+ p_transfer_worker->recording = false;
+}
+
+void RenderingDevice::_submit_transfer_worker(TransferWorker *p_transfer_worker, VectorView<RDD::SemaphoreID> p_signal_semaphores) {
+ driver->command_queue_execute_and_present(transfer_queue, {}, p_transfer_worker->command_buffer, p_signal_semaphores, p_transfer_worker->command_fence, {});
+
+ for (uint32_t i = 0; i < p_signal_semaphores.size(); i++) {
+ // Indicate the frame should wait on these semaphores before executing the main command buffer.
+ frames[frame].semaphores_to_wait_on.push_back(p_signal_semaphores[i]);
+ }
+
+ p_transfer_worker->submitted = true;
+
+ {
+ MutexLock lock(p_transfer_worker->operations_mutex);
+ p_transfer_worker->operations_submitted = p_transfer_worker->operations_counter;
+ }
+}
+
+void RenderingDevice::_wait_for_transfer_worker(TransferWorker *p_transfer_worker) {
+ driver->fence_wait(p_transfer_worker->command_fence);
+ p_transfer_worker->staging_buffer_size_in_use = 0;
+ p_transfer_worker->submitted = false;
+
+ {
+ MutexLock lock(p_transfer_worker->operations_mutex);
+ p_transfer_worker->operations_processed = p_transfer_worker->operations_submitted;
+ }
+
+ if (!p_transfer_worker->texture_barriers.is_empty()) {
+ MutexLock transfer_worker_lock(transfer_worker_pool_mutex);
+ _flush_barriers_for_transfer_worker(p_transfer_worker);
+ }
+}
+
+void RenderingDevice::_flush_barriers_for_transfer_worker(TransferWorker *p_transfer_worker) {
+ if (!p_transfer_worker->texture_barriers.is_empty()) {
+ for (uint32_t i = 0; i < p_transfer_worker->texture_barriers.size(); i++) {
+ transfer_worker_pool_texture_barriers.push_back(p_transfer_worker->texture_barriers[i]);
+ }
+
+ p_transfer_worker->texture_barriers.clear();
+ }
+}
+
+void RenderingDevice::_check_transfer_worker_operation(uint32_t p_transfer_worker_index, uint64_t p_transfer_worker_operation) {
+ TransferWorker *transfer_worker = transfer_worker_pool[p_transfer_worker_index];
+ MutexLock lock(transfer_worker->operations_mutex);
+ uint64_t &dst_operation = transfer_worker_operation_used_by_draw[transfer_worker->index];
+ dst_operation = MAX(dst_operation, p_transfer_worker_operation);
+}
+
+void RenderingDevice::_check_transfer_worker_buffer(Buffer *p_buffer) {
+ if (p_buffer->transfer_worker_index >= 0) {
+ _check_transfer_worker_operation(p_buffer->transfer_worker_index, p_buffer->transfer_worker_operation);
+ p_buffer->transfer_worker_index = -1;
+ }
+}
+
+void RenderingDevice::_check_transfer_worker_texture(Texture *p_texture) {
+ if (p_texture->transfer_worker_index >= 0) {
+ _check_transfer_worker_operation(p_texture->transfer_worker_index, p_texture->transfer_worker_operation);
+ p_texture->transfer_worker_index = -1;
+ }
+}
+
+void RenderingDevice::_check_transfer_worker_vertex_array(VertexArray *p_vertex_array) {
+ if (!p_vertex_array->transfer_worker_indices.is_empty()) {
+ for (int i = 0; i < p_vertex_array->transfer_worker_indices.size(); i++) {
+ _check_transfer_worker_operation(p_vertex_array->transfer_worker_indices[i], p_vertex_array->transfer_worker_operations[i]);
+ }
+
+ p_vertex_array->transfer_worker_indices.clear();
+ p_vertex_array->transfer_worker_operations.clear();
+ }
+}
+
+void RenderingDevice::_check_transfer_worker_index_array(IndexArray *p_index_array) {
+ if (p_index_array->transfer_worker_index >= 0) {
+ _check_transfer_worker_operation(p_index_array->transfer_worker_index, p_index_array->transfer_worker_operation);
+ p_index_array->transfer_worker_index = -1;
+ }
+}
+
+void RenderingDevice::_submit_transfer_workers(RDD::CommandBufferID p_draw_command_buffer) {
+ MutexLock transfer_worker_lock(transfer_worker_pool_mutex);
+ for (uint32_t i = 0; i < transfer_worker_pool.size(); i++) {
+ TransferWorker *worker = transfer_worker_pool[i];
+ if (p_draw_command_buffer) {
+ MutexLock lock(worker->operations_mutex);
+ if (worker->operations_processed >= transfer_worker_operation_used_by_draw[worker->index]) {
+ // The operation used by the draw has already been processed, we don't need to wait on the worker.
+ continue;
+ }
+ }
+
+ {
+ MutexLock lock(worker->thread_mutex);
+ if (worker->recording) {
+ VectorView<RDD::SemaphoreID> semaphores = p_draw_command_buffer ? frames[frame].transfer_worker_semaphores[i] : VectorView<RDD::SemaphoreID>();
+ _end_transfer_worker(worker);
+ _submit_transfer_worker(worker, semaphores);
+ }
+
+ if (p_draw_command_buffer) {
+ _flush_barriers_for_transfer_worker(worker);
+ }
+ }
+ }
+
+ if (p_draw_command_buffer && !transfer_worker_pool_texture_barriers.is_empty()) {
+ driver->command_pipeline_barrier(p_draw_command_buffer, RDD::PIPELINE_STAGE_COPY_BIT, RDD::PIPELINE_STAGE_ALL_COMMANDS_BIT, {}, {}, transfer_worker_pool_texture_barriers);
+ transfer_worker_pool_texture_barriers.clear();
+ }
+}
+
+void RenderingDevice::_wait_for_transfer_workers() {
+ MutexLock transfer_worker_lock(transfer_worker_pool_mutex);
+ for (TransferWorker *worker : transfer_worker_pool) {
+ MutexLock lock(worker->thread_mutex);
+ if (worker->submitted) {
+ _wait_for_transfer_worker(worker);
+ }
+ }
+}
+
+void RenderingDevice::_free_transfer_workers() {
+ MutexLock transfer_worker_lock(transfer_worker_pool_mutex);
+ for (TransferWorker *worker : transfer_worker_pool) {
+ driver->fence_free(worker->command_fence);
+ driver->buffer_free(worker->staging_buffer);
+ driver->command_pool_free(worker->command_pool);
+ memdelete(worker);
+ }
+
+ transfer_worker_pool.clear();
+}
+
/***********************/
/**** COMMAND GRAPH ****/
/***********************/
@@ -4851,7 +5406,7 @@ bool RenderingDevice::_dependency_make_mutable(RID p_id, RID p_resource_id, RDG:
}
}
-bool RenderingDevice::_dependencies_make_mutable(RID p_id, RDG::ResourceTracker *p_resource_tracker) {
+bool RenderingDevice::_dependencies_make_mutable_recursive(RID p_id, RDG::ResourceTracker *p_resource_tracker) {
bool made_mutable = false;
HashMap<RID, HashSet<RID>>::Iterator E = dependency_map.find(p_id);
if (E) {
@@ -4863,12 +5418,17 @@ bool RenderingDevice::_dependencies_make_mutable(RID p_id, RDG::ResourceTracker
return made_mutable;
}
+bool RenderingDevice::_dependencies_make_mutable(RID p_id, RDG::ResourceTracker *p_resource_tracker) {
+ _THREAD_SAFE_METHOD_
+ return _dependencies_make_mutable_recursive(p_id, p_resource_tracker);
+}
+
/**************************/
/**** FRAME MANAGEMENT ****/
/**************************/
void RenderingDevice::free(RID p_id) {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD();
_free_dependencies(p_id); // Recursively erase dependencies first, to avoid potential API problems.
_free_internal(p_id);
@@ -4886,6 +5446,8 @@ void RenderingDevice::_free_internal(RID p_id) {
// Push everything so it's disposed of next time this frame index is processed (means, it's safe to do it).
if (texture_owner.owns(p_id)) {
Texture *texture = texture_owner.get_or_null(p_id);
+ _check_transfer_worker_texture(texture);
+
RDG::ResourceTracker *draw_tracker = texture->draw_tracker;
if (draw_tracker != nullptr) {
draw_tracker->reference_count--;
@@ -4919,6 +5481,8 @@ void RenderingDevice::_free_internal(RID p_id) {
sampler_owner.free(p_id);
} else if (vertex_buffer_owner.owns(p_id)) {
Buffer *vertex_buffer = vertex_buffer_owner.get_or_null(p_id);
+ _check_transfer_worker_buffer(vertex_buffer);
+
RDG::resource_tracker_free(vertex_buffer->draw_tracker);
frames[frame].buffers_to_dispose_of.push_back(*vertex_buffer);
vertex_buffer_owner.free(p_id);
@@ -4926,6 +5490,8 @@ void RenderingDevice::_free_internal(RID p_id) {
vertex_array_owner.free(p_id);
} else if (index_buffer_owner.owns(p_id)) {
IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_id);
+ _check_transfer_worker_buffer(index_buffer);
+
RDG::resource_tracker_free(index_buffer->draw_tracker);
frames[frame].buffers_to_dispose_of.push_back(*index_buffer);
index_buffer_owner.free(p_id);
@@ -4939,16 +5505,22 @@ void RenderingDevice::_free_internal(RID p_id) {
shader_owner.free(p_id);
} else if (uniform_buffer_owner.owns(p_id)) {
Buffer *uniform_buffer = uniform_buffer_owner.get_or_null(p_id);
+ _check_transfer_worker_buffer(uniform_buffer);
+
RDG::resource_tracker_free(uniform_buffer->draw_tracker);
frames[frame].buffers_to_dispose_of.push_back(*uniform_buffer);
uniform_buffer_owner.free(p_id);
} else if (texture_buffer_owner.owns(p_id)) {
Buffer *texture_buffer = texture_buffer_owner.get_or_null(p_id);
+ _check_transfer_worker_buffer(texture_buffer);
+
RDG::resource_tracker_free(texture_buffer->draw_tracker);
frames[frame].buffers_to_dispose_of.push_back(*texture_buffer);
texture_buffer_owner.free(p_id);
} else if (storage_buffer_owner.owns(p_id)) {
Buffer *storage_buffer = storage_buffer_owner.get_or_null(p_id);
+ _check_transfer_worker_buffer(storage_buffer);
+
RDG::resource_tracker_free(storage_buffer->draw_tracker);
frames[frame].buffers_to_dispose_of.push_back(*storage_buffer);
storage_buffer_owner.free(p_id);
@@ -4980,6 +5552,8 @@ void RenderingDevice::_free_internal(RID p_id) {
// The full list of resources that can be named is in the VkObjectType enum.
// We just expose the resources that are owned and can be accessed easily.
void RenderingDevice::set_resource_name(RID p_id, const String &p_name) {
+ _THREAD_SAFE_METHOD_
+
if (texture_owner.owns(p_id)) {
Texture *texture = texture_owner.get_or_null(p_id);
driver->set_object_name(RDD::OBJECT_TYPE_TEXTURE, texture->driver_id, p_name);
@@ -5026,6 +5600,8 @@ void RenderingDevice::set_resource_name(RID p_id, const String &p_name) {
}
void RenderingDevice::draw_command_begin_label(String p_label_name, const Color &p_color) {
+ ERR_RENDER_THREAD_GUARD();
+
if (!context->is_debug_utils_enabled()) {
return;
}
@@ -5040,6 +5616,8 @@ void RenderingDevice::draw_command_insert_label(String p_label_name, const Color
#endif
void RenderingDevice::draw_command_end_label() {
+ ERR_RENDER_THREAD_GUARD();
+
draw_graph.end_label();
}
@@ -5072,7 +5650,7 @@ String RenderingDevice::get_device_pipeline_cache_uuid() const {
}
void RenderingDevice::swap_buffers() {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD();
_end_frame();
_execute_frame(true);
@@ -5083,18 +5661,20 @@ void RenderingDevice::swap_buffers() {
}
void RenderingDevice::submit() {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD();
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_RENDER_THREAD_GUARD();
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;
}
@@ -5207,17 +5787,16 @@ uint64_t RenderingDevice::get_memory_usage(MemoryType p_type) const {
void RenderingDevice::_begin_frame() {
// Before beginning this frame, wait on the fence if it was signaled to make sure its work is finished.
- if (frames[frame].draw_fence_signaled) {
- driver->fence_wait(frames[frame].draw_fence);
- frames[frame].draw_fence_signaled = false;
+ if (frames[frame].fence_signaled) {
+ driver->fence_wait(frames[frame].fence);
+ frames[frame].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);
- driver->command_buffer_begin(frames[frame].draw_command_buffer);
+ driver->command_buffer_begin(frames[frame].command_buffer);
// Reset the graph.
draw_graph.begin();
@@ -5233,7 +5812,7 @@ void RenderingDevice::_begin_frame() {
if (frames[frame].timestamp_count) {
driver->timestamp_query_pool_get_results(frames[frame].timestamp_pool, frames[frame].timestamp_count, frames[frame].timestamp_result_values.ptr());
- driver->command_timestamp_query_pool_reset(frames[frame].setup_command_buffer, frames[frame].timestamp_pool, frames[frame].timestamp_count);
+ driver->command_timestamp_query_pool_reset(frames[frame].command_buffer, frames[frame].timestamp_pool, frames[frame].timestamp_count);
SWAP(frames[frame].timestamp_names, frames[frame].timestamp_result_names);
SWAP(frames[frame].timestamp_cpu_values, frames[frame].timestamp_cpu_result_values);
}
@@ -5252,10 +5831,10 @@ void RenderingDevice::_end_frame() {
ERR_PRINT("Found open compute list at the end of the frame, this should never happen (further compute will likely not work).");
}
- driver->command_buffer_end(frames[frame].setup_command_buffer);
-
// The command buffer must be copied into a stack variable as the driver workarounds can change the command buffer in use.
- RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer;
+ RDD::CommandBufferID command_buffer = frames[frame].command_buffer;
+ _submit_transfer_workers(command_buffer);
+
draw_graph.end(RENDER_GRAPH_REORDER, RENDER_GRAPH_FULL_BARRIERS, command_buffer, frames[frame].command_buffer_pool);
driver->command_buffer_end(command_buffer);
driver->end_segment();
@@ -5268,9 +5847,6 @@ void RenderingDevice::_execute_frame(bool p_present) {
thread_local LocalVector<RDD::SwapChainID> swap_chains;
swap_chains.clear();
- // Execute the setup command buffer.
- driver->command_queue_execute_and_present(main_queue, {}, frames[frame].setup_command_buffer, frames[frame].setup_semaphore, {}, {});
-
// Execute command buffers and use semaphores to wait on the execution of the previous one. Normally there's only one command buffer,
// but driver workarounds can force situations where there'll be more.
uint32_t command_buffer_count = 1;
@@ -5280,7 +5856,9 @@ void RenderingDevice::_execute_frame(bool p_present) {
buffer_pool.buffers_used = 0;
}
- RDD::SemaphoreID wait_semaphore = frames[frame].setup_semaphore;
+ thread_local LocalVector<RDD::SemaphoreID> wait_semaphores;
+ wait_semaphores = frames[frame].semaphores_to_wait_on;
+
for (uint32_t i = 0; i < command_buffer_count; i++) {
RDD::CommandBufferID command_buffer;
RDD::SemaphoreID signal_semaphore;
@@ -5289,14 +5867,14 @@ void RenderingDevice::_execute_frame(bool p_present) {
command_buffer = buffer_pool.buffers[i - 1];
signal_semaphore = buffer_pool.semaphores[i - 1];
} else {
- command_buffer = frames[frame].draw_command_buffer;
- signal_semaphore = frames[frame].draw_semaphore;
+ command_buffer = frames[frame].command_buffer;
+ signal_semaphore = frames[frame].semaphore;
}
bool signal_semaphore_valid;
if (i == (command_buffer_count - 1)) {
// This is the last command buffer, it should signal the fence.
- signal_fence = frames[frame].draw_fence;
+ signal_fence = frames[frame].fence;
signal_semaphore_valid = false;
if (frame_can_present && separate_present_queue) {
@@ -5311,19 +5889,21 @@ void RenderingDevice::_execute_frame(bool p_present) {
signal_semaphore_valid = true;
}
- driver->command_queue_execute_and_present(main_queue, wait_semaphore, command_buffer, signal_semaphore_valid ? signal_semaphore : VectorView<RDD::SemaphoreID>(), signal_fence, swap_chains);
+ driver->command_queue_execute_and_present(main_queue, wait_semaphores, command_buffer, signal_semaphore_valid ? signal_semaphore : VectorView<RDD::SemaphoreID>(), signal_fence, swap_chains);
// Make the next command buffer wait on the semaphore signaled by this one.
- wait_semaphore = signal_semaphore;
+ wait_semaphores.resize(1);
+ wait_semaphores[0] = signal_semaphore;
}
// Indicate the fence has been signaled so the next time the frame's contents need to be used, the CPU needs to wait on the work to be completed.
- frames[frame].draw_fence_signaled = true;
+ frames[frame].semaphores_to_wait_on.clear();
+ frames[frame].fence_signaled = true;
if (frame_can_present) {
if (separate_present_queue) {
// Issue the presentation separately if the presentation queue is different from the main queue.
- driver->command_queue_execute_and_present(present_queue, wait_semaphore, {}, {}, {}, frames[frame].swap_chains_to_present);
+ driver->command_queue_execute_and_present(present_queue, wait_semaphores, {}, {}, {}, frames[frame].swap_chains_to_present);
}
frames[frame].swap_chains_to_present.clear();
@@ -5332,9 +5912,9 @@ void RenderingDevice::_execute_frame(bool p_present) {
void RenderingDevice::_stall_for_previous_frames() {
for (uint32_t i = 0; i < frames.size(); i++) {
- if (frames[i].draw_fence_signaled) {
- driver->fence_wait(frames[i].draw_fence);
- frames[i].draw_fence_signaled = false;
+ if (frames[i].fence_signaled) {
+ driver->fence_wait(frames[i].fence);
+ frames[i].fence_signaled = false;
}
}
}
@@ -5347,8 +5927,9 @@ void RenderingDevice::_flush_and_stall_for_all_frames() {
}
Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServer::WindowID p_main_window) {
- Error err;
+ ERR_RENDER_THREAD_GUARD_V(ERR_UNAVAILABLE);
+ Error err;
RenderingContextDriver::SurfaceID main_surface = 0;
is_main_instance = (singleton == this) && (p_main_window != DisplayServer::INVALID_WINDOW_ID);
if (p_main_window != DisplayServer::INVALID_WINDOW_ID) {
@@ -5436,14 +6017,30 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ
main_queue = driver->command_queue_create(main_queue_family, true);
ERR_FAIL_COND_V(!main_queue, FAILED);
+ transfer_queue_family = driver->command_queue_family_get(RDD::COMMAND_QUEUE_FAMILY_TRANSFER_BIT);
+ if (transfer_queue_family) {
+ // Create the transfer queue.
+ transfer_queue = driver->command_queue_create(transfer_queue_family);
+ ERR_FAIL_COND_V(!transfer_queue, FAILED);
+ } else {
+ // Use main queue as the transfer queue.
+ transfer_queue = main_queue;
+ transfer_queue_family = main_queue_family;
+ }
+
if (present_queue_family) {
- // Create the presentation queue.
+ // Create the present queue.
present_queue = driver->command_queue_create(present_queue_family);
ERR_FAIL_COND_V(!present_queue, FAILED);
} else {
+ // Use main queue as the present queue.
present_queue = main_queue;
+ present_queue_family = main_queue_family;
}
+ // Use the processor count as the max amount of transfer workers that can be created.
+ transfer_worker_pool_max_size = OS::get_singleton()->get_processor_count();
+
// Create data for all the frames.
for (uint32_t i = 0; i < frames.size(); i++) {
frames[i].index = 0;
@@ -5451,17 +6048,13 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ
// Create command pool, command buffers, semaphores and fences.
frames[i].command_pool = driver->command_pool_create(main_queue_family, RDD::COMMAND_BUFFER_TYPE_PRIMARY);
ERR_FAIL_COND_V(!frames[i].command_pool, FAILED);
- frames[i].setup_command_buffer = driver->command_buffer_create(frames[i].command_pool);
- ERR_FAIL_COND_V(!frames[i].setup_command_buffer, FAILED);
- frames[i].draw_command_buffer = driver->command_buffer_create(frames[i].command_pool);
- ERR_FAIL_COND_V(!frames[i].draw_command_buffer, FAILED);
- frames[i].setup_semaphore = driver->semaphore_create();
- ERR_FAIL_COND_V(!frames[i].setup_semaphore, FAILED);
- frames[i].draw_semaphore = driver->semaphore_create();
- ERR_FAIL_COND_V(!frames[i].draw_semaphore, FAILED);
- frames[i].draw_fence = driver->fence_create();
- ERR_FAIL_COND_V(!frames[i].draw_fence, FAILED);
- frames[i].draw_fence_signaled = false;
+ frames[i].command_buffer = driver->command_buffer_create(frames[i].command_pool);
+ ERR_FAIL_COND_V(!frames[i].command_buffer, FAILED);
+ frames[i].semaphore = driver->semaphore_create();
+ ERR_FAIL_COND_V(!frames[i].semaphore, FAILED);
+ frames[i].fence = driver->fence_create();
+ ERR_FAIL_COND_V(!frames[i].fence, FAILED);
+ frames[i].fence_signaled = false;
// Create query pool.
frames[i].timestamp_pool = driver->timestamp_query_pool_create(max_timestamp_query_elements);
@@ -5475,6 +6068,13 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ
// Assign the main queue family and command pool to the command buffer pool.
frames[i].command_buffer_pool.pool = frames[i].command_pool;
+
+ // Create the semaphores for the transfer workers.
+ frames[i].transfer_worker_semaphores.resize(transfer_worker_pool_max_size);
+ for (uint32_t j = 0; j < transfer_worker_pool_max_size; j++) {
+ frames[i].transfer_worker_semaphores[j] = driver->semaphore_create();
+ ERR_FAIL_COND_V(!frames[i].transfer_worker_semaphores[j], FAILED);
+ }
}
// Start from frame count, so everything else is immediately old.
@@ -5482,8 +6082,7 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ
// Initialize recording on the first frame.
driver->begin_segment(frame, frames_drawn++);
- driver->command_buffer_begin(frames[0].setup_command_buffer);
- driver->command_buffer_begin(frames[0].draw_command_buffer);
+ driver->command_buffer_begin(frames[0].command_buffer);
// Create draw graph and start it initialized as well.
draw_graph.initialize(driver, device, frames.size(), main_queue_family, SECONDARY_COMMAND_BUFFERS_PER_FRAME);
@@ -5491,7 +6090,7 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ
for (uint32_t i = 0; i < frames.size(); i++) {
// Reset all queries in a query pool before doing any operations with them..
- driver->command_timestamp_query_pool_reset(frames[0].setup_command_buffer, frames[i].timestamp_pool, max_timestamp_query_elements);
+ driver->command_timestamp_query_pool_reset(frames[0].command_buffer, frames[i].timestamp_pool, max_timestamp_query_elements);
}
// Convert block size from KB.
@@ -5560,6 +6159,8 @@ Vector<uint8_t> RenderingDevice::_load_pipeline_cache() {
}
void RenderingDevice::_update_pipeline_cache(bool p_closing) {
+ _THREAD_SAFE_METHOD_
+
{
bool still_saving = pipeline_cache_save_task != WorkerThreadPool::INVALID_TASK_ID && !WorkerThreadPool::get_singleton()->is_task_completed(pipeline_cache_save_task);
if (still_saving) {
@@ -5641,6 +6242,8 @@ void RenderingDevice::_free_rids(T &p_owner, const char *p_type) {
}
void RenderingDevice::capture_timestamp(const String &p_name) {
+ ERR_RENDER_THREAD_GUARD();
+
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_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));
@@ -5653,7 +6256,7 @@ void RenderingDevice::capture_timestamp(const String &p_name) {
}
uint64_t RenderingDevice::get_driver_resource(DriverResource p_resource, RID p_rid, uint64_t p_index) {
- _THREAD_SAFE_METHOD_
+ ERR_RENDER_THREAD_GUARD_V(0);
uint64_t driver_id = 0;
switch (p_resource) {
@@ -5769,19 +6372,23 @@ uint64_t RenderingDevice::get_device_allocs_by_object_type(uint32_t type) const
}
uint32_t RenderingDevice::get_captured_timestamps_count() const {
+ ERR_RENDER_THREAD_GUARD_V(0);
return frames[frame].timestamp_result_count;
}
uint64_t RenderingDevice::get_captured_timestamps_frame() const {
+ ERR_RENDER_THREAD_GUARD_V(0);
return frames[frame].index;
}
uint64_t RenderingDevice::get_captured_timestamp_gpu_time(uint32_t p_index) const {
+ ERR_RENDER_THREAD_GUARD_V(0);
ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0);
return driver->timestamp_query_result_to_time(frames[frame].timestamp_result_values[p_index]);
}
uint64_t RenderingDevice::get_captured_timestamp_cpu_time(uint32_t p_index) const {
+ ERR_RENDER_THREAD_GUARD_V(0);
ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0);
return frames[frame].timestamp_cpu_result_values[p_index];
}
@@ -5796,11 +6403,17 @@ uint64_t RenderingDevice::limit_get(Limit p_limit) const {
}
void RenderingDevice::finalize() {
+ ERR_RENDER_THREAD_GUARD();
+
if (!frames.is_empty()) {
// Wait for all frames to have finished rendering.
_flush_and_stall_for_all_frames();
}
+ // Wait for transfer workers to finish.
+ _submit_transfer_workers();
+ _wait_for_transfer_workers();
+
// Delete everything the graph has created.
draw_graph.finalize();
@@ -5854,20 +6467,26 @@ void RenderingDevice::finalize() {
}
}
+ // Erase the transfer workers after all resources have been freed.
+ _free_transfer_workers();
+
// Free everything pending.
for (uint32_t i = 0; i < frames.size(); i++) {
int f = (frame + i) % frames.size();
_free_pending_resources(f);
driver->command_pool_free(frames[i].command_pool);
driver->timestamp_query_pool_free(frames[i].timestamp_pool);
- driver->semaphore_free(frames[i].setup_semaphore);
- driver->semaphore_free(frames[i].draw_semaphore);
- driver->fence_free(frames[i].draw_fence);
+ driver->semaphore_free(frames[i].semaphore);
+ driver->fence_free(frames[i].fence);
RDG::CommandBufferPool &buffer_pool = frames[i].command_buffer_pool;
for (uint32_t j = 0; j < buffer_pool.buffers.size(); j++) {
driver->semaphore_free(buffer_pool.semaphores[j]);
}
+
+ for (uint32_t j = 0; j < frames[i].transfer_worker_semaphores.size(); j++) {
+ driver->semaphore_free(frames[i].transfer_worker_semaphores[j]);
+ }
}
if (pipeline_cache_enabled) {
@@ -5909,6 +6528,15 @@ void RenderingDevice::finalize() {
present_queue = RDD::CommandQueueID();
}
+ if (transfer_queue) {
+ if (main_queue != transfer_queue) {
+ // Only delete the transfer queue if it's unique.
+ driver->command_queue_free(transfer_queue);
+ }
+
+ transfer_queue = RDD::CommandQueueID();
+ }
+
if (main_queue) {
driver->command_queue_free(main_queue);
main_queue = RDD::CommandQueueID();
@@ -6644,6 +7272,8 @@ RenderingDevice::RenderingDevice() {
if (singleton == nullptr) {
singleton = this;
}
+
+ render_thread_id = Thread::get_caller_id();
}
/*****************/
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index d8bf845756..71ffbfbd88 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -33,6 +33,7 @@
#include "core/object/class_db.h"
#include "core/object/worker_thread_pool.h"
+#include "core/os/condition_variable.h"
#include "core/os/thread_safe.h"
#include "core/templates/local_vector.h"
#include "core/templates/oa_hash_map.h"
@@ -62,6 +63,10 @@ class RenderingDevice : public RenderingDeviceCommons {
GDCLASS(RenderingDevice, Object)
_THREAD_SAFE_CLASS_
+
+private:
+ Thread::ID render_thread_id;
+
public:
enum ShaderLanguage {
SHADER_LANGUAGE_GLSL,
@@ -178,10 +183,12 @@ private:
uint32_t size = 0;
BitField<RDD::BufferUsageBits> usage;
RDG::ResourceTracker *draw_tracker = nullptr;
+ int32_t transfer_worker_index = -1;
+ uint64_t transfer_worker_operation = 0;
};
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);
+ Error _buffer_initialize(Buffer *p_buffer, const uint8_t *p_data, size_t p_data_size, uint32_t p_required_align = 32);
void update_perf_report();
@@ -189,9 +196,9 @@ private:
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;
+ RID_Owner<Buffer, true> uniform_buffer_owner;
+ RID_Owner<Buffer, true> storage_buffer_owner;
+ RID_Owner<Buffer, true> texture_buffer_owner;
public:
Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size);
@@ -254,6 +261,8 @@ public:
RDG::ResourceTracker *draw_tracker = nullptr;
HashMap<Rect2i, RDG::ResourceTracker *> slice_trackers;
SharedFallback *shared_fallback = nullptr;
+ int32_t transfer_worker_index = -1;
+ uint64_t transfer_worker_operation = 0;
RDD::TextureSubresourceRange barrier_range() const {
RDD::TextureSubresourceRange r;
@@ -282,11 +291,13 @@ public:
}
};
- RID_Owner<Texture> texture_owner;
+ RID_Owner<Texture, true> texture_owner;
uint32_t texture_upload_region_size_px = 0;
Vector<uint8_t> _texture_get_data(Texture *tex, uint32_t p_layer, bool p_2d = false);
- Error _texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, bool p_use_setup_queue, bool p_validate_can_update);
+ uint32_t _texture_layer_count(Texture *p_texture) const;
+ uint32_t _texture_alignment(Texture *p_texture) const;
+ Error _texture_initialize(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data);
void _texture_check_shared_fallback(Texture *p_texture);
void _texture_update_shared_fallback(RID p_texture_rid, Texture *p_texture, bool p_for_writing);
void _texture_free_shared_fallback(Texture *p_texture);
@@ -572,7 +583,7 @@ private:
uint32_t view_count;
};
- RID_Owner<Framebuffer> framebuffer_owner;
+ RID_Owner<Framebuffer, true> framebuffer_owner;
public:
// This ID is warranted to be unique for the same formats, does not need to be freed
@@ -593,7 +604,7 @@ public:
/**** SAMPLER ****/
/*****************/
private:
- RID_Owner<RDD::SamplerID> sampler_owner;
+ RID_Owner<RDD::SamplerID, true> sampler_owner;
public:
RID sampler_create(const SamplerState &p_state);
@@ -615,7 +626,7 @@ private:
// This mapping is done here internally, and it's not
// exposed.
- RID_Owner<Buffer> vertex_buffer_owner;
+ RID_Owner<Buffer, true> vertex_buffer_owner;
struct VertexDescriptionKey {
Vector<VertexAttribute> vertex_formats;
@@ -695,10 +706,12 @@ private:
Vector<RDD::BufferID> buffers; // Not owned, just referenced.
Vector<RDG::ResourceTracker *> draw_trackers; // Not owned, just referenced.
Vector<uint64_t> offsets;
+ Vector<int32_t> transfer_worker_indices;
+ Vector<uint64_t> transfer_worker_operations;
HashSet<RID> untracked_buffers;
};
- RID_Owner<VertexArray> vertex_array_owner;
+ RID_Owner<VertexArray, true> vertex_array_owner;
struct IndexBuffer : public Buffer {
uint32_t max_index = 0; // Used for validation.
@@ -707,7 +720,7 @@ private:
bool supports_restart_indices = false;
};
- RID_Owner<IndexBuffer> index_buffer_owner;
+ RID_Owner<IndexBuffer, true> index_buffer_owner;
struct IndexArray {
uint32_t max_index = 0; // Remember the maximum index here too, for validation.
@@ -717,9 +730,11 @@ private:
uint32_t indices = 0;
IndexBufferFormat format = INDEX_BUFFER_FORMAT_UINT16;
bool supports_restart_indices = false;
+ int32_t transfer_worker_index = -1;
+ uint64_t transfer_worker_operation = 0;
};
- RID_Owner<IndexArray> index_array_owner;
+ RID_Owner<IndexArray, true> index_array_owner;
public:
RID vertex_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>(), bool p_use_as_storage = false);
@@ -796,7 +811,7 @@ private:
String _shader_uniform_debug(RID p_shader, int p_set = -1);
- RID_Owner<Shader> shader_owner;
+ RID_Owner<Shader, true> shader_owner;
#ifndef DISABLE_DEPRECATED
public:
@@ -977,7 +992,7 @@ private:
void *invalidated_callback_userdata = nullptr;
};
- RID_Owner<UniformSet> uniform_set_owner;
+ RID_Owner<UniformSet, true> uniform_set_owner;
void _uniform_set_update_shared(UniformSet *p_uniform_set);
@@ -1024,7 +1039,7 @@ private:
uint32_t push_constant_size = 0;
};
- RID_Owner<RenderPipeline> render_pipeline_owner;
+ RID_Owner<RenderPipeline, true> render_pipeline_owner;
bool pipeline_cache_enabled = false;
size_t pipeline_cache_size = 0;
@@ -1045,7 +1060,7 @@ private:
uint32_t local_group_size[3] = { 0, 0, 0 };
};
- RID_Owner<ComputePipeline> compute_pipeline_owner;
+ RID_Owner<ComputePipeline, true> compute_pipeline_owner;
public:
RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, BitField<PipelineDynamicStateFlags> p_dynamic_state_flags = 0, uint32_t p_for_render_pass = 0, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>());
@@ -1101,6 +1116,7 @@ private:
RID pipeline_shader;
RDD::ShaderID pipeline_shader_driver_id;
uint32_t pipeline_shader_layout_hash = 0;
+ uint32_t pipeline_push_constant_size = 0;
RID vertex_array;
RID index_array;
uint32_t draw_count = 0;
@@ -1153,8 +1169,6 @@ 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, 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);
Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_subpass);
void _draw_list_free(Rect2i *r_last_viewport = nullptr);
@@ -1240,6 +1254,52 @@ public:
void compute_list_end();
private:
+ /*************************/
+ /**** TRANSFER WORKER ****/
+ /*************************/
+
+ struct TransferWorker {
+ uint32_t index = 0;
+ RDD::BufferID staging_buffer;
+ uint32_t max_transfer_size = 0;
+ uint32_t staging_buffer_size_in_use = 0;
+ uint32_t staging_buffer_size_allocated = 0;
+ RDD::CommandBufferID command_buffer;
+ RDD::CommandPoolID command_pool;
+ RDD::FenceID command_fence;
+ LocalVector<RDD::TextureBarrier> texture_barriers;
+ bool recording = false;
+ bool submitted = false;
+ BinaryMutex thread_mutex;
+ uint64_t operations_processed = 0;
+ uint64_t operations_submitted = 0;
+ uint64_t operations_counter = 0;
+ BinaryMutex operations_mutex;
+ };
+
+ LocalVector<TransferWorker *> transfer_worker_pool;
+ uint32_t transfer_worker_pool_max_size = 1;
+ LocalVector<uint64_t> transfer_worker_operation_used_by_draw;
+ LocalVector<uint32_t> transfer_worker_pool_available_list;
+ LocalVector<RDD::TextureBarrier> transfer_worker_pool_texture_barriers;
+ BinaryMutex transfer_worker_pool_mutex;
+ ConditionVariable transfer_worker_pool_condition;
+
+ TransferWorker *_acquire_transfer_worker(uint32_t p_transfer_size, uint32_t p_required_align, uint32_t &r_staging_offset);
+ void _release_transfer_worker(TransferWorker *p_transfer_worker);
+ void _end_transfer_worker(TransferWorker *p_transfer_worker);
+ void _submit_transfer_worker(TransferWorker *p_transfer_worker, VectorView<RDD::SemaphoreID> p_signal_semaphores = VectorView<RDD::SemaphoreID>());
+ void _wait_for_transfer_worker(TransferWorker *p_transfer_worker);
+ void _flush_barriers_for_transfer_worker(TransferWorker *p_transfer_worker);
+ void _check_transfer_worker_operation(uint32_t p_transfer_worker_index, uint64_t p_transfer_worker_operation);
+ void _check_transfer_worker_buffer(Buffer *p_buffer);
+ void _check_transfer_worker_texture(Texture *p_texture);
+ void _check_transfer_worker_vertex_array(VertexArray *p_vertex_array);
+ void _check_transfer_worker_index_array(IndexArray *p_index_array);
+ void _submit_transfer_workers(RDD::CommandBufferID p_draw_command_buffer = RDD::CommandBufferID());
+ void _wait_for_transfer_workers();
+ void _free_transfer_workers();
+
/***********************/
/**** COMMAND GRAPH ****/
/***********************/
@@ -1250,6 +1310,7 @@ private:
bool _index_array_make_mutable(IndexArray *p_index_array, RDG::ResourceTracker *p_resource_tracker);
bool _uniform_set_make_mutable(UniformSet *p_uniform_set, RID p_resource_id, RDG::ResourceTracker *p_resource_tracker);
bool _dependency_make_mutable(RID p_id, RID p_resource_id, RDG::ResourceTracker *p_resource_tracker);
+ bool _dependencies_make_mutable_recursive(RID p_id, RDG::ResourceTracker *p_resource_tracker);
bool _dependencies_make_mutable(RID p_id, RDG::ResourceTracker *p_resource_tracker);
RenderingDeviceGraph draw_graph;
@@ -1259,8 +1320,10 @@ private:
/**************************/
RDD::CommandQueueFamilyID main_queue_family;
+ RDD::CommandQueueFamilyID transfer_queue_family;
RDD::CommandQueueFamilyID present_queue_family;
RDD::CommandQueueID main_queue;
+ RDD::CommandQueueID transfer_queue;
RDD::CommandQueueID present_queue;
/**************************/
@@ -1292,32 +1355,29 @@ private:
List<RenderPipeline> render_pipelines_to_dispose_of;
List<ComputePipeline> compute_pipelines_to_dispose_of;
+ // The command pool used by the command buffer.
RDD::CommandPoolID command_pool;
- // Used at the beginning of every frame for set-up.
- // Used for filling up newly created buffers with data provided on creation.
- // Primarily intended to be accessed by worker threads.
- // Ideally this command buffer should use an async transfer queue.
- RDD::CommandBufferID setup_command_buffer;
-
- // The main command buffer for drawing and compute.
- // Primarily intended to be used by the main thread to do most stuff.
- RDD::CommandBufferID draw_command_buffer;
+ // The command buffer used by the main thread when recording the frame.
+ RDD::CommandBufferID command_buffer;
- // Signaled by the setup submission. Draw must wait on this semaphore.
- RDD::SemaphoreID setup_semaphore;
+ // Signaled by the command buffer submission. Present must wait on this semaphore.
+ RDD::SemaphoreID semaphore;
- // Signaled by the draw submission. Present must wait on this semaphore.
- RDD::SemaphoreID draw_semaphore;
+ // Signaled by the command buffer submission. Must wait on this fence before beginning command recording for the frame.
+ RDD::FenceID fence;
+ bool fence_signaled = false;
- // Signaled by the draw submission. Must wait on this fence before beginning
- // command recording for the frame.
- RDD::FenceID draw_fence;
- bool draw_fence_signaled = false;
+ // Semaphores the frame must wait on before executing the command buffer.
+ LocalVector<RDD::SemaphoreID> semaphores_to_wait_on;
// Swap chains prepared for drawing during the frame that must be presented.
LocalVector<RDD::SwapChainID> swap_chains_to_present;
+ // Semaphores the transfer workers can use to wait before rendering the frame.
+ // This must have the same size of the transfer worker pool.
+ TightLocalVector<RDD::SemaphoreID> transfer_worker_semaphores;
+
// Extra command buffer pool used for driver workarounds.
RDG::CommandBufferPool command_buffer_pool;
diff --git a/servers/rendering/rendering_device_commons.cpp b/servers/rendering/rendering_device_commons.cpp
index 4dbd0e3964..03fad5493a 100644
--- a/servers/rendering/rendering_device_commons.cpp
+++ b/servers/rendering/rendering_device_commons.cpp
@@ -600,7 +600,7 @@ void RenderingDeviceCommons::get_compressed_image_format_block_dimensions(DataFo
}
}
-uint32_t RenderingDeviceCommons::get_compressed_image_format_block_byte_size(DataFormat p_format) {
+uint32_t RenderingDeviceCommons::get_compressed_image_format_block_byte_size(DataFormat p_format) const {
switch (p_format) {
case DATA_FORMAT_BC1_RGB_UNORM_BLOCK:
case DATA_FORMAT_BC1_RGB_SRGB_BLOCK:
diff --git a/servers/rendering/rendering_device_commons.h b/servers/rendering/rendering_device_commons.h
index d72265958c..d516d968af 100644
--- a/servers/rendering/rendering_device_commons.h
+++ b/servers/rendering/rendering_device_commons.h
@@ -893,7 +893,7 @@ protected:
static uint32_t get_image_format_pixel_size(DataFormat p_format);
static void get_compressed_image_format_block_dimensions(DataFormat p_format, uint32_t &r_w, uint32_t &r_h);
- uint32_t get_compressed_image_format_block_byte_size(DataFormat p_format);
+ uint32_t get_compressed_image_format_block_byte_size(DataFormat p_format) const;
static uint32_t get_compressed_image_format_pixel_rshift(DataFormat p_format);
static uint32_t get_image_format_required_size(DataFormat p_format, uint32_t p_width, uint32_t p_height, uint32_t p_depth, uint32_t p_mipmaps, uint32_t *r_blockw = nullptr, uint32_t *r_blockh = nullptr, uint32_t *r_depth = nullptr);
static uint32_t get_image_required_mipmaps(uint32_t p_width, uint32_t p_height, uint32_t p_depth);
diff --git a/servers/rendering/rendering_device_driver.cpp b/servers/rendering/rendering_device_driver.cpp
index 3b8e3efeb8..9ff7b83215 100644
--- a/servers/rendering/rendering_device_driver.cpp
+++ b/servers/rendering/rendering_device_driver.cpp
@@ -374,6 +374,8 @@ uint64_t RenderingDeviceDriver::api_trait_get(ApiTrait p_trait) {
return 1;
case API_TRAIT_CLEARS_WITH_COPY_ENGINE:
return true;
+ case API_TRAIT_USE_GENERAL_IN_COPY_QUEUES:
+ return false;
default:
ERR_FAIL_V(0);
}
diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h
index 97c84c9d05..637d52c060 100644
--- a/servers/rendering/rendering_device_driver.h
+++ b/servers/rendering/rendering_device_driver.h
@@ -104,14 +104,14 @@ struct VersatileResourceTemplate {
uint8_t data[MAX_RESOURCE_SIZE];
template <typename T>
- static T *allocate(PagedAllocator<VersatileResourceTemplate> &p_allocator) {
+ static T *allocate(PagedAllocator<VersatileResourceTemplate, true> &p_allocator) {
T *obj = (T *)p_allocator.alloc();
memnew_placement(obj, T);
return obj;
}
template <typename T>
- static void free(PagedAllocator<VersatileResourceTemplate> &p_allocator, T *p_object) {
+ static void free(PagedAllocator<VersatileResourceTemplate, true> &p_allocator, T *p_object) {
p_object->~T();
p_allocator.free((VersatileResourceTemplate *)p_object);
}
@@ -220,6 +220,7 @@ public:
enum TextureLayout {
TEXTURE_LAYOUT_UNDEFINED,
+ TEXTURE_LAYOUT_GENERAL,
TEXTURE_LAYOUT_STORAGE_OPTIMAL,
TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
@@ -750,6 +751,7 @@ public:
API_TRAIT_TEXTURE_DATA_ROW_PITCH_STEP,
API_TRAIT_SECONDARY_VIEWPORT_SCISSOR,
API_TRAIT_CLEARS_WITH_COPY_ENGINE,
+ API_TRAIT_USE_GENERAL_IN_COPY_QUEUES,
};
enum ShaderChangeInvalidation {
diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h
index f6212faf08..4c277ac215 100644
--- a/servers/rendering/rendering_method.h
+++ b/servers/rendering/rendering_method.h
@@ -118,6 +118,11 @@ public:
virtual Variant instance_geometry_get_shader_parameter(RID p_instance, const StringName &p_parameter) const = 0;
virtual Variant instance_geometry_get_shader_parameter_default_value(RID p_instance, const StringName &p_parameter) const = 0;
+ /* PIPELINES */
+
+ virtual void mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) = 0;
+ virtual uint32_t get_pipeline_compilations(RS::PipelineSource p_source) = 0;
+
/* SKY API */
virtual RID sky_allocate() = 0;
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index b994ebf337..20f1f9ad6f 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -281,6 +281,16 @@ uint64_t RenderingServerDefault::get_rendering_info(RenderingInfo p_info) {
return RSG::viewport->get_total_primitives_drawn();
} else if (p_info == RENDERING_INFO_TOTAL_DRAW_CALLS_IN_FRAME) {
return RSG::viewport->get_total_draw_calls_used();
+ } else if (p_info == RENDERING_INFO_PIPELINE_COMPILATIONS_CANVAS) {
+ return RSG::canvas_render->get_pipeline_compilations(PIPELINE_SOURCE_CANVAS);
+ } else if (p_info == RENDERING_INFO_PIPELINE_COMPILATIONS_MESH) {
+ return RSG::canvas_render->get_pipeline_compilations(PIPELINE_SOURCE_MESH) + RSG::scene->get_pipeline_compilations(PIPELINE_SOURCE_MESH);
+ } else if (p_info == RENDERING_INFO_PIPELINE_COMPILATIONS_SURFACE) {
+ return RSG::scene->get_pipeline_compilations(PIPELINE_SOURCE_SURFACE);
+ } else if (p_info == RENDERING_INFO_PIPELINE_COMPILATIONS_DRAW) {
+ return RSG::canvas_render->get_pipeline_compilations(PIPELINE_SOURCE_DRAW) + RSG::scene->get_pipeline_compilations(PIPELINE_SOURCE_DRAW);
+ } else if (p_info == RENDERING_INFO_PIPELINE_COMPILATIONS_SPECIALIZATION) {
+ return RSG::canvas_render->get_pipeline_compilations(PIPELINE_SOURCE_SPECIALIZATION) + RSG::scene->get_pipeline_compilations(PIPELINE_SOURCE_SPECIALIZATION);
}
return RSG::utilities->get_rendering_info(p_info);
}
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 2dcdc3f254..225a67fb52 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -129,32 +129,32 @@ public:
#define ServerName RendererTextureStorage
#define server_name RSG::texture_storage
-#define FUNCRIDTEX0(m_type) \
- virtual RID m_type##_create() override { \
- RID ret = RSG::texture_storage->texture_allocate(); \
- if (Thread::get_caller_id() == server_thread || RSG::texture_storage->can_create_resources_async()) { \
- RSG::texture_storage->m_type##_initialize(ret); \
- } else { \
- command_queue.push(RSG::texture_storage, &RendererTextureStorage::m_type##_initialize, ret); \
- } \
- return ret; \
+#define FUNCRIDTEX0(m_type) \
+ virtual RID m_type##_create() override { \
+ RID ret = RSG::texture_storage->texture_allocate(); \
+ if (Thread::get_caller_id() == server_thread || RSG::rasterizer->can_create_resources_async()) { \
+ RSG::texture_storage->m_type##_initialize(ret); \
+ } else { \
+ command_queue.push(RSG::texture_storage, &RendererTextureStorage::m_type##_initialize, ret); \
+ } \
+ return ret; \
}
-#define FUNCRIDTEX1(m_type, m_type1) \
- virtual RID m_type##_create(m_type1 p1) override { \
- RID ret = RSG::texture_storage->texture_allocate(); \
- if (Thread::get_caller_id() == server_thread || RSG::texture_storage->can_create_resources_async()) { \
- RSG::texture_storage->m_type##_initialize(ret, p1); \
- } else { \
- command_queue.push(RSG::texture_storage, &RendererTextureStorage::m_type##_initialize, ret, p1); \
- } \
- return ret; \
+#define FUNCRIDTEX1(m_type, m_type1) \
+ virtual RID m_type##_create(m_type1 p1) override { \
+ RID ret = RSG::texture_storage->texture_allocate(); \
+ if (Thread::get_caller_id() == server_thread || RSG::rasterizer->can_create_resources_async()) { \
+ RSG::texture_storage->m_type##_initialize(ret, p1); \
+ } else { \
+ command_queue.push(RSG::texture_storage, &RendererTextureStorage::m_type##_initialize, ret, p1); \
+ } \
+ return ret; \
}
#define FUNCRIDTEX2(m_type, m_type1, m_type2) \
virtual RID m_type##_create(m_type1 p1, m_type2 p2) override { \
RID ret = RSG::texture_storage->texture_allocate(); \
- if (Thread::get_caller_id() == server_thread || RSG::texture_storage->can_create_resources_async()) { \
+ if (Thread::get_caller_id() == server_thread || RSG::rasterizer->can_create_resources_async()) { \
RSG::texture_storage->m_type##_initialize(ret, p1, p2); \
} else { \
command_queue.push(RSG::texture_storage, &RendererTextureStorage::m_type##_initialize, ret, p1, p2); \
@@ -165,7 +165,7 @@ public:
#define FUNCRIDTEX3(m_type, m_type1, m_type2, m_type3) \
virtual RID m_type##_create(m_type1 p1, m_type2 p2, m_type3 p3) override { \
RID ret = RSG::texture_storage->texture_allocate(); \
- if (Thread::get_caller_id() == server_thread || RSG::texture_storage->can_create_resources_async()) { \
+ if (Thread::get_caller_id() == server_thread || RSG::rasterizer->can_create_resources_async()) { \
RSG::texture_storage->m_type##_initialize(ret, p1, p2, p3); \
} else { \
command_queue.push(RSG::texture_storage, &RendererTextureStorage::m_type##_initialize, ret, p1, p2, p3); \
@@ -176,7 +176,7 @@ public:
#define FUNCRIDTEX6(m_type, m_type1, m_type2, m_type3, m_type4, m_type5, m_type6) \
virtual RID m_type##_create(m_type1 p1, m_type2 p2, m_type3 p3, m_type4 p4, m_type5 p5, m_type6 p6) override { \
RID ret = RSG::texture_storage->texture_allocate(); \
- if (Thread::get_caller_id() == server_thread || RSG::texture_storage->can_create_resources_async()) { \
+ if (Thread::get_caller_id() == server_thread || RSG::rasterizer->can_create_resources_async()) { \
RSG::texture_storage->m_type##_initialize(ret, p1, p2, p3, p4, p5, p6); \
} else { \
command_queue.push(RSG::texture_storage, &RendererTextureStorage::m_type##_initialize, ret, p1, p2, p3, p4, p5, p6); \
@@ -245,6 +245,26 @@ public:
FUNCRIDSPLIT(shader)
+ virtual RID shader_create_from_code(const String &p_code, const String &p_path_hint = String()) override {
+ RID shader = RSG::material_storage->shader_allocate();
+ bool using_server_thread = Thread::get_caller_id() == server_thread;
+ if (using_server_thread || RSG::rasterizer->can_create_resources_async()) {
+ if (using_server_thread) {
+ command_queue.flush_if_pending();
+ }
+
+ RSG::material_storage->shader_initialize(shader);
+ RSG::material_storage->shader_set_code(shader, p_code);
+ RSG::material_storage->shader_set_path_hint(shader, p_path_hint);
+ } else {
+ command_queue.push(RSG::material_storage, &RendererMaterialStorage::shader_initialize, shader);
+ command_queue.push(RSG::material_storage, &RendererMaterialStorage::shader_set_code, shader, p_code);
+ command_queue.push(RSG::material_storage, &RendererMaterialStorage::shader_set_path_hint, shader, p_path_hint);
+ }
+
+ return shader;
+ }
+
FUNC2(shader_set_code, RID, const String &)
FUNC2(shader_set_path_hint, RID, const String &)
FUNC1RC(String, shader_get_code, RID)
@@ -261,6 +281,28 @@ public:
FUNCRIDSPLIT(material)
+ virtual RID material_create_from_shader(RID p_next_pass, int p_render_priority, RID p_shader) override {
+ RID material = RSG::material_storage->material_allocate();
+ bool using_server_thread = Thread::get_caller_id() == server_thread;
+ if (using_server_thread || RSG::rasterizer->can_create_resources_async()) {
+ if (using_server_thread) {
+ command_queue.flush_if_pending();
+ }
+
+ RSG::material_storage->material_initialize(material);
+ RSG::material_storage->material_set_next_pass(material, p_next_pass);
+ RSG::material_storage->material_set_render_priority(material, p_render_priority);
+ RSG::material_storage->material_set_shader(material, p_shader);
+ } else {
+ command_queue.push(RSG::material_storage, &RendererMaterialStorage::material_initialize, material);
+ command_queue.push(RSG::material_storage, &RendererMaterialStorage::material_set_next_pass, material, p_next_pass);
+ command_queue.push(RSG::material_storage, &RendererMaterialStorage::material_set_render_priority, material, p_render_priority);
+ command_queue.push(RSG::material_storage, &RendererMaterialStorage::material_set_shader, material, p_shader);
+ }
+
+ return material;
+ }
+
FUNC2(material_set_shader, RID, RID)
FUNC3(material_set_param, RID, const StringName &, const Variant &)
@@ -281,10 +323,9 @@ public:
virtual RID mesh_create_from_surfaces(const Vector<SurfaceData> &p_surfaces, int p_blend_shape_count = 0) override {
RID mesh = RSG::mesh_storage->mesh_allocate();
- // TODO once we have RSG::mesh_storage, add can_create_resources_async and call here instead of texture_storage!!
-
- if (Thread::get_caller_id() == server_thread || RSG::texture_storage->can_create_resources_async()) {
- if (Thread::get_caller_id() == server_thread) {
+ bool using_server_thread = Thread::get_caller_id() == server_thread;
+ if (using_server_thread || RSG::rasterizer->can_create_resources_async()) {
+ if (using_server_thread) {
command_queue.flush_if_pending();
}
RSG::mesh_storage->mesh_initialize(mesh);
@@ -292,12 +333,14 @@ public:
for (int i = 0; i < p_surfaces.size(); i++) {
RSG::mesh_storage->mesh_add_surface(mesh, p_surfaces[i]);
}
+ RSG::scene->mesh_generate_pipelines(mesh, using_server_thread);
} else {
command_queue.push(RSG::mesh_storage, &RendererMeshStorage::mesh_initialize, mesh);
command_queue.push(RSG::mesh_storage, &RendererMeshStorage::mesh_set_blend_shape_count, mesh, p_blend_shape_count);
for (int i = 0; i < p_surfaces.size(); i++) {
command_queue.push(RSG::mesh_storage, &RendererMeshStorage::mesh_add_surface, mesh, p_surfaces[i]);
}
+ command_queue.push(RSG::scene, &RenderingMethod::mesh_generate_pipelines, mesh, true);
}
return mesh;
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 3a0b9cf158..119ac677eb 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -355,7 +355,7 @@ void ShaderCompiler::_dump_function_deps(const SL::ShaderNode *p_node, const Str
}
header += " ";
- header += _mkid(fnode->name);
+ header += _mkid(fnode->rname);
header += "(";
for (int i = 0; i < fnode->arguments.size(); i++) {
@@ -951,7 +951,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
code = _get_global_shader_uniform_from_type_and_index(p_default_actions.global_buffer_array_variable, code, u.type);
} else if (u.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
//instance variable, index it as such
- code = "(" + p_default_actions.instance_uniform_index_variable + "+" + itos(u.instance_index) + ")";
+ code = "(" + p_default_actions.instance_uniform_index_variable + "+" + itos(u.instance_index) + "u)";
code = _get_global_shader_uniform_from_type_and_index(p_default_actions.global_buffer_array_variable, code, u.type);
} else {
//regular uniform, index from UBO
@@ -1051,7 +1051,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
code = _get_global_shader_uniform_from_type_and_index(p_default_actions.global_buffer_array_variable, code, u.type);
} else if (u.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
//instance variable, index it as such
- code = "(" + p_default_actions.instance_uniform_index_variable + "+" + itos(u.instance_index) + ")";
+ code = "(" + p_default_actions.instance_uniform_index_variable + "+" + itos(u.instance_index) + "u)";
code = _get_global_shader_uniform_from_type_and_index(p_default_actions.global_buffer_array_variable, code, u.type);
} else {
//regular uniform, index from UBO
@@ -1190,7 +1190,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
} else if (p_default_actions.renames.has(vnode->name)) {
code += p_default_actions.renames[vnode->name];
} else {
- code += _mkid(vnode->name);
+ code += _mkid(vnode->rname);
}
}
@@ -1354,7 +1354,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
}
code += ")";
if (is_screen_texture && !texture_func_returns_data && actions.apply_luminance_multiplier) {
- code = "(" + code + " * vec4(vec3(sc_luminance_multiplier), 1.0))";
+ code = "(" + code + " * vec4(vec3(sc_luminance_multiplier()), 1.0))";
}
if (is_normal_roughness_texture && !texture_func_returns_data) {
code = "normal_roughness_compatibility(" + code + ")";
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 879a83f519..b6770c773c 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -1305,6 +1305,7 @@ void ShaderLanguage::clear() {
include_markers_handled.clear();
calls_info.clear();
+ function_overload_count.clear();
#ifdef DEBUG_ENABLED
keyword_completion_context = CF_UNSPECIFIED;
@@ -3554,6 +3555,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
ERR_FAIL_COND_V(p_func->arguments[0]->type != Node::NODE_TYPE_VARIABLE, false);
StringName name = static_cast<VariableNode *>(p_func->arguments[0])->name.operator String();
+ StringName rname = static_cast<VariableNode *>(p_func->arguments[0])->rname.operator String();
for (int i = 1; i < p_func->arguments.size(); i++) {
args.push_back(p_func->arguments[i]->get_datatype());
@@ -3891,9 +3893,10 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
bool fail = false;
+ bool use_constant_conversion = function_overload_count[rname] == 0;
for (int j = 0; j < args.size(); j++) {
- if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::NODE_TYPE_CONSTANT && args3[j] == 0 && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) {
+ if (use_constant_conversion && get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::NODE_TYPE_CONSTANT && args3[j] == 0 && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) {
//all good, but it needs implicit conversion later
} else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].struct_name) || args3[j] != pfunc->arguments[j].array_size) {
String func_arg_name;
@@ -3919,7 +3922,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
arg_name += "]";
}
- _set_error(vformat(RTR("Invalid argument for \"%s(%s)\" function: argument %d should be %s but is %s."), String(name), arg_list, j + 1, func_arg_name, arg_name));
+ _set_error(vformat(RTR("Invalid argument for \"%s(%s)\" function: argument %d should be %s but is %s."), String(rname), arg_list, j + 1, func_arg_name, arg_name));
fail = true;
break;
}
@@ -3960,9 +3963,9 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
if (exists) {
if (last_arg_count > args.size()) {
- _set_error(vformat(RTR("Too few arguments for \"%s(%s)\" call. Expected at least %d but received %d."), String(name), arg_list, last_arg_count, args.size()));
+ _set_error(vformat(RTR("Too few arguments for \"%s(%s)\" call. Expected at least %d but received %d."), String(rname), arg_list, last_arg_count, args.size()));
} else if (last_arg_count < args.size()) {
- _set_error(vformat(RTR("Too many arguments for \"%s(%s)\" call. Expected at most %d but received %d."), String(name), arg_list, last_arg_count, args.size()));
+ _set_error(vformat(RTR("Too many arguments for \"%s(%s)\" call. Expected at most %d but received %d."), String(rname), arg_list, last_arg_count, args.size()));
}
}
@@ -5822,42 +5825,16 @@ 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;
- }
- }
+ const StringName &rname = identifier;
+ StringName name = identifier;
OperatorNode *func = alloc_node<OperatorNode>();
func->op = OP_CALL;
+
VariableNode *funcname = alloc_node<VariableNode>();
funcname->name = name;
+ funcname->rname = name;
+
func->arguments.push_back(funcname);
int carg = -1;
@@ -5874,22 +5851,72 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
bnode = bnode->parent_block;
}
- //test if function was parsed first
+ // Test if function was parsed first.
int function_index = -1;
- for (int i = 0; i < shader->vfunctions.size(); i++) {
- if (shader->vfunctions[i].name == name) {
- //add to current function as dependency
- for (int j = 0; j < shader->vfunctions.size(); j++) {
- if (shader->vfunctions[j].name == current_function) {
- shader->vfunctions.write[j].uses_function.insert(name);
- break;
+ for (int i = 0, max_valid_args = 0; i < shader->vfunctions.size(); i++) {
+ if (!shader->vfunctions[i].callable || shader->vfunctions[i].rname != rname) {
+ continue;
+ }
+
+ bool found = true;
+ int valid_args = 0;
+
+ // Search for correct overload.
+ for (int j = 1; j < func->arguments.size(); j++) {
+ if (j - 1 == shader->vfunctions[i].function->arguments.size()) {
+ found = false;
+ break;
+ }
+
+ const FunctionNode::Argument &a = shader->vfunctions[i].function->arguments[j - 1];
+ Node *b = func->arguments[j];
+
+ if (a.type == b->get_datatype() && a.array_size == b->get_array_size()) {
+ if (a.type == TYPE_STRUCT) {
+ if (a.struct_name != b->get_datatype_name()) {
+ found = false;
+ break;
+ } else {
+ valid_args++;
+ }
+ } else {
+ valid_args++;
}
+ } else {
+ if (function_overload_count[rname] == 0 && get_scalar_type(a.type) == a.type && b->type == Node::NODE_TYPE_CONSTANT && a.array_size == 0 && convert_constant(static_cast<ConstantNode *>(b), a.type)) {
+ // Implicit cast if no overloads.
+ continue;
+ }
+ found = false;
+ break;
}
+ }
- //see if texture arguments must connect
- function_index = i;
- break;
+ // Using the best match index for completion hint if the function not found.
+ if (valid_args > max_valid_args) {
+ name = shader->vfunctions[i].name;
+ funcname->name = name;
+ max_valid_args = valid_args;
}
+
+ if (!found) {
+ continue;
+ }
+
+ // Add to current function as dependency.
+ for (int j = 0; j < shader->vfunctions.size(); j++) {
+ if (shader->vfunctions[j].name == current_function) {
+ shader->vfunctions.write[j].uses_function.insert(name);
+ break;
+ }
+ }
+
+ name = shader->vfunctions[i].name;
+ funcname->name = name;
+
+ // See if texture arguments must connect.
+ function_index = i;
+ break;
}
if (carg >= 0) {
@@ -5904,9 +5931,39 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
return nullptr;
}
+ 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;
+ }
+ }
+
bool is_custom_func = false;
if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name, &is_custom_func)) {
- _set_error(vformat(RTR("No matching function found for: '%s'."), String(funcname->name)));
+ _set_error(vformat(RTR("No matching function found for: '%s'."), String(funcname->rname)));
return nullptr;
}
completion_class = TAG_GLOBAL; // reset sub-class
@@ -6095,7 +6152,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
expr = func;
#ifdef DEBUG_ENABLED
- if (check_warnings) {
+ if (check_warnings && is_custom_func) {
StringName func_name;
if (p_block && p_block->parent_function) {
@@ -9089,10 +9146,6 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
_set_error(vformat(RTR("Uniform instances are not yet implemented for '%s' shaders."), shader_type_identifier));
return ERR_PARSE_ERROR;
}
- if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
- _set_error(RTR("Uniform instances are not supported in gl_compatibility shaders."));
- return ERR_PARSE_ERROR;
- }
if (uniform_scope == ShaderNode::Uniform::SCOPE_LOCAL) {
tk = _get_token();
if (tk.type != TK_UNIFORM) {
@@ -9936,7 +9989,8 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
return ERR_PARSE_ERROR;
}
- if (shader->structs.has(name) || _find_identifier(nullptr, false, constants, name) || has_builtin(p_functions, name, !is_constant)) {
+ IdentifierType itype;
+ if (shader->structs.has(name) || (_find_identifier(nullptr, false, constants, name, nullptr, &itype) && itype != IDENTIFIER_FUNCTION) || has_builtin(p_functions, name, !is_constant)) {
_set_redefinition_error(String(name));
return ERR_PARSE_ERROR;
}
@@ -10264,20 +10318,13 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
function.callable = !p_functions.has(name);
function.name = name;
+ function.rname = name;
FunctionNode *func_node = alloc_node<FunctionNode>();
-
function.function = func_node;
- 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->rname = name;
func_node->return_type = type;
func_node->return_struct_name = struct_name;
func_node->return_precision = precision;
@@ -10285,12 +10332,12 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
if (p_functions.has(name)) {
func_node->can_discard = p_functions[name].can_discard;
+ }
+
+ if (!function_overload_count.has(name)) {
+ function_overload_count.insert(name, 0);
} else {
-#ifdef DEBUG_ENABLED
- if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_FUNCTION_FLAG)) {
- used_functions.insert(name, Usage(tk_line));
- }
-#endif // DEBUG_ENABLED
+ function_overload_count[name]++;
}
func_node->body = alloc_node<BlockNode>();
@@ -10470,7 +10517,6 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
param_name = tk.text;
- ShaderLanguage::IdentifierType itype;
if (_find_identifier(func_node->body, false, builtins, param_name, (ShaderLanguage::DataType *)nullptr, &itype)) {
if (itype != IDENTIFIER_FUNCTION) {
_set_redefinition_error(String(param_name));
@@ -10516,6 +10562,66 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
}
}
+ // Searches for function index and check for the exact duplicate in overloads.
+ int function_index = 0;
+ for (int i = 0; i < shader->vfunctions.size(); i++) {
+ if (!shader->vfunctions[i].callable || shader->vfunctions[i].rname != name) {
+ continue;
+ }
+
+ function_index++;
+
+ if (shader->vfunctions[i].function->arguments.size() != func_node->arguments.size()) {
+ continue;
+ }
+
+ bool is_same = true;
+
+ for (int j = 0; j < shader->vfunctions[i].function->arguments.size(); j++) {
+ FunctionNode::Argument a = func_node->arguments[j];
+ FunctionNode::Argument b = shader->vfunctions[i].function->arguments[j];
+
+ if (a.type == b.type && a.array_size == b.array_size) {
+ if (a.type == TYPE_STRUCT) {
+ is_same = a.struct_name == b.struct_name;
+ }
+ } else {
+ is_same = false;
+ }
+
+ if (!is_same) {
+ break;
+ }
+ }
+
+ if (is_same) {
+ _set_redefinition_error(String(name));
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ // Creates a fake name for function overload, which will be replaced by the real name by the compiler.
+ String name2 = name;
+ if (function_index > 0) {
+ name2 = vformat("%s@%s", name, itos(function_index + 1));
+
+ function.name = name2;
+ func_node->name = name2;
+ }
+
+ shader->functions.insert(name2, function);
+ shader->vfunctions.push_back(function);
+
+ CallInfo call_info;
+ call_info.name = name2;
+ calls_info.insert(name2, call_info);
+
+#ifdef DEBUG_ENABLED
+ if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_FUNCTION_FLAG) && !p_functions.has(name)) {
+ used_functions.insert(name2, Usage(tk_line));
+ }
+#endif // DEBUG_ENABLED
+
if (p_functions.has(name)) {
//if one of the core functions, make sure they are of the correct form
if (func_node->arguments.size() > 0) {
@@ -10535,7 +10641,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
return ERR_PARSE_ERROR;
}
- current_function = name;
+ current_function = name2;
#ifdef DEBUG_ENABLED
keyword_completion_context = CF_BLOCK;
@@ -11048,7 +11154,7 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
if (!shader->vfunctions[i].callable || shader->vfunctions[i].name == skip_function) {
continue;
}
- matches.insert(String(shader->vfunctions[i].name), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
+ matches.insert(String(shader->vfunctions[i].rname), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
}
int idx = 0;
@@ -11099,6 +11205,7 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
case COMPLETION_CALL_ARGUMENTS: {
StringName block_function;
BlockNode *block = completion_block;
+ String calltip;
while (block) {
if (block->parent_function) {
@@ -11107,85 +11214,83 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
block = block->parent_block;
}
- for (int i = 0; i < shader->vfunctions.size(); i++) {
- if (!shader->vfunctions[i].callable) {
+ for (int i = 0, overload_index = 0; i < shader->vfunctions.size(); i++) {
+ if (!shader->vfunctions[i].callable || shader->vfunctions[i].rname != completion_function) {
continue;
}
- if (shader->vfunctions[i].name == completion_function) {
- String calltip;
- if (shader->vfunctions[i].function->return_type == TYPE_STRUCT) {
- calltip += String(shader->vfunctions[i].function->return_struct_name);
- } else {
- calltip += get_datatype_name(shader->vfunctions[i].function->return_type);
- }
-
- if (shader->vfunctions[i].function->return_array_size > 0) {
- calltip += "[";
- calltip += itos(shader->vfunctions[i].function->return_array_size);
- calltip += "]";
- }
+ if (shader->vfunctions[i].function->return_type == TYPE_STRUCT) {
+ calltip += String(shader->vfunctions[i].function->return_struct_name);
+ } else {
+ calltip += get_datatype_name(shader->vfunctions[i].function->return_type);
+ }
- calltip += " ";
- calltip += shader->vfunctions[i].name;
- calltip += "(";
+ if (shader->vfunctions[i].function->return_array_size > 0) {
+ calltip += "[";
+ calltip += itos(shader->vfunctions[i].function->return_array_size);
+ calltip += "]";
+ }
- for (int j = 0; j < shader->vfunctions[i].function->arguments.size(); j++) {
- if (j > 0) {
- calltip += ", ";
- } else {
- calltip += " ";
- }
+ calltip += " ";
+ calltip += shader->vfunctions[i].rname;
+ calltip += "(";
- if (j == completion_argument) {
- calltip += char32_t(0xFFFF);
- }
+ for (int j = 0; j < shader->vfunctions[i].function->arguments.size(); j++) {
+ if (j > 0) {
+ calltip += ", ";
+ } else {
+ calltip += " ";
+ }
- if (shader->vfunctions[i].function->arguments[j].is_const) {
- calltip += "const ";
- }
+ if (j == completion_argument) {
+ calltip += char32_t(0xFFFF);
+ }
- if (shader->vfunctions[i].function->arguments[j].qualifier != ArgumentQualifier::ARGUMENT_QUALIFIER_IN) {
- if (shader->vfunctions[i].function->arguments[j].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_OUT) {
- calltip += "out ";
- } else { // ArgumentQualifier::ARGUMENT_QUALIFIER_INOUT
- calltip += "inout ";
- }
- }
+ if (shader->vfunctions[i].function->arguments[j].is_const) {
+ calltip += "const ";
+ }
- if (shader->vfunctions[i].function->arguments[j].type == TYPE_STRUCT) {
- calltip += String(shader->vfunctions[i].function->arguments[j].struct_name);
- } else {
- calltip += get_datatype_name(shader->vfunctions[i].function->arguments[j].type);
+ if (shader->vfunctions[i].function->arguments[j].qualifier != ArgumentQualifier::ARGUMENT_QUALIFIER_IN) {
+ if (shader->vfunctions[i].function->arguments[j].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_OUT) {
+ calltip += "out ";
+ } else { // ArgumentQualifier::ARGUMENT_QUALIFIER_INOUT
+ calltip += "inout ";
}
- calltip += " ";
- calltip += shader->vfunctions[i].function->arguments[j].name;
+ }
- if (shader->vfunctions[i].function->arguments[j].array_size > 0) {
- calltip += "[";
- calltip += itos(shader->vfunctions[i].function->arguments[j].array_size);
- calltip += "]";
- }
+ if (shader->vfunctions[i].function->arguments[j].type == TYPE_STRUCT) {
+ calltip += String(shader->vfunctions[i].function->arguments[j].struct_name);
+ } else {
+ calltip += get_datatype_name(shader->vfunctions[i].function->arguments[j].type);
+ }
+ calltip += " ";
+ calltip += shader->vfunctions[i].function->arguments[j].name;
- if (j == completion_argument) {
- calltip += char32_t(0xFFFF);
- }
+ if (shader->vfunctions[i].function->arguments[j].array_size > 0) {
+ calltip += "[";
+ calltip += itos(shader->vfunctions[i].function->arguments[j].array_size);
+ calltip += "]";
}
- if (shader->vfunctions[i].function->arguments.size()) {
- calltip += " ";
+ if (j == completion_argument) {
+ calltip += char32_t(0xFFFF);
}
- calltip += ")";
+ }
- r_call_hint = calltip;
- return OK;
+ if (shader->vfunctions[i].function->arguments.size()) {
+ calltip += " ";
}
- }
+ calltip += ")";
- int idx = 0;
+ if (overload_index < function_overload_count[shader->vfunctions[i].rname]) {
+ overload_index++;
+ calltip += "\n";
+ continue;
+ }
- String calltip;
- bool low_end = RenderingServer::get_singleton()->is_low_end();
+ r_call_hint = calltip;
+ return OK;
+ }
if (stages && stages->has(block_function)) {
for (const KeyValue<StringName, StageFunctionInfo> &E : (*stages)[block_function].stage_functions) {
@@ -11226,6 +11331,9 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
}
}
+ int idx = 0;
+ bool low_end = RenderingServer::get_singleton()->is_low_end();
+
while (builtin_func_defs[idx].name) {
if ((low_end && builtin_func_defs[idx].high_end) || _check_restricted_func(builtin_func_defs[idx].name, block_function)) {
idx++;
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index 48df77f6bb..fb0a526230 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -427,6 +427,7 @@ public:
struct VariableNode : public Node {
DataType datatype_cache = TYPE_VOID;
StringName name;
+ StringName rname;
StringName struct_name;
bool is_const = false;
bool is_local = false;
@@ -604,6 +605,7 @@ public:
struct Function {
StringName name;
+ StringName rname;
FunctionNode *function = nullptr;
HashSet<StringName> uses_function;
bool callable;
@@ -729,6 +731,7 @@ public:
};
StringName name;
+ StringName rname;
DataType return_type = TYPE_VOID;
StringName return_struct_name;
DataPrecision return_precision = PRECISION_DEFAULT;
@@ -944,6 +947,7 @@ private:
Vector<FilePosition> include_positions;
HashSet<String> include_markers_handled;
+ HashMap<StringName, int> function_overload_count;
// Additional function information (eg. call hierarchy). No need to expose it to compiler.
struct CallInfo {
diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h
index fd17b052ee..e528384891 100644
--- a/servers/rendering/storage/texture_storage.h
+++ b/servers/rendering/storage/texture_storage.h
@@ -59,7 +59,6 @@ public:
virtual void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) = 0;
/* Texture API */
- virtual bool can_create_resources_async() const = 0;
virtual ~RendererTextureStorage() {}
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 5c85080298..32ef5261f3 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -3447,6 +3447,18 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(RENDERING_INFO_TEXTURE_MEM_USED);
BIND_ENUM_CONSTANT(RENDERING_INFO_BUFFER_MEM_USED);
BIND_ENUM_CONSTANT(RENDERING_INFO_VIDEO_MEM_USED);
+ BIND_ENUM_CONSTANT(RENDERING_INFO_PIPELINE_COMPILATIONS_CANVAS);
+ BIND_ENUM_CONSTANT(RENDERING_INFO_PIPELINE_COMPILATIONS_MESH);
+ BIND_ENUM_CONSTANT(RENDERING_INFO_PIPELINE_COMPILATIONS_SURFACE);
+ BIND_ENUM_CONSTANT(RENDERING_INFO_PIPELINE_COMPILATIONS_DRAW);
+ BIND_ENUM_CONSTANT(RENDERING_INFO_PIPELINE_COMPILATIONS_SPECIALIZATION);
+
+ BIND_ENUM_CONSTANT(PIPELINE_SOURCE_CANVAS);
+ BIND_ENUM_CONSTANT(PIPELINE_SOURCE_MESH);
+ BIND_ENUM_CONSTANT(PIPELINE_SOURCE_SURFACE);
+ BIND_ENUM_CONSTANT(PIPELINE_SOURCE_DRAW);
+ BIND_ENUM_CONSTANT(PIPELINE_SOURCE_SPECIALIZATION);
+ BIND_ENUM_CONSTANT(PIPELINE_SOURCE_MAX);
ADD_SIGNAL(MethodInfo("frame_pre_draw"));
ADD_SIGNAL(MethodInfo("frame_post_draw"));
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 878b02eaf1..0208a640a5 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -199,6 +199,17 @@ public:
virtual RID texture_get_rd_texture(RID p_texture, bool p_srgb = false) const = 0;
virtual uint64_t texture_get_native_handle(RID p_texture, bool p_srgb = false) const = 0;
+ /* PIPELINES API */
+
+ enum PipelineSource {
+ PIPELINE_SOURCE_CANVAS,
+ PIPELINE_SOURCE_MESH,
+ PIPELINE_SOURCE_SURFACE,
+ PIPELINE_SOURCE_DRAW,
+ PIPELINE_SOURCE_SPECIALIZATION,
+ PIPELINE_SOURCE_MAX
+ };
+
/* SHADER API */
enum ShaderMode {
@@ -211,6 +222,7 @@ public:
};
virtual RID shader_create() = 0;
+ virtual RID shader_create_from_code(const String &p_code, const String &p_path_hint = String()) = 0;
virtual void shader_set_code(RID p_shader, const String &p_code) = 0;
virtual void shader_set_path_hint(RID p_shader, const String &p_path) = 0;
@@ -242,6 +254,7 @@ public:
};
virtual RID material_create() = 0;
+ virtual RID material_create_from_shader(RID p_next_pass, int p_render_priority, RID p_shader) = 0;
virtual void material_set_shader(RID p_shader_material, RID p_shader) = 0;
@@ -1697,6 +1710,11 @@ public:
RENDERING_INFO_TEXTURE_MEM_USED,
RENDERING_INFO_BUFFER_MEM_USED,
RENDERING_INFO_VIDEO_MEM_USED,
+ RENDERING_INFO_PIPELINE_COMPILATIONS_CANVAS,
+ RENDERING_INFO_PIPELINE_COMPILATIONS_MESH,
+ RENDERING_INFO_PIPELINE_COMPILATIONS_SURFACE,
+ RENDERING_INFO_PIPELINE_COMPILATIONS_DRAW,
+ RENDERING_INFO_PIPELINE_COMPILATIONS_SPECIALIZATION,
RENDERING_INFO_MAX
};
@@ -1807,6 +1825,7 @@ private:
VARIANT_ENUM_CAST(RenderingServer::TextureType);
VARIANT_ENUM_CAST(RenderingServer::TextureLayeredType);
VARIANT_ENUM_CAST(RenderingServer::CubeMapLayer);
+VARIANT_ENUM_CAST(RenderingServer::PipelineSource);
VARIANT_ENUM_CAST(RenderingServer::ShaderMode);
VARIANT_ENUM_CAST(RenderingServer::ArrayType);
VARIANT_BITFIELD_CAST(RenderingServer::ArrayFormat);