You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-29 16:16:38 +00:00
Acyclic Command Graph for RenderingDevice.
Adds a new system to automatically reorder commands, perform layout transitions and insert synchronization barriers based on the commands issued to RenderingDevice.
This commit is contained in:
@@ -576,31 +576,13 @@ void RenderForwardClustered::_render_list(RenderingDevice::DrawListID p_draw_lis
|
||||
}
|
||||
}
|
||||
|
||||
void RenderForwardClustered::_render_list_thread_function(uint32_t p_thread, RenderListParameters *p_params) {
|
||||
uint32_t render_total = p_params->element_count;
|
||||
uint32_t total_threads = WorkerThreadPool::get_singleton()->get_thread_count();
|
||||
uint32_t render_from = p_thread * render_total / total_threads;
|
||||
uint32_t render_to = (p_thread + 1 == total_threads) ? render_total : ((p_thread + 1) * render_total / total_threads);
|
||||
_render_list(thread_draw_lists[p_thread], p_params->framebuffer_format, p_params, render_from, render_to);
|
||||
}
|
||||
|
||||
void RenderForwardClustered::_render_list_with_threads(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) {
|
||||
void RenderForwardClustered::_render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::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) {
|
||||
RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(p_framebuffer);
|
||||
p_params->framebuffer_format = fb_format;
|
||||
|
||||
if ((uint32_t)p_params->element_count > render_list_thread_threshold && false) { // secondary command buffers need more testing at this time
|
||||
//multi threaded
|
||||
thread_draw_lists.resize(WorkerThreadPool::get_singleton()->get_thread_count());
|
||||
RD::get_singleton()->draw_list_begin_split(p_framebuffer, thread_draw_lists.size(), thread_draw_lists.ptr(), p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, p_storage_textures);
|
||||
WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &RenderForwardClustered::_render_list_thread_function, p_params, thread_draw_lists.size(), -1, true, SNAME("ForwardClusteredRenderList"));
|
||||
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
|
||||
RD::get_singleton()->draw_list_end(p_params->barrier);
|
||||
} else {
|
||||
//single threaded
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, p_storage_textures);
|
||||
_render_list(draw_list, fb_format, p_params, 0, p_params->element_count);
|
||||
RD::get_singleton()->draw_list_end(p_params->barrier);
|
||||
}
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region);
|
||||
_render_list(draw_list, fb_format, p_params, 0, p_params->element_count);
|
||||
RD::get_singleton()->draw_list_end();
|
||||
}
|
||||
|
||||
void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_opaque_render_buffers, bool p_apply_alpha_multiplier, bool p_pancake_shadows, int p_index) {
|
||||
@@ -683,7 +665,7 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat
|
||||
}
|
||||
}
|
||||
|
||||
RD::get_singleton()->buffer_update(scene_state.implementation_uniform_buffers[p_index], 0, sizeof(SceneState::UBO), &scene_state.ubo, RD::BARRIER_MASK_RASTER);
|
||||
RD::get_singleton()->buffer_update(scene_state.implementation_uniform_buffers[p_index], 0, sizeof(SceneState::UBO), &scene_state.ubo);
|
||||
}
|
||||
|
||||
void RenderForwardClustered::_update_instance_data_buffer(RenderListType p_render_list) {
|
||||
@@ -696,7 +678,7 @@ void RenderForwardClustered::_update_instance_data_buffer(RenderListType p_rende
|
||||
scene_state.instance_buffer[p_render_list] = RD::get_singleton()->storage_buffer_create(new_size * sizeof(SceneState::InstanceData));
|
||||
scene_state.instance_buffer_size[p_render_list] = new_size;
|
||||
}
|
||||
RD::get_singleton()->buffer_update(scene_state.instance_buffer[p_render_list], 0, sizeof(SceneState::InstanceData) * scene_state.instance_data[p_render_list].size(), scene_state.instance_data[p_render_list].ptr(), RD::BARRIER_MASK_RASTER);
|
||||
RD::get_singleton()->buffer_update(scene_state.instance_buffer[p_render_list], 0, sizeof(SceneState::InstanceData) * scene_state.instance_data[p_render_list].size(), scene_state.instance_data[p_render_list].ptr());
|
||||
}
|
||||
}
|
||||
void RenderForwardClustered::_fill_instance_data(RenderListType p_render_list, int *p_render_info, uint32_t p_offset, int32_t p_max_elements, bool p_update_buffer) {
|
||||
@@ -1097,7 +1079,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
|
||||
}
|
||||
|
||||
if (p_render_list == RENDER_LIST_OPAQUE && lightmap_captures_used) {
|
||||
RD::get_singleton()->buffer_update(scene_state.lightmap_capture_buffer, 0, sizeof(LightmapCaptureData) * lightmap_captures_used, scene_state.lightmap_captures, RD::BARRIER_MASK_RASTER);
|
||||
RD::get_singleton()->buffer_update(scene_state.lightmap_capture_buffer, 0, sizeof(LightmapCaptureData) * lightmap_captures_used, scene_state.lightmap_captures);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1135,7 +1117,7 @@ void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data,
|
||||
scene_state.lightmaps_used++;
|
||||
}
|
||||
if (scene_state.lightmaps_used > 0) {
|
||||
RD::get_singleton()->buffer_update(scene_state.lightmap_buffer, 0, sizeof(LightmapData) * scene_state.lightmaps_used, scene_state.lightmaps, RD::BARRIER_MASK_RASTER);
|
||||
RD::get_singleton()->buffer_update(scene_state.lightmap_buffer, 0, sizeof(LightmapData) * scene_state.lightmaps_used, scene_state.lightmaps);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1427,7 +1409,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
|
||||
if (p_render_data->directional_shadows.size()) {
|
||||
//open the pass for directional shadows
|
||||
light_storage->update_directional_shadow_atlas();
|
||||
RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE);
|
||||
RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE);
|
||||
RD::get_singleton()->draw_list_end();
|
||||
}
|
||||
}
|
||||
@@ -1461,18 +1443,12 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
|
||||
_render_shadow_process();
|
||||
}
|
||||
|
||||
//start GI
|
||||
if (render_gi) {
|
||||
gi.process_gi(rb, p_normal_roughness_slices, p_voxel_gi_buffer, p_render_data->environment, p_render_data->scene_data->view_count, p_render_data->scene_data->view_projection, p_render_data->scene_data->view_eye_offset, p_render_data->scene_data->cam_transform, *p_render_data->voxel_gi_instances);
|
||||
}
|
||||
|
||||
//Do shadow rendering (in parallel with GI)
|
||||
if (render_shadows) {
|
||||
_render_shadow_end(RD::BARRIER_MASK_NO_BARRIER);
|
||||
}
|
||||
|
||||
if (render_gi) {
|
||||
RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER); //use a later barrier
|
||||
_render_shadow_end();
|
||||
}
|
||||
|
||||
if (rb_data.is_valid() && ss_effects) {
|
||||
@@ -1496,9 +1472,6 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo
|
||||
}
|
||||
}
|
||||
|
||||
//full barrier here, we need raster, transfer and compute and it depends from the previous work
|
||||
RD::get_singleton()->barrier(RD::BARRIER_MASK_ALL_BARRIERS, RD::BARRIER_MASK_ALL_BARRIERS);
|
||||
|
||||
if (current_cluster_builder) {
|
||||
// Note: when rendering stereoscopic (multiview) we are using our combined frustum projection to create
|
||||
// our cluster data. We use reprojection in the shader to adjust for our left/right eye.
|
||||
@@ -1814,7 +1787,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
|
||||
float sky_energy_multiplier = 1.0 / _render_buffers_get_luminance_multiplier();
|
||||
|
||||
Color clear_color;
|
||||
bool keep_color = false;
|
||||
bool load_color = false;
|
||||
|
||||
if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) {
|
||||
clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black
|
||||
@@ -1857,10 +1830,10 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
|
||||
bool convert_to_linear = !RendererRD::TextureStorage::get_singleton()->render_target_is_using_hdr(rb->get_render_target());
|
||||
copy_effects->copy_to_fb_rect(texture, color_only_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, convert_to_linear);
|
||||
}
|
||||
keep_color = true;
|
||||
load_color = true;
|
||||
} break;
|
||||
case RS::ENV_BG_KEEP: {
|
||||
keep_color = true;
|
||||
load_color = true;
|
||||
} break;
|
||||
case RS::ENV_BG_CAMERA_FEED: {
|
||||
} break;
|
||||
@@ -1912,7 +1885,6 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
|
||||
bool depth_pre_pass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable")) && depth_framebuffer.is_valid();
|
||||
|
||||
bool using_ssao = depth_pre_pass && !is_reflection_probe && p_render_data->environment.is_valid() && environment_get_ssao_enabled(p_render_data->environment);
|
||||
bool continue_depth = false;
|
||||
if (depth_pre_pass) { //depth pre pass
|
||||
|
||||
bool needs_pre_resolve = _needs_post_prepass_render(p_render_data, using_sdfgi || using_voxelgi);
|
||||
@@ -1923,7 +1895,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
|
||||
}
|
||||
if (needs_pre_resolve) {
|
||||
//pre clear the depth framebuffer, as AMD (and maybe others?) use compute for it, and barrier other compute shaders.
|
||||
RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE, depth_pass_clear);
|
||||
RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, depth_pass_clear);
|
||||
RD::get_singleton()->draw_list_end();
|
||||
//start compute processes here, so they run at the same time as depth pre-pass
|
||||
_post_prepass_render(p_render_data, using_sdfgi || using_voxelgi);
|
||||
@@ -1935,21 +1907,14 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
|
||||
|
||||
bool finish_depth = using_ssao || using_ssil || using_sdfgi || using_voxelgi;
|
||||
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);
|
||||
_render_list_with_threads(&render_list_params, depth_framebuffer, needs_pre_resolve ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, needs_pre_resolve ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, finish_depth ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, needs_pre_resolve ? Vector<Color>() : depth_pass_clear);
|
||||
_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();
|
||||
|
||||
if (needs_pre_resolve) {
|
||||
_pre_resolve_render(p_render_data, using_sdfgi || using_voxelgi);
|
||||
}
|
||||
|
||||
if (rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
|
||||
RENDER_TIMESTAMP("Resolve Depth Pre-Pass (MSAA)");
|
||||
RD::get_singleton()->draw_command_begin_label("Resolve Depth Pre-Pass (MSAA)");
|
||||
if (depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS || depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI) {
|
||||
if (needs_pre_resolve) {
|
||||
RD::get_singleton()->barrier(RD::BARRIER_MASK_RASTER, RD::BARRIER_MASK_COMPUTE);
|
||||
}
|
||||
for (uint32_t v = 0; v < rb->get_view_count(); v++) {
|
||||
resolve_effects->resolve_gi(rb->get_depth_msaa(v), rb_data->get_normal_roughness_msaa(v), using_voxelgi ? rb_data->get_voxelgi_msaa(v) : RID(), rb->get_depth_texture(v), rb_data->get_normal_roughness(v), using_voxelgi ? rb_data->get_voxelgi(v) : RID(), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]);
|
||||
}
|
||||
@@ -1960,8 +1925,6 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
|
||||
}
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
}
|
||||
|
||||
continue_depth = !finish_depth;
|
||||
}
|
||||
|
||||
RID normal_roughness_views[RendererSceneRender::MAX_RENDER_VIEWS];
|
||||
@@ -1990,10 +1953,6 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
|
||||
|
||||
{
|
||||
bool render_motion_pass = !render_list[RENDER_LIST_MOTION].elements.is_empty();
|
||||
bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only || debug_voxelgis || debug_sdfgi_probes);
|
||||
bool will_continue_depth = (can_continue_depth || draw_sky || draw_sky_fog_only || debug_voxelgis || debug_sdfgi_probes);
|
||||
RD::FinalAction final_color_action = will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ;
|
||||
RD::FinalAction final_depth_action = will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ;
|
||||
|
||||
{
|
||||
Vector<Color> c;
|
||||
@@ -2014,7 +1973,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);
|
||||
_render_list_with_threads(&render_list_params, opaque_framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, render_motion_pass ? RD::FINAL_ACTION_CONTINUE : final_color_action, depth_pre_pass ? (continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP) : RD::INITIAL_ACTION_CLEAR, render_motion_pass ? RD::FINAL_ACTION_CONTINUE : final_depth_action, c, 1.0, 0);
|
||||
_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, 1.0, 0);
|
||||
}
|
||||
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
@@ -2022,7 +1981,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
|
||||
if (using_motion_pass) {
|
||||
Vector<Color> motion_vector_clear_colors;
|
||||
motion_vector_clear_colors.push_back(Color(-1, -1, 0, 0));
|
||||
RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::INITIAL_ACTION_CLEAR, render_motion_pass ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_CONTINUE, motion_vector_clear_colors);
|
||||
RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, motion_vector_clear_colors);
|
||||
RD::get_singleton()->draw_list_end();
|
||||
}
|
||||
|
||||
@@ -2034,33 +1993,17 @@ 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);
|
||||
_render_list_with_threads(&render_list_params, color_framebuffer, RD::INITIAL_ACTION_CONTINUE, final_color_action, RD::INITIAL_ACTION_CONTINUE, final_depth_action);
|
||||
_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();
|
||||
|
||||
if (will_continue_color) {
|
||||
// Close the motion vectors framebuffer as it'll no longer be used.
|
||||
RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_CONTINUE);
|
||||
RD::get_singleton()->draw_list_end();
|
||||
}
|
||||
}
|
||||
|
||||
if (will_continue_color && using_separate_specular) {
|
||||
// Close the specular framebuffer as it'll no longer be used.
|
||||
RD::get_singleton()->draw_list_begin(rb_data->get_specular_only_fb(), RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_CONTINUE);
|
||||
RD::get_singleton()->draw_list_end();
|
||||
}
|
||||
}
|
||||
|
||||
if (debug_voxelgis) {
|
||||
//debug voxelgis
|
||||
bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only);
|
||||
bool will_continue_depth = (can_continue_depth || draw_sky || draw_sky_fog_only);
|
||||
|
||||
Projection dc;
|
||||
dc.set_depth_correction(true);
|
||||
Projection cm = (dc * p_render_data->scene_data->cam_projection) * Projection(p_render_data->scene_data->cam_transform.affine_inverse());
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::INITIAL_ACTION_CONTINUE, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ);
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE);
|
||||
RD::get_singleton()->draw_command_begin_label("Debug VoxelGIs");
|
||||
for (int i = 0; i < (int)p_render_data->voxel_gi_instances->size(); i++) {
|
||||
gi.debug_voxel_gi((*p_render_data->voxel_gi_instances)[i], draw_list, color_only_framebuffer, cm, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_LIGHTING, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_EMISSION, 1.0);
|
||||
@@ -2070,24 +2013,20 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
|
||||
}
|
||||
|
||||
if (debug_sdfgi_probes) {
|
||||
//debug sdfgi
|
||||
bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only);
|
||||
bool will_continue_depth = (can_continue_depth || draw_sky || draw_sky_fog_only);
|
||||
|
||||
Projection dc;
|
||||
dc.set_depth_correction(true);
|
||||
Projection cms[RendererSceneRender::MAX_RENDER_VIEWS];
|
||||
for (uint32_t v = 0; v < p_render_data->scene_data->view_count; v++) {
|
||||
cms[v] = (dc * p_render_data->scene_data->view_projection[v]) * Projection(p_render_data->scene_data->cam_transform.affine_inverse());
|
||||
}
|
||||
_debug_sdfgi_probes(rb, color_only_framebuffer, p_render_data->scene_data->view_count, cms, will_continue_color, will_continue_depth);
|
||||
_debug_sdfgi_probes(rb, color_only_framebuffer, p_render_data->scene_data->view_count, cms);
|
||||
}
|
||||
|
||||
if (draw_sky || draw_sky_fog_only) {
|
||||
RENDER_TIMESTAMP("Render Sky");
|
||||
|
||||
RD::get_singleton()->draw_command_begin_label("Draw Sky");
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::INITIAL_ACTION_CONTINUE, can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ);
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE);
|
||||
|
||||
sky.draw_sky(draw_list, rb, p_render_data->environment, color_only_framebuffer, time, sky_energy_multiplier);
|
||||
|
||||
@@ -2146,7 +2085,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
|
||||
RENDER_TIMESTAMP("Clear Separate Specular (Canvas Background Mode)");
|
||||
Vector<Color> blank_clear_color;
|
||||
blank_clear_color.push_back(Color(0.0, 0.0, 0.0));
|
||||
RD::get_singleton()->draw_list_begin(rb_data->get_specular_only_fb(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, blank_clear_color);
|
||||
RD::get_singleton()->draw_list_begin(rb_data->get_specular_only_fb(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, blank_clear_color);
|
||||
RD::get_singleton()->draw_list_end();
|
||||
}
|
||||
|
||||
@@ -2187,7 +2126,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
|
||||
|
||||
RID alpha_framebuffer = rb_data.is_valid() ? rb_data->get_color_pass_fb(transparent_color_pass_flags) : color_only_framebuffer;
|
||||
RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), false, PASS_MODE_COLOR, transparent_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count);
|
||||
_render_list_with_threads(&render_list_params, alpha_framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ);
|
||||
_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);
|
||||
}
|
||||
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
@@ -2226,7 +2165,9 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
|
||||
exposure = luminance->get_current_luminance_buffer(rb);
|
||||
}
|
||||
|
||||
RD::get_singleton()->draw_command_begin_label("FSR2");
|
||||
RENDER_TIMESTAMP("FSR2");
|
||||
|
||||
for (uint32_t v = 0; v < rb->get_view_count(); v++) {
|
||||
real_t fov = p_render_data->scene_data->cam_projection.get_fov();
|
||||
real_t aspect = p_render_data->scene_data->cam_projection.get_aspect();
|
||||
@@ -2257,9 +2198,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
|
||||
|
||||
fsr2_effect->upscale(params);
|
||||
}
|
||||
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
} else if (using_taa) {
|
||||
RD::get_singleton()->draw_command_begin_label("TAA");
|
||||
RENDER_TIMESTAMP("TAA");
|
||||
taa->process(rb, _render_buffers_get_color_format(), p_render_data->scene_data->z_near, p_render_data->scene_data->z_far);
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2571,8 +2516,7 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page
|
||||
shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier;
|
||||
|
||||
shadow_pass.framebuffer = p_framebuffer;
|
||||
shadow_pass.initial_depth_action = p_begin ? (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION : RD::INITIAL_ACTION_CLEAR) : (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION_CONTINUE : RD::INITIAL_ACTION_CONTINUE);
|
||||
shadow_pass.final_depth_action = p_end ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE;
|
||||
shadow_pass.initial_depth_action = p_begin ? RD::INITIAL_ACTION_CLEAR : (p_clear_region ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD);
|
||||
shadow_pass.rect = p_rect;
|
||||
|
||||
scene_state.shadow_passes.push_back(shadow_pass);
|
||||
@@ -2591,17 +2535,14 @@ void RenderForwardClustered::_render_shadow_process() {
|
||||
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
}
|
||||
void RenderForwardClustered::_render_shadow_end(uint32_t p_barrier) {
|
||||
void RenderForwardClustered::_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, 0, true, false, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from, RD::BARRIER_MASK_NO_BARRIER);
|
||||
_render_list_with_threads(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, shadow_pass.final_depth_action, Vector<Color>(), 1.0, 0, shadow_pass.rect);
|
||||
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, 0, true, false, shadow_pass.rp_uniform_set, 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>(), 1.0, 0, shadow_pass.rect);
|
||||
}
|
||||
|
||||
if (p_barrier != RD::BARRIER_MASK_NO_BARRIER) {
|
||||
RD::get_singleton()->barrier(RD::BARRIER_MASK_RASTER, p_barrier);
|
||||
}
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
}
|
||||
|
||||
@@ -2644,7 +2585,7 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con
|
||||
{
|
||||
//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, 0, true, false, rp_uniform_set);
|
||||
_render_list_with_threads(&render_list_params, p_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ);
|
||||
_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();
|
||||
}
|
||||
@@ -2697,7 +2638,7 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform
|
||||
Color(0, 0, 0, 0)
|
||||
};
|
||||
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, clear, 1.0, 0, p_region);
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 1.0, 0, p_region);
|
||||
_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count);
|
||||
RD::get_singleton()->draw_list_end();
|
||||
}
|
||||
@@ -2747,7 +2688,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance
|
||||
Color(0, 0, 0, 0),
|
||||
Color(0, 0, 0, 0)
|
||||
};
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, clear, 1.0, 0, p_region);
|
||||
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 1.0, 0, p_region);
|
||||
|
||||
const int uv_offset_count = 9;
|
||||
static const Vector2 uv_offsets[uv_offset_count] = {
|
||||
@@ -2803,13 +2744,6 @@ void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_bu
|
||||
Vector3 half_size = p_bounds.size * 0.5;
|
||||
Vector3 center = p_bounds.position + half_size;
|
||||
|
||||
Vector<RID> sbs = {
|
||||
p_albedo_texture,
|
||||
p_emission_texture,
|
||||
p_emission_aniso_texture,
|
||||
p_geom_facing_texture
|
||||
};
|
||||
|
||||
//print_line("re-render " + p_from + " - " + p_size + " bounds " + p_bounds);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
scene_state.ubo.sdf_offset[i] = p_from[i];
|
||||
@@ -2860,7 +2794,7 @@ void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_bu
|
||||
}
|
||||
|
||||
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, 0, true, false, rp_uniform_set, false);
|
||||
_render_list_with_threads(&render_list_params, E->value, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, Rect2(), sbs);
|
||||
_render_list_with_draw_list(&render_list_params, E->value, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, Rect2());
|
||||
}
|
||||
|
||||
RD::get_singleton()->draw_command_end_label();
|
||||
@@ -4280,8 +4214,6 @@ RenderForwardClustered::RenderForwardClustered() {
|
||||
best_fit_normal.shader.version_free(best_fit_normal.shader_version);
|
||||
}
|
||||
|
||||
render_list_thread_threshold = GLOBAL_GET("rendering/limits/forward_renderer/threaded_render_minimum_instances");
|
||||
|
||||
_update_shader_quality_settings();
|
||||
|
||||
resolve_effects = memnew(RendererRD::Resolve());
|
||||
|
||||
@@ -209,10 +209,9 @@ class RenderForwardClustered : public RendererSceneRenderRD {
|
||||
float screen_mesh_lod_threshold = 0.0;
|
||||
RD::FramebufferFormatID framebuffer_format = 0;
|
||||
uint32_t element_offset = 0;
|
||||
uint32_t barrier = RD::BARRIER_MASK_ALL_BARRIERS;
|
||||
bool use_directional_soft_shadow = false;
|
||||
|
||||
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_barrier = RD::BARRIER_MASK_ALL_BARRIERS) {
|
||||
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) {
|
||||
elements = p_elements;
|
||||
element_info = p_element_info;
|
||||
element_count = p_element_count;
|
||||
@@ -227,7 +226,6 @@ class RenderForwardClustered : public RendererSceneRenderRD {
|
||||
lod_distance_multiplier = p_lod_distance_multiplier;
|
||||
screen_mesh_lod_threshold = p_screen_mesh_lod_threshold;
|
||||
element_offset = p_element_offset;
|
||||
barrier = p_barrier;
|
||||
use_directional_soft_shadow = p_use_directional_soft_shadows;
|
||||
}
|
||||
};
|
||||
@@ -352,7 +350,6 @@ class RenderForwardClustered : public RendererSceneRenderRD {
|
||||
|
||||
RID framebuffer;
|
||||
RD::InitialAction initial_depth_action;
|
||||
RD::FinalAction final_depth_action;
|
||||
Rect2i rect;
|
||||
};
|
||||
|
||||
@@ -378,14 +375,8 @@ class RenderForwardClustered : public RendererSceneRenderRD {
|
||||
|
||||
template <PassMode p_pass_mode, uint32_t p_color_pass_flags = 0>
|
||||
_FORCE_INLINE_ void _render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
|
||||
|
||||
void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element);
|
||||
|
||||
LocalVector<RD::DrawListID> thread_draw_lists;
|
||||
void _render_list_thread_function(uint32_t p_thread, RenderListParameters *p_params);
|
||||
void _render_list_with_threads(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>());
|
||||
|
||||
uint32_t render_list_thread_threshold = 500;
|
||||
void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
|
||||
|
||||
void _update_instance_data_buffer(RenderListType p_render_list);
|
||||
void _fill_instance_data(RenderListType p_render_list, int *p_render_info = nullptr, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true);
|
||||
@@ -604,7 +595,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
|
||||
void _render_shadow_begin();
|
||||
void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1));
|
||||
void _render_shadow_process();
|
||||
void _render_shadow_end(uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS);
|
||||
void _render_shadow_end();
|
||||
|
||||
/* Render Scene */
|
||||
void _process_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, const RID *p_normal_buffers, const Projection *p_projections);
|
||||
|
||||
@@ -446,7 +446,7 @@ 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, RD::BARRIER_MASK_RASTER);
|
||||
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);
|
||||
}
|
||||
|
||||
SceneShaderForwardClustered::MaterialData::~MaterialData() {
|
||||
|
||||
Reference in New Issue
Block a user