Metal: Implement suppot for clip plane toggling via GPU_clip_distances.

The Metal backend already supports output for the 6 clipping planes via gl_ClipDistances equivalent, however, functionality to toggle clipping plane enablement was missing.

Authored by Apple: Michael Parkin-White

Ref T96261
Depends on D16777

Reviewed By: fclem

Maniphest Tasks: T96261

Differential Revision: https://developer.blender.org/D16813
This commit is contained in:
Jason Fielder
2022-12-20 14:15:52 +01:00
committed by Clément Foucault
parent df1fe18ed7
commit b3464fe152
8 changed files with 113 additions and 12 deletions

View File

@@ -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")

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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")
};

View File

@@ -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)

View File

@@ -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