Fix: Metal validation error when shader has no uniforms #105796

Merged
Jeroen Bakker merged 2 commits from Jason-Fielder/blender:MetalNullUniforms_Fix into blender-v3.5-release 2023-03-16 08:10:48 +01:00
2 changed files with 96 additions and 38 deletions

View File

@ -490,8 +490,12 @@ class MSLGeneratorInterface {
std::string generate_msl_uniform_undefs(ShaderStage stage); std::string generate_msl_uniform_undefs(ShaderStage stage);
std::string generate_ubo_block_undef_chain(ShaderStage stage); std::string generate_ubo_block_undef_chain(ShaderStage stage);
std::string generate_msl_texture_vars(ShaderStage shader_stage); std::string generate_msl_texture_vars(ShaderStage shader_stage);
void generate_msl_textures_input_string(std::stringstream &out, ShaderStage stage); void generate_msl_textures_input_string(std::stringstream &out,
void generate_msl_uniforms_input_string(std::stringstream &out, ShaderStage stage); ShaderStage stage,
bool &is_first_parameter);
void generate_msl_uniforms_input_string(std::stringstream &out,
ShaderStage stage,
bool &is_first_parameter);
/* Location is not always specified, so this will resolve outstanding locations. */ /* Location is not always specified, so this will resolve outstanding locations. */
void resolve_input_attribute_locations(); void resolve_input_attribute_locations();

View File

@ -2145,8 +2145,20 @@ std::string MSLGeneratorInterface::generate_msl_compute_entry_stub()
return out.str(); return out.str();
} }
/* If first parameter in function signature, do not print out a comma.
* Update first parameter flag to false for future invocations. */
static char parameter_delimiter(bool &is_first_parameter)
{
if (is_first_parameter) {
is_first_parameter = false;
return ' ';
}
return ',';
}
void MSLGeneratorInterface::generate_msl_textures_input_string(std::stringstream &out, void MSLGeneratorInterface::generate_msl_textures_input_string(std::stringstream &out,
ShaderStage stage) ShaderStage stage,
bool &is_first_parameter)
{ {
/* Note: Shader stage must be specified as the singular stage index for which the input /* Note: Shader stage must be specified as the singular stage index for which the input
* is generating. Compound stages are not valid inputs. */ * is generating. Compound stages are not valid inputs. */
@ -2156,7 +2168,8 @@ void MSLGeneratorInterface::generate_msl_textures_input_string(std::stringstream
BLI_assert(this->texture_samplers.size() <= GPU_max_textures_vert()); BLI_assert(this->texture_samplers.size() <= GPU_max_textures_vert());
for (const MSLTextureSampler &tex : this->texture_samplers) { for (const MSLTextureSampler &tex : this->texture_samplers) {
if (bool(tex.stage & stage)) { if (bool(tex.stage & stage)) {
out << ",\n\t" << tex.get_msl_typestring(false) << " [[texture(" << tex.location << ")]]"; out << parameter_delimiter(is_first_parameter) << "\n\t" << tex.get_msl_typestring(false)
<< " [[texture(" << tex.location << ")]]";
} }
} }
@ -2166,7 +2179,8 @@ void MSLGeneratorInterface::generate_msl_textures_input_string(std::stringstream
* If we exceed the hardware-supported limit, then follow a bind-less model using argument * If we exceed the hardware-supported limit, then follow a bind-less model using argument
* buffers. */ * buffers. */
if (this->use_argument_buffer_for_samplers()) { if (this->use_argument_buffer_for_samplers()) {
out << ",\n\tconstant SStruct& samplers [[buffer(MTL_uniform_buffer_base_index+" out << parameter_delimiter(is_first_parameter)
<< "\n\tconstant SStruct& samplers [[buffer(MTL_uniform_buffer_base_index+"
<< (this->get_sampler_argument_buffer_bind_index(stage)) << ")]]"; << (this->get_sampler_argument_buffer_bind_index(stage)) << ")]]";
} }
else { else {
@ -2175,7 +2189,8 @@ void MSLGeneratorInterface::generate_msl_textures_input_string(std::stringstream
BLI_assert(this->texture_samplers.size() <= MTL_MAX_DEFAULT_SAMPLERS); BLI_assert(this->texture_samplers.size() <= MTL_MAX_DEFAULT_SAMPLERS);
for (const MSLTextureSampler &tex : this->texture_samplers) { for (const MSLTextureSampler &tex : this->texture_samplers) {
if (bool(tex.stage & stage)) { if (bool(tex.stage & stage)) {
out << ",\n\tsampler " << tex.name << "_sampler [[sampler(" << tex.location << ")]]"; out << parameter_delimiter(is_first_parameter) << "\n\tsampler " << tex.name
<< "_sampler [[sampler(" << tex.location << ")]]";
} }
} }
@ -2189,12 +2204,13 @@ void MSLGeneratorInterface::generate_msl_textures_input_string(std::stringstream
} }
void MSLGeneratorInterface::generate_msl_uniforms_input_string(std::stringstream &out, void MSLGeneratorInterface::generate_msl_uniforms_input_string(std::stringstream &out,
ShaderStage stage) ShaderStage stage,
bool &is_first_parameter)
{ {
for (const MSLUniformBlock &ubo : this->uniform_blocks) { for (const MSLUniformBlock &ubo : this->uniform_blocks) {
if (bool(ubo.stage & stage)) { if (bool(ubo.stage & stage)) {
/* For literal/existing global types, we do not need the class name-space accessor. */ /* For literal/existing global types, we do not need the class name-space accessor. */
out << ",\n\tconstant "; out << parameter_delimiter(is_first_parameter) << "\n\tconstant ";
if (!is_builtin_type(ubo.type_name)) { if (!is_builtin_type(ubo.type_name)) {
out << get_stage_class_name(stage) << "::"; out << get_stage_class_name(stage) << "::";
} }
@ -2211,104 +2227,135 @@ void MSLGeneratorInterface::generate_msl_uniforms_input_string(std::stringstream
std::string MSLGeneratorInterface::generate_msl_vertex_inputs_string() std::string MSLGeneratorInterface::generate_msl_vertex_inputs_string()
{ {
std::stringstream out; std::stringstream out;
bool is_first_parameter = true;
if (this->uses_ssbo_vertex_fetch_mode) { if (this->uses_ssbo_vertex_fetch_mode) {
/* Vertex Buffers bound as raw buffers. */ /* Vertex Buffers bound as raw buffers. */
for (int i = 0; i < MTL_SSBO_VERTEX_FETCH_MAX_VBOS; i++) { for (int i = 0; i < MTL_SSBO_VERTEX_FETCH_MAX_VBOS; i++) {
out << "\tconstant uchar* MTL_VERTEX_DATA_" << i << " [[buffer(" << i << ")]],\n"; out << parameter_delimiter(is_first_parameter) << "\tconstant uchar* MTL_VERTEX_DATA_" << i
<< " [[buffer(" << i << ")]]\n";
} }
out << "\tconstant ushort* MTL_INDEX_DATA[[buffer(MTL_SSBO_VERTEX_FETCH_IBO_INDEX)]],"; out << parameter_delimiter(is_first_parameter)
<< "\tconstant ushort* MTL_INDEX_DATA[[buffer(MTL_SSBO_VERTEX_FETCH_IBO_INDEX)]]";
} }
else { else {
if (this->vertex_input_attributes.size() > 0) { if (this->vertex_input_attributes.size() > 0) {
/* Vertex Buffers use input assembly. */ /* Vertex Buffers use input assembly. */
out << get_stage_class_name(ShaderStage::VERTEX) << "::VertexIn v_in [[stage_in]],"; out << get_stage_class_name(ShaderStage::VERTEX) << "::VertexIn v_in [[stage_in]]";
is_first_parameter = false;
} }
} }
out << "\n\tconstant " << get_stage_class_name(ShaderStage::VERTEX)
<< "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]";
this->generate_msl_uniforms_input_string(out, ShaderStage::VERTEX); if (this->uniforms.size() > 0) {
out << parameter_delimiter(is_first_parameter) << "\n\tconstant "
<< get_stage_class_name(ShaderStage::VERTEX)
<< "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]";
is_first_parameter = false;
}
this->generate_msl_uniforms_input_string(out, ShaderStage::VERTEX, is_first_parameter);
/* Transform feedback buffer binding. */ /* Transform feedback buffer binding. */
if (this->uses_transform_feedback) { if (this->uses_transform_feedback) {
out << ",\n\tdevice " << get_stage_class_name(ShaderStage::VERTEX) out << parameter_delimiter(is_first_parameter) << "\n\tdevice "
<< get_stage_class_name(ShaderStage::VERTEX)
<< "::VertexOut_TF* " << "::VertexOut_TF* "
"transform_feedback_results[[buffer(MTL_transform_feedback_buffer_index)]]"; "transform_feedback_results[[buffer(MTL_transform_feedback_buffer_index)]]";
} }
/* Generate texture signatures. */ /* Generate texture signatures. */
this->generate_msl_textures_input_string(out, ShaderStage::VERTEX); this->generate_msl_textures_input_string(out, ShaderStage::VERTEX, is_first_parameter);
/* Entry point parameters for gl Globals. */ /* Entry point parameters for gl Globals. */
if (this->uses_gl_VertexID) { if (this->uses_gl_VertexID) {
out << ",\n\tconst uint32_t gl_VertexID [[vertex_id]]"; out << parameter_delimiter(is_first_parameter)
<< "\n\tconst uint32_t gl_VertexID [[vertex_id]]";
} }
if (this->uses_gl_InstanceID) { if (this->uses_gl_InstanceID) {
out << ",\n\tconst uint32_t gl_InstanceID [[instance_id]]"; out << parameter_delimiter(is_first_parameter)
<< "\n\tconst uint32_t gl_InstanceID [[instance_id]]";
} }
if (this->uses_gl_BaseInstanceARB) { if (this->uses_gl_BaseInstanceARB) {
out << ",\n\tconst uint32_t gl_BaseInstanceARB [[base_instance]]"; out << parameter_delimiter(is_first_parameter)
<< "\n\tconst uint32_t gl_BaseInstanceARB [[base_instance]]";
} }
return out.str(); return out.str();
} }
std::string MSLGeneratorInterface::generate_msl_fragment_inputs_string() std::string MSLGeneratorInterface::generate_msl_fragment_inputs_string()
{ {
bool is_first_parameter = true;
std::stringstream out; std::stringstream out;
out << get_stage_class_name(ShaderStage::FRAGMENT) out << parameter_delimiter(is_first_parameter) << get_stage_class_name(ShaderStage::FRAGMENT)
<< "::VertexOut v_in [[stage_in]],\n\tconstant " << "::VertexOut v_in [[stage_in]]";
<< get_stage_class_name(ShaderStage::FRAGMENT)
<< "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]";
this->generate_msl_uniforms_input_string(out, ShaderStage::FRAGMENT); if (this->uniforms.size() > 0) {
out << parameter_delimiter(is_first_parameter) << "\n\tconstant "
<< get_stage_class_name(ShaderStage::FRAGMENT)
<< "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]";
}
this->generate_msl_uniforms_input_string(out, ShaderStage::FRAGMENT, is_first_parameter);
/* Generate texture signatures. */ /* Generate texture signatures. */
this->generate_msl_textures_input_string(out, ShaderStage::FRAGMENT); this->generate_msl_textures_input_string(out, ShaderStage::FRAGMENT, is_first_parameter);
if (this->uses_gl_PointCoord) { if (this->uses_gl_PointCoord) {
out << ",\n\tconst float2 gl_PointCoord [[point_coord]]"; out << parameter_delimiter(is_first_parameter)
<< "\n\tconst float2 gl_PointCoord [[point_coord]]";
} }
if (this->uses_gl_FrontFacing) { if (this->uses_gl_FrontFacing) {
out << ",\n\tconst MTLBOOL gl_FrontFacing [[front_facing]]"; out << parameter_delimiter(is_first_parameter)
<< "\n\tconst MTLBOOL gl_FrontFacing [[front_facing]]";
} }
if (this->uses_gl_PrimitiveID) { if (this->uses_gl_PrimitiveID) {
out << ",\n\tconst uint gl_PrimitiveID [[primitive_id]]"; out << parameter_delimiter(is_first_parameter)
<< "\n\tconst uint gl_PrimitiveID [[primitive_id]]";
} }
/* Barycentrics. */ /* Barycentrics. */
if (this->uses_barycentrics) { if (this->uses_barycentrics) {
out << ",\n\tconst float3 mtl_barycentric_coord [[barycentric_coord]]"; out << parameter_delimiter(is_first_parameter)
<< "\n\tconst float3 mtl_barycentric_coord [[barycentric_coord]]";
} }
return out.str(); return out.str();
} }
std::string MSLGeneratorInterface::generate_msl_compute_inputs_string() std::string MSLGeneratorInterface::generate_msl_compute_inputs_string()
{ {
bool is_first_parameter = true;
std::stringstream out; std::stringstream out;
out << "constant " << get_stage_class_name(ShaderStage::COMPUTE) if (this->uniforms.size() > 0) {
<< "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]"; out << parameter_delimiter(is_first_parameter) << "constant "
<< get_stage_class_name(ShaderStage::COMPUTE)
<< "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]";
}
this->generate_msl_uniforms_input_string(out, ShaderStage::COMPUTE); this->generate_msl_uniforms_input_string(out, ShaderStage::COMPUTE, is_first_parameter);
/* Generate texture signatures. */ /* Generate texture signatures. */
this->generate_msl_textures_input_string(out, ShaderStage::COMPUTE); this->generate_msl_textures_input_string(out, ShaderStage::COMPUTE, is_first_parameter);
/* Entry point parameters for gl Globals. */ /* Entry point parameters for gl Globals. */
if (this->uses_gl_GlobalInvocationID) { if (this->uses_gl_GlobalInvocationID) {
out << ",\n\tconst uint3 gl_GlobalInvocationID [[thread_position_in_grid]]"; out << parameter_delimiter(is_first_parameter)
<< "\n\tconst uint3 gl_GlobalInvocationID [[thread_position_in_grid]]";
} }
if (this->uses_gl_WorkGroupID) { if (this->uses_gl_WorkGroupID) {
out << ",\n\tconst uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]"; out << parameter_delimiter(is_first_parameter)
<< "\n\tconst uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]";
} }
if (this->uses_gl_NumWorkGroups) { if (this->uses_gl_NumWorkGroups) {
out << ",\n\tconst uint3 gl_NumWorkGroups [[threadgroups_per_grid]]"; out << parameter_delimiter(is_first_parameter)
<< "\n\tconst uint3 gl_NumWorkGroups [[threadgroups_per_grid]]";
} }
if (this->uses_gl_LocalInvocationIndex) { if (this->uses_gl_LocalInvocationIndex) {
out << ",\n\tconst uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]"; out << parameter_delimiter(is_first_parameter)
<< "\n\tconst uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]";
} }
if (this->uses_gl_LocalInvocationID) { if (this->uses_gl_LocalInvocationID) {
out << ",\n\tconst uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]"; out << parameter_delimiter(is_first_parameter)
<< "\n\tconst uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]";
} }
return out.str(); return out.str();
@ -2316,6 +2363,10 @@ std::string MSLGeneratorInterface::generate_msl_compute_inputs_string()
std::string MSLGeneratorInterface::generate_msl_uniform_structs(ShaderStage shader_stage) std::string MSLGeneratorInterface::generate_msl_uniform_structs(ShaderStage shader_stage)
{ {
/* Only generate PushConstantBlock if we have uniforms. */
if (this->uniforms.size() == 0) {
return "";
}
BLI_assert(shader_stage == ShaderStage::VERTEX || shader_stage == ShaderStage::FRAGMENT); BLI_assert(shader_stage == ShaderStage::VERTEX || shader_stage == ShaderStage::FRAGMENT);
std::stringstream out; std::stringstream out;
@ -2624,6 +2675,9 @@ std::string MSLGeneratorInterface::generate_msl_fragment_out_struct()
std::string MSLGeneratorInterface::generate_msl_global_uniform_population(ShaderStage stage) std::string MSLGeneratorInterface::generate_msl_global_uniform_population(ShaderStage stage)
{ {
if (this->uniforms.size() == 0) {
return "";
}
/* Populate Global Uniforms. */ /* Populate Global Uniforms. */
std::stringstream out; std::stringstream out;