diff --git a/source/blender/draw/engines/eevee/shaders/infos/eevee_legacy_material_info.hh b/source/blender/draw/engines/eevee/shaders/infos/eevee_legacy_material_info.hh index 2d1671ac98d..dd848d6fef9 100644 --- a/source/blender/draw/engines/eevee/shaders/infos/eevee_legacy_material_info.hh +++ b/source/blender/draw/engines/eevee/shaders/infos/eevee_legacy_material_info.hh @@ -59,7 +59,9 @@ GPU_SHADER_CREATE_INFO(eevee_legacy_material_surface_vert_common) .additional_info("eevee_legacy_material_empty_base") .additional_info("draw_resource_id_varying") .additional_info("eevee_legacy_common_utiltex_lib") - .additional_info("eevee_legacy_closure_eval_surface_lib"); + .additional_info("eevee_legacy_closure_eval_surface_lib") + /* Planar reflections assigns to gl_ClipDistance via surface_vert.glsl. */ + .define("USE_CLIP_PLANES"); GPU_SHADER_CREATE_INFO(eevee_legacy_material_surface_vert) .additional_info("eevee_legacy_material_surface_vert_common") diff --git a/source/blender/gpu/metal/mtl_context.hh b/source/blender/gpu/metal/mtl_context.hh index 3656fbdcd19..a26e31aadde 100644 --- a/source/blender/gpu/metal/mtl_context.hh +++ b/source/blender/gpu/metal/mtl_context.hh @@ -460,6 +460,9 @@ struct MTLContextGlobalShaderPipelineState { /* Render parameters. */ float point_size = 1.0f; float line_width = 1.0f; + + /* Clipping plane enablement. */ + bool clip_distance_enabled[6] = {false}; }; /* Command Buffer Manager - Owned by MTLContext. diff --git a/source/blender/gpu/metal/mtl_pso_descriptor_state.hh b/source/blender/gpu/metal/mtl_pso_descriptor_state.hh index 8b0d69c6251..1890f6e8ccd 100644 --- a/source/blender/gpu/metal/mtl_pso_descriptor_state.hh +++ b/source/blender/gpu/metal/mtl_pso_descriptor_state.hh @@ -174,6 +174,9 @@ struct MTLRenderPipelineStateDescriptor { /* Global color write mask as this cannot be specified per attachment. */ MTLColorWriteMask color_write_mask; + /* Clip distance enablement. */ + uchar clipping_plane_enable_mask = 0; + /* Point size required by point primitives. */ float point_size = 0.0f; @@ -184,6 +187,10 @@ struct MTLRenderPipelineStateDescriptor { return false; } + if (clipping_plane_enable_mask != other.clipping_plane_enable_mask) { + return false; + } + if ((num_color_attachments != other.num_color_attachments) || (depth_attachment_format != other.depth_attachment_format) || (stencil_attachment_format != other.stencil_attachment_format) || @@ -243,6 +250,9 @@ struct MTLRenderPipelineStateDescriptor { hash |= uint64_t((this->blending_enabled && (this->num_color_attachments > 0)) ? 1 : 0) << 62; hash ^= uint64_t(this->point_size); + /* Clipping plane enablement. */ + hash ^= uint64_t(clipping_plane_enable_mask) << 20; + return hash; } diff --git a/source/blender/gpu/metal/mtl_shader.mm b/source/blender/gpu/metal/mtl_shader.mm index 747746ea424..496f919d189 100644 --- a/source/blender/gpu/metal/mtl_shader.mm +++ b/source/blender/gpu/metal/mtl_shader.mm @@ -654,6 +654,14 @@ MTLRenderPipelineStateInstance *MTLShader::bake_current_pipeline_state( pipeline_descriptor.src_rgb_blend_factor = ctx->pipeline_state.src_rgb_blend_factor; pipeline_descriptor.point_size = ctx->pipeline_state.point_size; + /* Resolve clipping plane enablement. */ + pipeline_descriptor.clipping_plane_enable_mask = 0; + for (const int plane : IndexRange(6)) { + pipeline_descriptor.clipping_plane_enable_mask = + pipeline_descriptor.clipping_plane_enable_mask | + ((ctx->pipeline_state.clip_distance_enabled[plane]) ? (1 << plane) : 0); + } + /* Primitive Type -- Primitive topology class needs to be specified for layered rendering. */ bool requires_specific_topology_class = uses_mtl_array_index_ || prim_type == MTLPrimitiveTopologyClassPoint; @@ -856,6 +864,29 @@ MTLRenderPipelineStateInstance *MTLShader::bake_current_pipeline_state( withName:@"MTL_transform_feedback_buffer_index"]; } + /* Clipping planes. */ + int MTL_clip_distances_enabled = (pipeline_descriptor.clipping_plane_enable_mask > 0) ? 1 : 0; + + /* Only define specialization constant if planes are required. + * We guard clip_planes usage on this flag. */ + [values setConstantValue:&MTL_clip_distances_enabled + type:MTLDataTypeInt + withName:@"MTL_clip_distances_enabled"]; + + if (MTL_clip_distances_enabled > 0) { + /* Assign individual enablement flags. Only define a flag function constant + * if it is used. */ + for (const int plane : IndexRange(6)) { + int plane_enabled = ctx->pipeline_state.clip_distance_enabled[plane] ? 1 : 0; + if (plane_enabled) { + [values + setConstantValue:&plane_enabled + type:MTLDataTypeInt + withName:[NSString stringWithFormat:@"MTL_clip_distance_enabled%d", plane]]; + } + } + } + /* gl_PointSize constant. */ bool null_pointsize = true; float MTL_pointsize = pipeline_descriptor.point_size; diff --git a/source/blender/gpu/metal/mtl_shader_generator.mm b/source/blender/gpu/metal/mtl_shader_generator.mm index b8c9a28efbd..b938f3fd228 100644 --- a/source/blender/gpu/metal/mtl_shader_generator.mm +++ b/source/blender/gpu/metal/mtl_shader_generator.mm @@ -1876,11 +1876,14 @@ std::string MSLGeneratorInterface::generate_msl_vertex_out_struct(ShaderStage sh out << "#if defined(USE_CLIP_PLANES) || defined(USE_WORLD_CLIP_PLANES)" << std::endl; if (this->clip_distances.size() > 1) { /* Output array of clip distances if specified. */ - out << "\tfloat clipdistance [[clip_distance]] [" << this->clip_distances.size() << "];" - << std::endl; + out << "\tfloat clipdistance [[clip_distance, " + "function_constant(MTL_clip_distances_enabled)]] [" + << this->clip_distances.size() << "];" << std::endl; } else if (this->clip_distances.size() > 0) { - out << "\tfloat clipdistance [[clip_distance]];" << std::endl; + out << "\tfloat clipdistance [[clip_distance, " + "function_constant(MTL_clip_distances_enabled)]];" + << std::endl; } out << "#endif" << std::endl; } @@ -2157,18 +2160,24 @@ std::string MSLGeneratorInterface::generate_msl_vertex_output_population() << std::endl; } - /* Output clip-distances. */ - out << "#if defined(USE_CLIP_PLANES) || defined(USE_WORLD_CLIP_PLANES)" << std::endl; + /* Output clip-distances. + * Clip distances are only written to if both clipping planes are turned on for the shader, + * and the clipping planes are enabled. Enablement is controlled on a per-plane basis + * via function constants in the shader pipeline state object (PSO). */ + out << "#if defined(USE_CLIP_PLANES) || defined(USE_WORLD_CLIP_PLANES)" << std::endl + << "if(MTL_clip_distances_enabled) {" << std::endl; if (this->clip_distances.size() > 1) { for (int cd = 0; cd < this->clip_distances.size(); cd++) { - out << "\toutput.clipdistance[" << cd << "] = vertex_shader_instance.gl_ClipDistance_" << cd - << ";" << std::endl; + /* Default value when clipping is disabled >= 0.0 to ensure primitive is not clipped. */ + out << "\toutput.clipdistance[" << cd + << "] = (is_function_constant_defined(MTL_clip_distance_enabled" << cd + << "))?vertex_shader_instance.gl_ClipDistance_" << cd << ":1.0;" << std::endl; } } else if (this->clip_distances.size() > 0) { out << "\toutput.clipdistance = vertex_shader_instance.gl_ClipDistance_0;" << std::endl; } - out << "#endif" << std::endl; + out << "}" << std::endl << "#endif" << std::endl; /* Populate output vertex variables. */ int output_id = 0; diff --git a/source/blender/gpu/metal/mtl_state.hh b/source/blender/gpu/metal/mtl_state.hh index 56114fd313e..bc6c700859d 100644 --- a/source/blender/gpu/metal/mtl_state.hh +++ b/source/blender/gpu/metal/mtl_state.hh @@ -80,6 +80,8 @@ class MTLStateManager : public StateManager { void mtl_depth_range(float near, float far); void mtl_stencil_mask(uint mask); void mtl_stencil_set_func(eGPUStencilTest stencil_func, int ref, uint mask); + void mtl_clip_plane_enable(uint i); + void mtl_clip_plane_disable(uint i); MEM_CXX_CLASS_ALLOC_FUNCS("MTLStateManager") }; diff --git a/source/blender/gpu/metal/mtl_state.mm b/source/blender/gpu/metal/mtl_state.mm index aeb1ec15de3..95b7e5edc7a 100644 --- a/source/blender/gpu/metal/mtl_state.mm +++ b/source/blender/gpu/metal/mtl_state.mm @@ -35,6 +35,12 @@ MTLStateManager::MTLStateManager(MTLContext *ctx) : StateManager() /* Force update using default state. */ current_ = ~state; current_mutable_ = ~mutable_state; + + /* Clip distances initial mask forces to 0x111, which exceeds + * max clip plane count of 6, so limit to ensure all clipping + * planes get disabled. */ + current_.clip_distances = 6; + set_state(state); set_mutable_state(mutable_state); } @@ -52,6 +58,7 @@ void MTLStateManager::force_state() { /* Little exception for clip distances since they need to keep the old count correct. */ uint32_t clip_distances = current_.clip_distances; + BLI_assert(clip_distances <= 6); current_ = ~this->state; current_.clip_distances = clip_distances; current_mutable_ = ~this->mutable_state; @@ -329,11 +336,32 @@ void MTLStateManager::set_stencil_mask(const eGPUStencilTest test, const GPUStat } } +void MTLStateManager::mtl_clip_plane_enable(uint i) +{ + BLI_assert(context_); + MTLContextGlobalShaderPipelineState &pipeline_state = context_->pipeline_state; + pipeline_state.clip_distance_enabled[i] = true; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; +} + +void MTLStateManager::mtl_clip_plane_disable(uint i) +{ + BLI_assert(context_); + MTLContextGlobalShaderPipelineState &pipeline_state = context_->pipeline_state; + pipeline_state.clip_distance_enabled[i] = false; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; +} + void MTLStateManager::set_clip_distances(const int new_dist_len, const int old_dist_len) { - /* TODO(Metal): Support Clip distances in METAL. Clip distance - * assignment via shader is supported, but global clip-states require - * support. */ + BLI_assert(new_dist_len <= 6); + BLI_assert(old_dist_len <= 6); + for (uint i = 0; i < new_dist_len; i++) { + mtl_clip_plane_enable(i); + } + for (uint i = new_dist_len; i < old_dist_len; i++) { + mtl_clip_plane_disable(i); + } } void MTLStateManager::set_logic_op(const bool enable) diff --git a/source/blender/gpu/shaders/metal/mtl_shader_common.msl b/source/blender/gpu/shaders/metal/mtl_shader_common.msl index c504cdbacb1..9ed320335c7 100644 --- a/source/blender/gpu/shaders/metal/mtl_shader_common.msl +++ b/source/blender/gpu/shaders/metal/mtl_shader_common.msl @@ -42,6 +42,22 @@ constant int MTL_AttributeConvert15 [[function_constant(17)]]; * Unused if function constant not set. */ constant int MTL_transform_feedback_buffer_index [[function_constant(18)]]; +/** Clip distance enablement. */ +/* General toggle to control whether any clipping distanes are written at all. + * This is an optimization to avoid having the clipping distance shader output + * paramter if it is not needed. */ +constant int MTL_clip_distances_enabled [[function_constant(19)]]; + +/* If clipping planes are enabled at all, then we require an enablement + * flag per clipping plane. If enabled, then gl_ClipDistances[N] will + * control clipping for a given plane, otherwise the value is ignored. */ +constant int MTL_clip_distance_enabled0 [[function_constant(20)]]; +constant int MTL_clip_distance_enabled1 [[function_constant(21)]]; +constant int MTL_clip_distance_enabled2 [[function_constant(22)]]; +constant int MTL_clip_distance_enabled3 [[function_constant(23)]]; +constant int MTL_clip_distance_enabled4 [[function_constant(24)]]; +constant int MTL_clip_distance_enabled5 [[function_constant(25)]]; + /** Internal attribute conversion functionality. */ /* Following descriptions in mtl_shader.hh, Metal only supports some implicit * attribute type conversions. These conversions occur when there is a difference