From f930d71a1ed121b5055f3c40d1525f3ed5c15423 Mon Sep 17 00:00:00 2001 From: Miguel Pozo Date: Thu, 27 Feb 2025 19:20:33 +0100 Subject: [PATCH] GPU: Threadsafe shader creation and acquisition Move the `StaticShader` class from Workbench to `GPU_shader` and make compilation thread-safe (Shader usage is still not thread-safe). Use `StaticShader`s for all shader caches. Subdivision shaders are still not ported. (Part of #134690) Pull Request: https://projects.blender.org/blender/blender/pulls/134812 --- .../draw/engines/eevee_next/eevee_shader.cc | 54 ++-- .../draw/engines/eevee_next/eevee_shader.hh | 12 +- .../draw/engines/gpencil/gpencil_shader_c.cc | 155 ++++------- .../engines/overlay/overlay_next_private.hh | 243 +++++++++--------- .../engines/overlay/overlay_next_shader.cc | 28 +- .../engines/select/select_debug_engine.cc | 40 ++- .../engines/workbench/workbench_private.hh | 47 ++-- .../workbench/workbench_shader_cache.cc | 18 -- source/blender/draw/intern/draw_shader.cc | 114 ++++---- source/blender/gpu/GPU_shader.hh | 123 +++++++++ source/blender/gpu/intern/gpu_shader.cc | 6 + .../blender/gpu/intern/gpu_shader_private.hh | 4 +- 12 files changed, 439 insertions(+), 405 deletions(-) diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index cc078f8f221..e02629162b4 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -29,39 +29,31 @@ namespace blender::eevee { * * \{ */ -ShaderModule *ShaderModule::g_shader_module = nullptr; - ShaderModule *ShaderModule::module_get() { - if (g_shader_module == nullptr) { - /* TODO(@fclem) thread-safety. */ - g_shader_module = new ShaderModule(); - } - return g_shader_module; + return &get_static_cache().get(); } void ShaderModule::module_free() { - if (g_shader_module != nullptr) { - /* TODO(@fclem) thread-safety. */ - delete g_shader_module; - g_shader_module = nullptr; - } + get_static_cache().release(); } ShaderModule::ShaderModule() { - for (GPUShader *&shader : shaders_) { - shader = nullptr; - } - Vector infos; infos.reserve(MAX_SHADER_TYPE); for (auto i : IndexRange(MAX_SHADER_TYPE)) { const char *name = static_shader_create_info_name_get(eShaderType(i)); const GPUShaderCreateInfo *create_info = GPU_shader_create_info_get(name); - infos.append(create_info); + + if (GPU_use_parallel_compilation()) { + infos.append(create_info); + } + else { + shaders_[i] = {name}; + } #ifndef NDEBUG if (name == nullptr) { @@ -84,10 +76,6 @@ ShaderModule::~ShaderModule() /* Finish compilation to avoid asserts on exit at GLShaderCompiler destructor. */ is_ready(true); } - - for (GPUShader *&shader : shaders_) { - GPU_SHADER_FREE_SAFE(shader); - } } /** \} */ @@ -101,8 +89,6 @@ void ShaderModule::precompile_specializations(int render_buffers_shadow_id, int shadow_ray_count, int shadow_ray_step_count) { - BLI_assert(specialization_handle_ == 0); - if (!GPU_use_parallel_compilation()) { return; } @@ -125,16 +111,22 @@ void ShaderModule::precompile_specializations(int render_buffers_shadow_id, } } + std::lock_guard lock = get_static_cache().lock_guard(); + /* TODO: This is broken. Different scenes can have different specializations. */ + BLI_assert(specialization_handle_ == 0); + specialization_handle_ = GPU_shader_batch_specializations(specializations); } bool ShaderModule::is_ready(bool block) { + std::lock_guard lock = get_static_cache().lock_guard(); + if (compilation_handle_) { if (GPU_shader_batch_is_ready(compilation_handle_) || block) { Vector shaders = GPU_shader_batch_finalize(compilation_handle_); for (int i : IndexRange(MAX_SHADER_TYPE)) { - shaders_[i] = shaders[i]; + shaders_[i].set(shaders[i]); } } } @@ -385,17 +377,13 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ GPUShader *ShaderModule::static_shader_get(eShaderType shader_type) { BLI_assert(is_ready()); - if (shaders_[shader_type] == nullptr) { + if (GPU_use_parallel_compilation() && shaders_[shader_type].get() == nullptr) { const char *shader_name = static_shader_create_info_name_get(shader_type); - if (GPU_use_parallel_compilation()) { - fprintf(stderr, "EEVEE: error: Could not compile static shader \"%s\"\n", shader_name); - BLI_assert(0); - } - else { - shaders_[shader_type] = GPU_shader_create_from_info_name(shader_name); - } + fprintf(stderr, "EEVEE: error: Could not compile static shader \"%s\"\n", shader_name); + BLI_assert(0); } - return shaders_[shader_type]; + + return shaders_[shader_type].get(); } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 16e09ebed88..e7ec6629005 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -24,6 +24,8 @@ namespace blender::eevee { +using StaticShader = gpu::StaticShader; + /* Keep alphabetical order and clean prefix. */ enum eShaderType { AMBIENT_OCCLUSION_PASS = 0, @@ -166,12 +168,16 @@ enum eShaderType { */ class ShaderModule { private: - std::array shaders_; + std::array shaders_; BatchHandle compilation_handle_ = 0; SpecializationBatchHandle specialization_handle_ = 0; - /** Shared shader module across all engine instances. */ - static ShaderModule *g_shader_module; + static gpu::StaticShaderCache &get_static_cache() + { + /** Shared shader module across all engine instances. */ + static gpu::StaticShaderCache static_cache; + return static_cache; + } public: ShaderModule(); diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_c.cc b/source/blender/draw/engines/gpencil/gpencil_shader_c.cc index 65cbe313542..81db93fe911 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader_c.cc +++ b/source/blender/draw/engines/gpencil/gpencil_shader_c.cc @@ -11,171 +11,124 @@ #include "gpencil_engine.h" -extern "C" char datatoc_gpencil_common_lib_glsl[]; -extern "C" char datatoc_gpencil_frag_glsl[]; -extern "C" char datatoc_gpencil_vert_glsl[]; -extern "C" char datatoc_gpencil_antialiasing_frag_glsl[]; -extern "C" char datatoc_gpencil_antialiasing_vert_glsl[]; -extern "C" char datatoc_gpencil_layer_blend_frag_glsl[]; -extern "C" char datatoc_gpencil_mask_invert_frag_glsl[]; -extern "C" char datatoc_gpencil_depth_merge_frag_glsl[]; -extern "C" char datatoc_gpencil_depth_merge_vert_glsl[]; -extern "C" char datatoc_gpencil_vfx_frag_glsl[]; +namespace blender::draw::gpencil { -extern "C" char datatoc_common_colormanagement_lib_glsl[]; -extern "C" char datatoc_common_fullscreen_vert_glsl[]; -extern "C" char datatoc_common_view_lib_glsl[]; +using StaticShader = gpu::StaticShader; + +class ShaderCache { + private: + static gpu::StaticShaderCache &get_static_cache() + { + static gpu::StaticShaderCache static_cache; + return static_cache; + } + + public: + static ShaderCache &get() + { + return get_static_cache().get(); + } + static void release() + { + get_static_cache().release(); + } -static struct { /* SMAA antialiasing */ - GPUShader *antialiasing_sh[3]; + StaticShader antialiasing[3] = {{"gpencil_antialiasing_stage_0"}, + {"gpencil_antialiasing_stage_1"}, + {"gpencil_antialiasing_stage_2"}}; /* GPencil Object rendering */ - GPUShader *gpencil_sh; - /* Final Compositing over rendered background. */ - GPUShader *composite_sh; + StaticShader geometry = {"gpencil_geometry"}; /* All layer blend types in one shader! */ - GPUShader *layer_blend_sh; + StaticShader layer_blend = {"gpencil_layer_blend"}; /* Merge the final object depth to the depth buffer. */ - GPUShader *depth_merge_sh; + StaticShader depth_merge = {"gpencil_depth_merge"}; /* Invert the content of the mask buffer. */ - GPUShader *mask_invert_sh; + StaticShader mask_invert = {"gpencil_mask_invert"}; /* Effects. */ - GPUShader *fx_composite_sh; - GPUShader *fx_colorize_sh; - GPUShader *fx_blur_sh; - GPUShader *fx_glow_sh; - GPUShader *fx_pixel_sh; - GPUShader *fx_rim_sh; - GPUShader *fx_shadow_sh; - GPUShader *fx_transform_sh; -} g_shaders = {{nullptr}}; + StaticShader fx_composite = {"gpencil_fx_composite"}; + StaticShader fx_colorize = {"gpencil_fx_colorize"}; + StaticShader fx_blur = {"gpencil_fx_blur"}; + StaticShader fx_glow = {"gpencil_fx_glow"}; + StaticShader fx_pixelize = {"gpencil_fx_pixelize"}; + StaticShader fx_rim = {"gpencil_fx_rim"}; + StaticShader fx_shadow = {"gpencil_fx_shadow"}; + StaticShader fx_transform = {"gpencil_fx_transform"}; +}; + +} // namespace blender::draw::gpencil + +using namespace blender::draw::gpencil; void GPENCIL_shader_free() { - GPU_SHADER_FREE_SAFE(g_shaders.antialiasing_sh[0]); - GPU_SHADER_FREE_SAFE(g_shaders.antialiasing_sh[1]); - GPU_SHADER_FREE_SAFE(g_shaders.antialiasing_sh[2]); - GPU_SHADER_FREE_SAFE(g_shaders.gpencil_sh); - GPU_SHADER_FREE_SAFE(g_shaders.composite_sh); - GPU_SHADER_FREE_SAFE(g_shaders.layer_blend_sh); - GPU_SHADER_FREE_SAFE(g_shaders.depth_merge_sh); - GPU_SHADER_FREE_SAFE(g_shaders.mask_invert_sh); - GPU_SHADER_FREE_SAFE(g_shaders.fx_composite_sh); - GPU_SHADER_FREE_SAFE(g_shaders.fx_colorize_sh); - GPU_SHADER_FREE_SAFE(g_shaders.fx_blur_sh); - GPU_SHADER_FREE_SAFE(g_shaders.fx_glow_sh); - GPU_SHADER_FREE_SAFE(g_shaders.fx_pixel_sh); - GPU_SHADER_FREE_SAFE(g_shaders.fx_rim_sh); - GPU_SHADER_FREE_SAFE(g_shaders.fx_shadow_sh); - GPU_SHADER_FREE_SAFE(g_shaders.fx_transform_sh); + ShaderCache::get().release(); } GPUShader *GPENCIL_shader_antialiasing(int stage) { BLI_assert(stage < 3); - - if (!g_shaders.antialiasing_sh[stage]) { - char stage_info_name[32]; - SNPRINTF(stage_info_name, "gpencil_antialiasing_stage_%d", stage); - g_shaders.antialiasing_sh[stage] = GPU_shader_create_from_info_name(stage_info_name); - } - return g_shaders.antialiasing_sh[stage]; + return ShaderCache::get().antialiasing[stage].get(); } GPUShader *GPENCIL_shader_geometry_get() { - if (!g_shaders.gpencil_sh) { - g_shaders.gpencil_sh = GPU_shader_create_from_info_name("gpencil_geometry"); - } - return g_shaders.gpencil_sh; + return ShaderCache::get().geometry.get(); } GPUShader *GPENCIL_shader_layer_blend_get() { - if (!g_shaders.layer_blend_sh) { - g_shaders.layer_blend_sh = GPU_shader_create_from_info_name("gpencil_layer_blend"); - } - return g_shaders.layer_blend_sh; + return ShaderCache::get().layer_blend.get(); } GPUShader *GPENCIL_shader_mask_invert_get() { - if (!g_shaders.mask_invert_sh) { - g_shaders.mask_invert_sh = GPU_shader_create_from_info_name("gpencil_mask_invert"); - } - return g_shaders.mask_invert_sh; + return ShaderCache::get().mask_invert.get(); } GPUShader *GPENCIL_shader_depth_merge_get() { - if (!g_shaders.depth_merge_sh) { - g_shaders.depth_merge_sh = GPU_shader_create_from_info_name("gpencil_depth_merge"); - } - return g_shaders.depth_merge_sh; + return ShaderCache::get().depth_merge.get(); } /* ------- FX Shaders --------- */ GPUShader *GPENCIL_shader_fx_blur_get() { - if (!g_shaders.fx_blur_sh) { - g_shaders.fx_blur_sh = GPU_shader_create_from_info_name("gpencil_fx_blur"); - } - return g_shaders.fx_blur_sh; + return ShaderCache::get().fx_blur.get(); } GPUShader *GPENCIL_shader_fx_colorize_get() { - if (!g_shaders.fx_colorize_sh) { - g_shaders.fx_colorize_sh = GPU_shader_create_from_info_name("gpencil_fx_colorize"); - } - return g_shaders.fx_colorize_sh; + return ShaderCache::get().fx_colorize.get(); } GPUShader *GPENCIL_shader_fx_composite_get() { - if (!g_shaders.fx_composite_sh) { - g_shaders.fx_composite_sh = GPU_shader_create_from_info_name("gpencil_fx_composite"); - } - return g_shaders.fx_composite_sh; + return ShaderCache::get().fx_composite.get(); } GPUShader *GPENCIL_shader_fx_glow_get() { - if (!g_shaders.fx_glow_sh) { - g_shaders.fx_glow_sh = GPU_shader_create_from_info_name("gpencil_fx_glow"); - } - return g_shaders.fx_glow_sh; + return ShaderCache::get().fx_glow.get(); } GPUShader *GPENCIL_shader_fx_pixelize_get() { - if (!g_shaders.fx_pixel_sh) { - g_shaders.fx_pixel_sh = GPU_shader_create_from_info_name("gpencil_fx_pixelize"); - } - return g_shaders.fx_pixel_sh; + return ShaderCache::get().fx_pixelize.get(); } GPUShader *GPENCIL_shader_fx_rim_get() { - if (!g_shaders.fx_rim_sh) { - g_shaders.fx_rim_sh = GPU_shader_create_from_info_name("gpencil_fx_rim"); - } - return g_shaders.fx_rim_sh; + return ShaderCache::get().fx_rim.get(); } GPUShader *GPENCIL_shader_fx_shadow_get() { - if (!g_shaders.fx_shadow_sh) { - g_shaders.fx_shadow_sh = GPU_shader_create_from_info_name("gpencil_fx_shadow"); - } - return g_shaders.fx_shadow_sh; + return ShaderCache::get().fx_shadow.get(); } GPUShader *GPENCIL_shader_fx_transform_get() { - if (!g_shaders.fx_transform_sh) { - g_shaders.fx_transform_sh = GPU_shader_create_from_info_name("gpencil_fx_transform"); - } - return g_shaders.fx_transform_sh; + return ShaderCache::get().fx_transform.get(); } diff --git a/source/blender/draw/engines/overlay/overlay_next_private.hh b/source/blender/draw/engines/overlay/overlay_next_private.hh index 9b15b2e0fac..9238850f49c 100644 --- a/source/blender/draw/engines/overlay/overlay_next_private.hh +++ b/source/blender/draw/engines/overlay/overlay_next_private.hh @@ -447,21 +447,25 @@ class ShapeCache { } }; +using StaticShader = gpu::StaticShader; + /** * Shader module. Shared between instances. */ class ShaderModule { private: - struct ShaderDeleter { - void operator()(GPUShader *shader) - { - GPU_SHADER_FREE_SAFE(shader); - } - }; - using ShaderPtr = std::unique_ptr; + /* Allow StaticShaderCache access to the constructor. */ + friend gpu::StaticShaderCache; - /** Shared shader module across all engine instances. */ - static ShaderModule *g_shader_modules[2 /* Selection Instance. */][2 /* Clipping Enabled. */]; + using StaticCache = + gpu::StaticShaderCache[2 /* Selection Instance. */][2 /* Clipping Enabled. */]; + + static StaticCache &get_static_cache() + { + /** Shared shader module across all engine instances. */ + static StaticCache static_cache; + return static_cache; + } const SelectionType selection_type_; /** TODO: Support clipping. This global state should be set by the overlay::Instance and switch @@ -470,116 +474,119 @@ class ShaderModule { public: /** Shaders */ - ShaderPtr anti_aliasing = shader("overlay_antialiasing"); - ShaderPtr armature_degrees_of_freedom = shader_clippable("overlay_armature_dof"); - ShaderPtr attribute_viewer_mesh = shader_clippable("overlay_viewer_attribute_mesh"); - ShaderPtr attribute_viewer_pointcloud = shader_clippable("overlay_viewer_attribute_pointcloud"); - ShaderPtr attribute_viewer_curve = shader_clippable("overlay_viewer_attribute_curve"); - ShaderPtr attribute_viewer_curves = shader_clippable("overlay_viewer_attribute_curves"); - ShaderPtr background_fill = shader("overlay_background"); - ShaderPtr background_clip_bound = shader("overlay_clipbound"); - ShaderPtr curve_edit_points = shader_clippable("overlay_edit_curves_point"); - ShaderPtr curve_edit_line = shader_clippable("overlay_edit_particle_strand"); - ShaderPtr curve_edit_handles = shader_clippable("overlay_edit_curves_handle"); - ShaderPtr facing = shader_clippable("overlay_facing"); - ShaderPtr grid = shader("overlay_grid_next"); - ShaderPtr grid_background = shader("overlay_grid_background"); - ShaderPtr grid_grease_pencil = shader_clippable("overlay_gpencil_canvas"); - ShaderPtr grid_image = shader("overlay_grid_image"); - ShaderPtr lattice_points = shader_clippable("overlay_edit_lattice_point"); - ShaderPtr lattice_wire = shader_clippable("overlay_edit_lattice_wire"); - ShaderPtr legacy_curve_edit_handles = shader_clippable("overlay_edit_curve_handle"); - ShaderPtr legacy_curve_edit_normals = shader_clippable("overlay_edit_curve_normals"); - ShaderPtr legacy_curve_edit_points = shader_clippable("overlay_edit_curve_point"); - ShaderPtr legacy_curve_edit_wires = shader_clippable("overlay_edit_curve_wire"); - ShaderPtr light_spot_cone = shader_clippable("overlay_extra_spot_cone"); - ShaderPtr mesh_analysis = shader_clippable("overlay_edit_mesh_analysis"); - ShaderPtr mesh_edit_depth = shader_clippable("overlay_edit_mesh_depth"); - ShaderPtr mesh_edit_edge = shader_clippable("overlay_edit_mesh_edge"); - ShaderPtr mesh_edit_face = shader_clippable("overlay_edit_mesh_face"); - ShaderPtr mesh_edit_facedot = shader_clippable("overlay_edit_mesh_facedot"); - ShaderPtr mesh_edit_vert = shader_clippable("overlay_edit_mesh_vert"); - ShaderPtr mesh_edit_skin_root = shader_clippable("overlay_edit_mesh_skin_root"); - ShaderPtr mesh_face_normal = shader_clippable("overlay_mesh_face_normal"); - ShaderPtr mesh_face_normal_subdiv = shader_clippable("overlay_mesh_face_normal_subdiv"); - ShaderPtr mesh_loop_normal = shader_clippable("overlay_mesh_loop_normal"); - ShaderPtr mesh_loop_normal_subdiv = shader_clippable("overlay_mesh_loop_normal_subdiv"); - ShaderPtr mesh_vert_normal = shader_clippable("overlay_mesh_vert_normal"); - ShaderPtr mesh_vert_normal_subdiv = shader_clippable("overlay_mesh_vert_normal_subdiv"); - ShaderPtr motion_path_line = shader_clippable("overlay_motion_path_line"); - ShaderPtr motion_path_vert = shader_clippable("overlay_motion_path_point"); - ShaderPtr outline_detect = shader("overlay_outline_detect"); - ShaderPtr outline_prepass_curves = shader_clippable("overlay_outline_prepass_curves"); - ShaderPtr outline_prepass_gpencil = shader_clippable("overlay_outline_prepass_gpencil"); - ShaderPtr outline_prepass_mesh = shader_clippable("overlay_outline_prepass_mesh"); - ShaderPtr outline_prepass_pointcloud = shader_clippable("overlay_outline_prepass_pointcloud"); - ShaderPtr outline_prepass_wire = shader_clippable("overlay_outline_prepass_wire"); - ShaderPtr paint_region_edge = shader_clippable("overlay_paint_wire"); - ShaderPtr paint_region_face = shader_clippable("overlay_paint_face"); - ShaderPtr paint_region_vert = shader_clippable("overlay_paint_point"); - ShaderPtr paint_texture = shader_clippable("overlay_paint_texture"); - ShaderPtr paint_weight = shader_clippable("overlay_paint_weight"); + StaticShader anti_aliasing = {"overlay_antialiasing"}; + StaticShader armature_degrees_of_freedom = shader_clippable("overlay_armature_dof"); + StaticShader attribute_viewer_mesh = shader_clippable("overlay_viewer_attribute_mesh"); + StaticShader attribute_viewer_pointcloud = shader_clippable( + "overlay_viewer_attribute_pointcloud"); + StaticShader attribute_viewer_curve = shader_clippable("overlay_viewer_attribute_curve"); + StaticShader attribute_viewer_curves = shader_clippable("overlay_viewer_attribute_curves"); + StaticShader background_fill = {"overlay_background"}; + StaticShader background_clip_bound = {"overlay_clipbound"}; + StaticShader curve_edit_points = shader_clippable("overlay_edit_curves_point"); + StaticShader curve_edit_line = shader_clippable("overlay_edit_particle_strand"); + StaticShader curve_edit_handles = shader_clippable("overlay_edit_curves_handle"); + StaticShader facing = shader_clippable("overlay_facing"); + StaticShader grid = {"overlay_grid_next"}; + StaticShader grid_background = {"overlay_grid_background"}; + StaticShader grid_grease_pencil = shader_clippable("overlay_gpencil_canvas"); + StaticShader grid_image = {"overlay_grid_image"}; + StaticShader lattice_points = shader_clippable("overlay_edit_lattice_point"); + StaticShader lattice_wire = shader_clippable("overlay_edit_lattice_wire"); + StaticShader legacy_curve_edit_handles = shader_clippable("overlay_edit_curve_handle"); + StaticShader legacy_curve_edit_normals = shader_clippable("overlay_edit_curve_normals"); + StaticShader legacy_curve_edit_points = shader_clippable("overlay_edit_curve_point"); + StaticShader legacy_curve_edit_wires = shader_clippable("overlay_edit_curve_wire"); + StaticShader light_spot_cone = shader_clippable("overlay_extra_spot_cone"); + StaticShader mesh_analysis = shader_clippable("overlay_edit_mesh_analysis"); + StaticShader mesh_edit_depth = shader_clippable("overlay_edit_mesh_depth"); + StaticShader mesh_edit_edge = shader_clippable("overlay_edit_mesh_edge"); + StaticShader mesh_edit_face = shader_clippable("overlay_edit_mesh_face"); + StaticShader mesh_edit_facedot = shader_clippable("overlay_edit_mesh_facedot"); + StaticShader mesh_edit_vert = shader_clippable("overlay_edit_mesh_vert"); + StaticShader mesh_edit_skin_root = shader_clippable("overlay_edit_mesh_skin_root"); + StaticShader mesh_face_normal = shader_clippable("overlay_mesh_face_normal"); + StaticShader mesh_face_normal_subdiv = shader_clippable("overlay_mesh_face_normal_subdiv"); + StaticShader mesh_loop_normal = shader_clippable("overlay_mesh_loop_normal"); + StaticShader mesh_loop_normal_subdiv = shader_clippable("overlay_mesh_loop_normal_subdiv"); + StaticShader mesh_vert_normal = shader_clippable("overlay_mesh_vert_normal"); + StaticShader mesh_vert_normal_subdiv = shader_clippable("overlay_mesh_vert_normal_subdiv"); + StaticShader motion_path_line = shader_clippable("overlay_motion_path_line"); + StaticShader motion_path_vert = shader_clippable("overlay_motion_path_point"); + StaticShader outline_detect = {"overlay_outline_detect"}; + StaticShader outline_prepass_curves = shader_clippable("overlay_outline_prepass_curves"); + StaticShader outline_prepass_gpencil = shader_clippable("overlay_outline_prepass_gpencil"); + StaticShader outline_prepass_mesh = shader_clippable("overlay_outline_prepass_mesh"); + StaticShader outline_prepass_pointcloud = shader_clippable("overlay_outline_prepass_pointcloud"); + StaticShader outline_prepass_wire = shader_clippable("overlay_outline_prepass_wire"); + StaticShader paint_region_edge = shader_clippable("overlay_paint_wire"); + StaticShader paint_region_face = shader_clippable("overlay_paint_face"); + StaticShader paint_region_vert = shader_clippable("overlay_paint_point"); + StaticShader paint_texture = shader_clippable("overlay_paint_texture"); + StaticShader paint_weight = shader_clippable("overlay_paint_weight"); /* TODO(fclem): Specialization constant. */ - ShaderPtr paint_weight_fake_shading = shader_clippable("overlay_paint_weight_fake_shading"); - ShaderPtr particle_edit_vert = shader_clippable("overlay_edit_particle_point"); - ShaderPtr particle_edit_edge = shader_clippable("overlay_edit_particle_strand"); - ShaderPtr pointcloud_points = shader_clippable("overlay_edit_pointcloud"); - ShaderPtr sculpt_curves = shader_clippable("overlay_sculpt_curves_selection"); - ShaderPtr sculpt_curves_cage = shader_clippable("overlay_sculpt_curves_cage"); - ShaderPtr sculpt_mesh = shader_clippable("overlay_sculpt_mask"); - ShaderPtr uniform_color = shader_clippable("overlay_uniform_color"); - ShaderPtr uv_analysis_stretch_angle = shader("overlay_edit_uv_stretching_angle"); - ShaderPtr uv_analysis_stretch_area = shader("overlay_edit_uv_stretching_area"); - ShaderPtr uv_brush_stencil = shader("overlay_edit_uv_stencil_image"); - ShaderPtr uv_edit_edge = shader("overlay_edit_uv_edges"); - ShaderPtr uv_edit_face = shader("overlay_edit_uv_faces"); - ShaderPtr uv_edit_facedot = shader("overlay_edit_uv_face_dots"); - ShaderPtr uv_edit_vert = shader("overlay_edit_uv_verts"); - ShaderPtr uv_image_borders = shader("overlay_edit_uv_tiled_image_borders"); - ShaderPtr uv_paint_mask = shader("overlay_edit_uv_mask_image"); - ShaderPtr uv_wireframe = shader("overlay_wireframe_uv"); - ShaderPtr xray_fade = shader("overlay_xray_fade"); + StaticShader paint_weight_fake_shading = shader_clippable("overlay_paint_weight_fake_shading"); + StaticShader particle_edit_vert = shader_clippable("overlay_edit_particle_point"); + StaticShader particle_edit_edge = shader_clippable("overlay_edit_particle_strand"); + StaticShader pointcloud_points = shader_clippable("overlay_edit_pointcloud"); + StaticShader sculpt_curves = shader_clippable("overlay_sculpt_curves_selection"); + StaticShader sculpt_curves_cage = shader_clippable("overlay_sculpt_curves_cage"); + StaticShader sculpt_mesh = shader_clippable("overlay_sculpt_mask"); + StaticShader uniform_color = shader_clippable("overlay_uniform_color"); + StaticShader uv_analysis_stretch_angle = {"overlay_edit_uv_stretching_angle"}; + StaticShader uv_analysis_stretch_area = {"overlay_edit_uv_stretching_area"}; + StaticShader uv_brush_stencil = {"overlay_edit_uv_stencil_image"}; + StaticShader uv_edit_edge = {"overlay_edit_uv_edges"}; + StaticShader uv_edit_face = {"overlay_edit_uv_faces"}; + StaticShader uv_edit_facedot = {"overlay_edit_uv_face_dots"}; + StaticShader uv_edit_vert = {"overlay_edit_uv_verts"}; + StaticShader uv_image_borders = {"overlay_edit_uv_tiled_image_borders"}; + StaticShader uv_paint_mask = {"overlay_edit_uv_mask_image"}; + StaticShader uv_wireframe = {"overlay_wireframe_uv"}; + StaticShader xray_fade = {"overlay_xray_fade"}; /** Selectable Shaders */ - ShaderPtr armature_envelope_fill = shader_selectable("overlay_armature_envelope_solid"); - ShaderPtr armature_envelope_outline = shader_selectable("overlay_armature_envelope_outline"); - ShaderPtr armature_shape_outline = shader_selectable("overlay_armature_shape_outline"); - ShaderPtr armature_shape_fill = shader_selectable("overlay_armature_shape_solid"); - ShaderPtr armature_shape_wire = shader_selectable("overlay_armature_shape_wire"); - ShaderPtr armature_shape_wire_strip = shader_selectable("overlay_armature_shape_wire_strip"); - ShaderPtr armature_sphere_outline = shader_selectable("overlay_armature_sphere_outline"); - ShaderPtr armature_sphere_fill = shader_selectable("overlay_armature_sphere_solid"); - ShaderPtr armature_stick = shader_selectable("overlay_armature_stick"); - ShaderPtr armature_wire = shader_selectable("overlay_armature_wire"); - ShaderPtr depth_curves = shader_selectable("overlay_depth_curves"); - ShaderPtr depth_grease_pencil = shader_selectable("overlay_depth_gpencil"); - ShaderPtr depth_mesh = shader_selectable("overlay_depth_mesh"); - ShaderPtr depth_mesh_conservative = shader_selectable("overlay_depth_mesh_conservative"); - ShaderPtr depth_pointcloud = shader_selectable("overlay_depth_pointcloud"); - ShaderPtr extra_shape = shader_selectable("overlay_extra"); - ShaderPtr extra_point = shader_selectable("overlay_extra_point"); - ShaderPtr extra_wire = shader_selectable("overlay_extra_wire"); - ShaderPtr extra_wire_object = shader_selectable("overlay_extra_wire_object"); - ShaderPtr extra_loose_points = shader_selectable("overlay_extra_loose_point"); - ShaderPtr extra_grid = shader_selectable("overlay_extra_grid"); - ShaderPtr extra_ground_line = shader_selectable("overlay_extra_groundline"); - ShaderPtr image_plane = shader_selectable("overlay_image"); - ShaderPtr image_plane_depth_bias = shader_selectable("overlay_image_depth_bias"); - ShaderPtr particle_dot = shader_selectable("overlay_particle_dot"); - ShaderPtr particle_shape = shader_selectable("overlay_particle_shape"); - ShaderPtr particle_hair = shader_selectable("overlay_particle_hair"); - ShaderPtr wireframe_mesh = shader_selectable("overlay_wireframe"); + StaticShader armature_envelope_fill = shader_selectable("overlay_armature_envelope_solid"); + StaticShader armature_envelope_outline = shader_selectable("overlay_armature_envelope_outline"); + StaticShader armature_shape_outline = shader_selectable("overlay_armature_shape_outline"); + StaticShader armature_shape_fill = shader_selectable("overlay_armature_shape_solid"); + StaticShader armature_shape_wire = shader_selectable("overlay_armature_shape_wire"); + StaticShader armature_shape_wire_strip = shader_selectable("overlay_armature_shape_wire_strip"); + StaticShader armature_sphere_outline = shader_selectable("overlay_armature_sphere_outline"); + StaticShader armature_sphere_fill = shader_selectable("overlay_armature_sphere_solid"); + StaticShader armature_stick = shader_selectable("overlay_armature_stick"); + StaticShader armature_wire = shader_selectable("overlay_armature_wire"); + StaticShader depth_curves = shader_selectable("overlay_depth_curves"); + StaticShader depth_grease_pencil = shader_selectable("overlay_depth_gpencil"); + StaticShader depth_mesh = shader_selectable("overlay_depth_mesh"); + StaticShader depth_mesh_conservative = shader_selectable("overlay_depth_mesh_conservative"); + StaticShader depth_pointcloud = shader_selectable("overlay_depth_pointcloud"); + StaticShader extra_shape = shader_selectable("overlay_extra"); + StaticShader extra_point = shader_selectable("overlay_extra_point"); + StaticShader extra_wire = shader_selectable("overlay_extra_wire"); + StaticShader extra_wire_object = shader_selectable("overlay_extra_wire_object"); + StaticShader extra_loose_points = shader_selectable("overlay_extra_loose_point"); + StaticShader extra_grid = shader_selectable("overlay_extra_grid"); + StaticShader extra_ground_line = shader_selectable("overlay_extra_groundline"); + StaticShader image_plane = shader_selectable("overlay_image"); + StaticShader image_plane_depth_bias = shader_selectable("overlay_image_depth_bias"); + StaticShader particle_dot = shader_selectable("overlay_particle_dot"); + StaticShader particle_shape = shader_selectable("overlay_particle_shape"); + StaticShader particle_hair = shader_selectable("overlay_particle_hair"); + StaticShader wireframe_mesh = shader_selectable("overlay_wireframe"); /* Draw objects without edges for the wireframe overlay. */ - ShaderPtr wireframe_points = shader_selectable("overlay_wireframe_points"); - ShaderPtr wireframe_curve = shader_selectable("overlay_wireframe_curve"); + StaticShader wireframe_points = shader_selectable("overlay_wireframe_points"); + StaticShader wireframe_curve = shader_selectable("overlay_wireframe_curve"); - ShaderPtr fluid_grid_lines_flags = shader_selectable_no_clip("overlay_volume_gridlines_flags"); - ShaderPtr fluid_grid_lines_flat = shader_selectable_no_clip("overlay_volume_gridlines_flat"); - ShaderPtr fluid_grid_lines_range = shader_selectable_no_clip("overlay_volume_gridlines_range"); - ShaderPtr fluid_velocity_streamline = shader_selectable_no_clip( + StaticShader fluid_grid_lines_flags = shader_selectable_no_clip( + "overlay_volume_gridlines_flags"); + StaticShader fluid_grid_lines_flat = shader_selectable_no_clip("overlay_volume_gridlines_flat"); + StaticShader fluid_grid_lines_range = shader_selectable_no_clip( + "overlay_volume_gridlines_range"); + StaticShader fluid_velocity_streamline = shader_selectable_no_clip( "overlay_volume_velocity_streamline"); - ShaderPtr fluid_velocity_mac = shader_selectable_no_clip("overlay_volume_velocity_mac"); - ShaderPtr fluid_velocity_needle = shader_selectable_no_clip("overlay_volume_velocity_needle"); + StaticShader fluid_velocity_mac = shader_selectable_no_clip("overlay_volume_velocity_mac"); + StaticShader fluid_velocity_needle = shader_selectable_no_clip("overlay_volume_velocity_needle"); /** Module */ /** Only to be used by Instance constructor. */ @@ -590,13 +597,9 @@ class ShaderModule { ShaderModule(const SelectionType selection_type, const bool clipping_enabled) : selection_type_(selection_type), clipping_enabled_(clipping_enabled){}; - ShaderPtr shader(const char *create_info_name) - { - return ShaderPtr(GPU_shader_create_from_info_name(create_info_name)); - } - ShaderPtr shader_clippable(const char *create_info_name); - ShaderPtr shader_selectable(const char *create_info_name); - ShaderPtr shader_selectable_no_clip(const char *create_info_name); + StaticShader shader_clippable(const char *create_info_name); + StaticShader shader_selectable(const char *create_info_name); + StaticShader shader_selectable_no_clip(const char *create_info_name); }; struct GreasePencilDepthPlane { diff --git a/source/blender/draw/engines/overlay/overlay_next_shader.cc b/source/blender/draw/engines/overlay/overlay_next_shader.cc index b9f374e7323..b8295a06cec 100644 --- a/source/blender/draw/engines/overlay/overlay_next_shader.cc +++ b/source/blender/draw/engines/overlay/overlay_next_shader.cc @@ -10,9 +10,7 @@ namespace blender::draw::overlay { -ShaderModule *ShaderModule::g_shader_modules[2][2] = {{nullptr}}; - -ShaderModule::ShaderPtr ShaderModule::shader_clippable(const char *create_info_name) +StaticShader ShaderModule::shader_clippable(const char *create_info_name) { std::string name = create_info_name; @@ -20,10 +18,10 @@ ShaderModule::ShaderPtr ShaderModule::shader_clippable(const char *create_info_n name += "_clipped"; } - return ShaderPtr(GPU_shader_create_from_info_name(name.c_str())); + return StaticShader(name); } -ShaderModule::ShaderPtr ShaderModule::shader_selectable(const char *create_info_name) +StaticShader ShaderModule::shader_selectable(const char *create_info_name) { std::string name = create_info_name; @@ -35,10 +33,10 @@ ShaderModule::ShaderPtr ShaderModule::shader_selectable(const char *create_info_ name += "_clipped"; } - return ShaderPtr(GPU_shader_create_from_info_name(name.c_str())); + return StaticShader(name); } -ShaderModule::ShaderPtr ShaderModule::shader_selectable_no_clip(const char *create_info_name) +StaticShader ShaderModule::shader_selectable_no_clip(const char *create_info_name) { std::string name = create_info_name; @@ -46,7 +44,7 @@ ShaderModule::ShaderPtr ShaderModule::shader_selectable_no_clip(const char *crea name += "_selectable"; } - return ShaderPtr(GPU_shader_create_from_info_name(name.c_str())); + return StaticShader(name); } using namespace blender::gpu::shader; @@ -54,23 +52,15 @@ using namespace blender::gpu::shader; ShaderModule &ShaderModule::module_get(SelectionType selection_type, bool clipping_enabled) { int selection_index = selection_type == SelectionType::DISABLED ? 0 : 1; - ShaderModule *&g_shader_module = g_shader_modules[selection_index][clipping_enabled]; - if (g_shader_module == nullptr) { - /* TODO(@fclem) thread-safety. */ - g_shader_module = new ShaderModule(selection_type, clipping_enabled); - } - return *g_shader_module; + return get_static_cache()[selection_index][clipping_enabled].get(selection_type, + clipping_enabled); } void ShaderModule::module_free() { for (int i : IndexRange(2)) { for (int j : IndexRange(2)) { - if (g_shader_modules[i][j] != nullptr) { - /* TODO(@fclem) thread-safety. */ - delete g_shader_modules[i][j]; - g_shader_modules[i][j] = nullptr; - } + get_static_cache()[i][j].release(); } } } diff --git a/source/blender/draw/engines/select/select_debug_engine.cc b/source/blender/draw/engines/select/select_debug_engine.cc index 70e17f8e6bc..0f9c805091a 100644 --- a/source/blender/draw/engines/select/select_debug_engine.cc +++ b/source/blender/draw/engines/select/select_debug_engine.cc @@ -13,6 +13,7 @@ #include "DNA_ID.h" #include "DRW_engine.hh" +#include "DRW_render.hh" #include "draw_manager.hh" #include "draw_pass.hh" @@ -29,9 +30,34 @@ struct SELECTIDDEBUG_Data { void *engine_type; }; -static struct { - struct GPUShader *select_debug_sh; -} e_data = {nullptr}; /* Engine data */ +namespace blender::draw::SelectDebug { + +using StaticShader = gpu::StaticShader; + +class ShaderCache { + private: + static gpu::StaticShaderCache &get_static_cache() + { + static gpu::StaticShaderCache static_cache; + return static_cache; + } + + public: + static ShaderCache &get() + { + return get_static_cache().get(); + } + static void release() + { + get_static_cache().release(); + } + + StaticShader select_debug = {"select_debug_fullscreen"}; +}; + +} // namespace blender::draw::SelectDebug + +using namespace blender::draw::SelectDebug; /** \} */ @@ -46,16 +72,12 @@ static void select_debug_draw_scene(void * /*vedata*/) return; } - if (!e_data.select_debug_sh) { - e_data.select_debug_sh = GPU_shader_create_from_info_name("select_debug_fullscreen"); - } - using namespace blender::draw; PassSimple pass = {"SelectEngineDebug"}; pass.init(); pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA); - pass.shader_set(e_data.select_debug_sh); + pass.shader_set(ShaderCache::get().select_debug.get()); pass.bind_texture("image", texture_u32); pass.bind_texture("image", texture_u32); pass.draw_procedural(GPU_PRIM_TRIS, 1, 3); @@ -65,7 +87,7 @@ static void select_debug_draw_scene(void * /*vedata*/) static void select_debug_engine_free() { - GPU_SHADER_FREE_SAFE(e_data.select_debug_sh); + ShaderCache::release(); } /** \} */ diff --git a/source/blender/draw/engines/workbench/workbench_private.hh b/source/blender/draw/engines/workbench/workbench_private.hh index 654addd8128..e6c6a6ac4f6 100644 --- a/source/blender/draw/engines/workbench/workbench_private.hh +++ b/source/blender/draw/engines/workbench/workbench_private.hh @@ -6,6 +6,7 @@ #include "DNA_camera_types.h" #include "DRW_render.hh" +#include "GPU_shader.hh" #include "draw_manager.hh" #include "draw_pass.hh" @@ -20,38 +21,10 @@ extern "C" DrawEngineType draw_engine_workbench; namespace blender::workbench { using namespace draw; - -class StaticShader : NonCopyable { - private: - std::string info_name_; - GPUShader *shader_ = nullptr; - - public: - StaticShader(std::string info_name) : info_name_(info_name) {} - - StaticShader() = default; - StaticShader(StaticShader &&other) = default; - StaticShader &operator=(StaticShader &&other) = default; - - ~StaticShader() - { - GPU_SHADER_FREE_SAFE(shader_); - } - - GPUShader *get() - { - if (!shader_) { - BLI_assert(!info_name_.empty()); - shader_ = GPU_shader_create_from_info_name(info_name_.c_str()); - } - return shader_; - } -}; +using StaticShader = gpu::StaticShader; class ShaderCache { private: - static ShaderCache *static_cache; - StaticShader prepass_[geometry_type_len][pipeline_type_len][lighting_type_len][shader_type_len] [2 /*clip*/]; StaticShader resolve_[lighting_type_len][2 /*cavity*/][2 /*curvature*/][2 /*shadow*/]; @@ -60,9 +33,21 @@ class ShaderCache { StaticShader volume_[2 /*smoke*/][3 /*interpolation*/][2 /*coba*/][2 /*slice*/]; + static gpu::StaticShaderCache &get_static_cache() + { + static gpu::StaticShaderCache static_cache; + return static_cache; + } + public: - static ShaderCache &get(); - static void release(); + static ShaderCache &get() + { + return get_static_cache().get(); + } + static void release() + { + get_static_cache().release(); + } ShaderCache(); diff --git a/source/blender/draw/engines/workbench/workbench_shader_cache.cc b/source/blender/draw/engines/workbench/workbench_shader_cache.cc index b515c6e2fbf..7a1e28840d7 100644 --- a/source/blender/draw/engines/workbench/workbench_shader_cache.cc +++ b/source/blender/draw/engines/workbench/workbench_shader_cache.cc @@ -6,24 +6,6 @@ namespace blender::workbench { -ShaderCache *ShaderCache::static_cache = nullptr; - -ShaderCache &ShaderCache::get() -{ - if (!ShaderCache::static_cache) { - ShaderCache::static_cache = new ShaderCache(); - } - return *ShaderCache::static_cache; -} - -void ShaderCache::release() -{ - if (ShaderCache::static_cache) { - delete ShaderCache::static_cache; - ShaderCache::static_cache = nullptr; - } -} - ShaderCache::ShaderCache() { const std::string geometries[] = {"_mesh", "_curves", "_ptcloud"}; diff --git a/source/blender/draw/intern/draw_shader.cc b/source/blender/draw/intern/draw_shader.cc index 1dc4d8f8e60..d4e3d06b4e7 100644 --- a/source/blender/draw/intern/draw_shader.cc +++ b/source/blender/draw/intern/draw_shader.cc @@ -16,99 +16,83 @@ #include "DRW_render.hh" -extern "C" char datatoc_common_hair_lib_glsl[]; +namespace blender::draw::Shader { -#define SHADER_CUSTOM_DATA_INTERP_MAX_DIMENSIONS 4 -static struct { - GPUShader *hair_refine_sh[PART_REFINE_MAX_SHADER]; - GPUShader *debug_print_display_sh; - GPUShader *debug_draw_display_sh; - GPUShader *draw_visibility_compute_sh; - GPUShader *draw_view_finalize_sh; - GPUShader *draw_resource_finalize_sh; - GPUShader *draw_command_generate_sh; - - GPUShader *subdiv_sh[SUBDIVISION_MAX_SHADERS]; - GPUShader *subdiv_custom_data_sh[SHADER_CUSTOM_DATA_INTERP_MAX_DIMENSIONS][GPU_COMP_MAX]; -} e_data = {{nullptr}}; - -/* -------------------------------------------------------------------- */ -/** \name Hair refinement - * \{ */ - -static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader /*refinement*/) -{ - return GPU_shader_create_from_info_name("draw_hair_refine_compute"); -} - -GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement) -{ - if (e_data.hair_refine_sh[refinement] == nullptr) { - GPUShader *sh = hair_refine_shader_compute_create(refinement); - e_data.hair_refine_sh[refinement] = sh; +class ShaderCache { + static gpu::StaticShaderCache &get_static_cache() + { + static gpu::StaticShaderCache static_cache; + return static_cache; } - return e_data.hair_refine_sh[refinement]; + public: + static ShaderCache &get() + { + return get_static_cache().get(); + } + static void release() + { + get_static_cache().release(); + } + + gpu::StaticShader hair_refine = {"draw_hair_refine_compute"}; + gpu::StaticShader debug_draw_display = {"draw_debug_draw_display"}; + gpu::StaticShader draw_visibility_compute = {"draw_visibility_compute"}; + gpu::StaticShader draw_view_finalize = {"draw_view_finalize"}; + gpu::StaticShader draw_resource_finalize = {"draw_resource_finalize"}; + gpu::StaticShader draw_command_generate = {"draw_command_generate"}; +}; + +} // namespace blender::draw::Shader + +using namespace blender::draw::Shader; + +GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader /*refinement*/) +{ + return ShaderCache::get().hair_refine.get(); } -GPUShader *DRW_shader_curves_refine_get(blender::draw::CurvesEvalShader type) +GPUShader *DRW_shader_curves_refine_get(blender::draw::CurvesEvalShader /*type*/) { /* TODO: Implement curves evaluation types (Bezier and Catmull Rom). */ - if (e_data.hair_refine_sh[type] == nullptr) { - GPUShader *sh = hair_refine_shader_compute_create(PART_REFINE_CATMULL_ROM); - e_data.hair_refine_sh[type] = sh; - } - - return e_data.hair_refine_sh[type]; + return ShaderCache::get().hair_refine.get(); } GPUShader *DRW_shader_debug_draw_display_get() { - if (e_data.debug_draw_display_sh == nullptr) { - e_data.debug_draw_display_sh = GPU_shader_create_from_info_name("draw_debug_draw_display"); - } - return e_data.debug_draw_display_sh; + return ShaderCache::get().debug_draw_display.get(); } GPUShader *DRW_shader_draw_visibility_compute_get() { - if (e_data.draw_visibility_compute_sh == nullptr) { - e_data.draw_visibility_compute_sh = GPU_shader_create_from_info_name( - "draw_visibility_compute"); - } - return e_data.draw_visibility_compute_sh; + return ShaderCache::get().draw_visibility_compute.get(); } GPUShader *DRW_shader_draw_view_finalize_get() { - if (e_data.draw_view_finalize_sh == nullptr) { - e_data.draw_view_finalize_sh = GPU_shader_create_from_info_name("draw_view_finalize"); - } - return e_data.draw_view_finalize_sh; + return ShaderCache::get().draw_view_finalize.get(); } GPUShader *DRW_shader_draw_resource_finalize_get() { - if (e_data.draw_resource_finalize_sh == nullptr) { - e_data.draw_resource_finalize_sh = GPU_shader_create_from_info_name("draw_resource_finalize"); - } - return e_data.draw_resource_finalize_sh; + return ShaderCache::get().draw_resource_finalize.get(); } GPUShader *DRW_shader_draw_command_generate_get() { - if (e_data.draw_command_generate_sh == nullptr) { - e_data.draw_command_generate_sh = GPU_shader_create_from_info_name("draw_command_generate"); - } - return e_data.draw_command_generate_sh; + return ShaderCache::get().draw_command_generate.get(); } -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Subdivision * \{ */ +#define SHADER_CUSTOM_DATA_INTERP_MAX_DIMENSIONS 4 +static struct { + GPUShader *subdiv_sh[SUBDIVISION_MAX_SHADERS]; + GPUShader *subdiv_custom_data_sh[SHADER_CUSTOM_DATA_INTERP_MAX_DIMENSIONS][GPU_COMP_MAX]; +} e_data = {{nullptr}}; + static blender::StringRefNull get_subdiv_shader_info_name(SubdivShaderType shader_type) { switch (shader_type) { @@ -239,15 +223,7 @@ GPUShader *DRW_shader_subdiv_custom_data_get(GPUVertCompType comp_type, int dime void DRW_shaders_free() { - for (int i = 0; i < PART_REFINE_MAX_SHADER; i++) { - GPU_SHADER_FREE_SAFE(e_data.hair_refine_sh[i]); - } - GPU_SHADER_FREE_SAFE(e_data.debug_print_display_sh); - GPU_SHADER_FREE_SAFE(e_data.debug_draw_display_sh); - GPU_SHADER_FREE_SAFE(e_data.draw_visibility_compute_sh); - GPU_SHADER_FREE_SAFE(e_data.draw_view_finalize_sh); - GPU_SHADER_FREE_SAFE(e_data.draw_resource_finalize_sh); - GPU_SHADER_FREE_SAFE(e_data.draw_command_generate_sh); + ShaderCache::release(); for (int i = 0; i < SUBDIVISION_MAX_SHADERS; i++) { GPU_SHADER_FREE_SAFE(e_data.subdiv_sh[i]); diff --git a/source/blender/gpu/GPU_shader.hh b/source/blender/gpu/GPU_shader.hh index 06eab06a8a3..4ea9c6423da 100644 --- a/source/blender/gpu/GPU_shader.hh +++ b/source/blender/gpu/GPU_shader.hh @@ -10,6 +10,7 @@ #pragma once +#include #include #include "BLI_span.hh" @@ -410,3 +411,125 @@ int GPU_shader_get_uniform_block(GPUShader *shader, const char *name); shader = nullptr; \ } \ } while (0) + +#include "BLI_utility_mixins.hh" +#include +#include + +namespace blender::gpu { + +/* GPUShader wrapper that makes compilation threadsafe. + * The compilation is deferred until the first get() call. + * Concurrently using the shader from multiple threads is still unsafe. */ +class StaticShader : NonCopyable { + private: + std::string info_name_; + std::atomic shader_ = nullptr; + /* TODO: Failed compilation detection should be supported by the GPUShader API. */ + std::atomic_bool failed_ = false; + std::mutex mutex_; + + void move(StaticShader &&other) + { + std::scoped_lock lock1(mutex_); + std::scoped_lock lock2(other.mutex_); + BLI_assert(shader_ == nullptr && info_name_.empty()); + std::swap(info_name_, other.info_name_); + /* No std::swap support for atomics. */ + shader_.exchange(other.shader_.exchange(shader_)); + failed_.exchange(other.failed_.exchange(failed_)); + } + + public: + StaticShader(std::string info_name) : info_name_(info_name) {} + + StaticShader() = default; + StaticShader(StaticShader &&other) + { + move(std::move(other)); + } + StaticShader &operator=(StaticShader &&other) + { + move(std::move(other)); + return *this; + }; + + ~StaticShader() + { + GPU_SHADER_FREE_SAFE(shader_); + } + + GPUShader *get() + { + if (shader_ || failed_) { + return shader_; + } + + std::scoped_lock lock(mutex_); + + if (!shader_ && !failed_) { + BLI_assert(!info_name_.empty()); + shader_ = GPU_shader_create_from_info_name(info_name_.c_str()); + failed_ = shader_ != nullptr; + } + + return shader_; + } + + /* For batch compiled shaders. */ + /* TODO: Find a better way to handle this. */ + void set(GPUShader *shader) + { + std::scoped_lock lock(mutex_); + BLI_assert(shader_ == nullptr); + shader_ = shader; + } +}; + +/* Thread-safe container for StaticShader cache classes. + * The class instance creation is deferred until the first get() call. */ +template class StaticShaderCache { + std::atomic cache_ = nullptr; + std::mutex mutex_; + + public: + ~StaticShaderCache() + { + BLI_assert(cache_ == nullptr); + } + + template T &get(Args &&...constructor_args) + { + if (cache_) { + return *cache_; + } + + std::lock_guard lock(mutex_); + + if (cache_ == nullptr) { + cache_ = new T(std::forward(constructor_args)...); + } + return *cache_; + } + + void release() + { + if (!cache_) { + return; + } + + std::lock_guard lock(mutex_); + + if (cache_) { + delete cache_; + cache_ = nullptr; + } + } + + std::lock_guard lock_guard() + { + return std::lock_guard(mutex_); + } +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index 9b40d459bca..077927a3b04 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -974,6 +974,8 @@ ShaderCompilerGeneric::~ShaderCompilerGeneric() BatchHandle ShaderCompilerGeneric::batch_compile(Span &infos) { + std::lock_guard lock(mutex_); + BatchHandle handle = next_batch_handle++; batches.add(handle, {{}, infos, true}); Batch &batch = batches.lookup(handle); @@ -986,12 +988,16 @@ BatchHandle ShaderCompilerGeneric::batch_compile(Span ShaderCompilerGeneric::batch_finalize(BatchHandle &handle) { + std::lock_guard lock(mutex_); + Vector shaders = batches.pop(handle).shaders; handle = 0; return shaders; diff --git a/source/blender/gpu/intern/gpu_shader_private.hh b/source/blender/gpu/intern/gpu_shader_private.hh index fa71ce65c0f..d2083eddaea 100644 --- a/source/blender/gpu/intern/gpu_shader_private.hh +++ b/source/blender/gpu/intern/gpu_shader_private.hh @@ -191,8 +191,7 @@ class ShaderCompiler { }; }; -/* Generic (fully synchronous) implementation for backends that don't implement their own - * ShaderCompiler. Used by Vulkan and Metal. */ +/* Generic (fully synchronous) implementation used as fallback. */ class ShaderCompilerGeneric : public ShaderCompiler { private: struct Batch { @@ -202,6 +201,7 @@ class ShaderCompilerGeneric : public ShaderCompiler { }; BatchHandle next_batch_handle = 1; Map batches; + std::mutex mutex_; public: ~ShaderCompilerGeneric() override;