MSL: Support PrimitiveID in fragment and barycentrics.

This commit is contained in:
Hans-Kristian Arntzen 2019-06-13 11:33:40 +02:00
parent 707312b83a
commit 2e1cee5e1e
8 changed files with 223 additions and 6 deletions

View File

@ -0,0 +1,28 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Vertices
{
float2 uvs[1];
};
struct main0_out
{
float2 value [[color(0)]];
};
struct main0_in
{
float3 gl_BaryCoordNoPerspNV [[barycentric_coord, center_no_perspective]];
};
fragment main0_out main0(main0_in in [[stage_in]], const device Vertices& _19 [[buffer(0)]], uint gl_PrimitiveID [[primitive_id]])
{
main0_out out = {};
int _23 = 3 * int(gl_PrimitiveID);
out.value = ((_19.uvs[_23] * in.gl_BaryCoordNoPerspNV.x) + (_19.uvs[_23 + 1] * in.gl_BaryCoordNoPerspNV.y)) + (_19.uvs[_23 + 2] * in.gl_BaryCoordNoPerspNV.z);
return out;
}

View File

@ -0,0 +1,28 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Vertices
{
float2 uvs[1];
};
struct main0_out
{
float2 value [[color(0)]];
};
struct main0_in
{
float3 gl_BaryCoordNV [[barycentric_coord, center_perspective]];
};
fragment main0_out main0(main0_in in [[stage_in]], const device Vertices& _19 [[buffer(0)]], uint gl_PrimitiveID [[primitive_id]])
{
main0_out out = {};
int _23 = 3 * int(gl_PrimitiveID);
out.value = ((_19.uvs[_23] * in.gl_BaryCoordNV.x) + (_19.uvs[_23 + 1] * in.gl_BaryCoordNV.y)) + (_19.uvs[_23 + 2] * in.gl_BaryCoordNV.z);
return out;
}

View File

@ -0,0 +1,31 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Vertices
{
float2 uvs[1];
};
struct main0_out
{
float2 value [[color(0)]];
};
struct main0_in
{
float3 gl_BaryCoordNoPerspNV [[barycentric_coord, center_no_perspective]];
};
fragment main0_out main0(main0_in in [[stage_in]], const device Vertices& _19 [[buffer(0)]], uint gl_PrimitiveID [[primitive_id]])
{
main0_out out = {};
int prim = int(gl_PrimitiveID);
float2 uv0 = _19.uvs[(3 * prim) + 0];
float2 uv1 = _19.uvs[(3 * prim) + 1];
float2 uv2 = _19.uvs[(3 * prim) + 2];
out.value = ((uv0 * in.gl_BaryCoordNoPerspNV.x) + (uv1 * in.gl_BaryCoordNoPerspNV.y)) + (uv2 * in.gl_BaryCoordNoPerspNV.z);
return out;
}

View File

@ -0,0 +1,31 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Vertices
{
float2 uvs[1];
};
struct main0_out
{
float2 value [[color(0)]];
};
struct main0_in
{
float3 gl_BaryCoordNV [[barycentric_coord, center_perspective]];
};
fragment main0_out main0(main0_in in [[stage_in]], const device Vertices& _19 [[buffer(0)]], uint gl_PrimitiveID [[primitive_id]])
{
main0_out out = {};
int prim = int(gl_PrimitiveID);
float2 uv0 = _19.uvs[(3 * prim) + 0];
float2 uv1 = _19.uvs[(3 * prim) + 1];
float2 uv2 = _19.uvs[(3 * prim) + 2];
out.value = ((uv0 * in.gl_BaryCoordNV.x) + (uv1 * in.gl_BaryCoordNV.y)) + (uv2 * in.gl_BaryCoordNV.z);
return out;
}

View File

@ -0,0 +1,17 @@
#version 450
#extension GL_NV_fragment_shader_barycentric : require
layout(location = 0) out vec2 value;
layout(set = 0, binding = 0) readonly buffer Vertices
{
vec2 uvs[];
};
void main () {
int prim = gl_PrimitiveID;
vec2 uv0 = uvs[3 * prim + 0];
vec2 uv1 = uvs[3 * prim + 1];
vec2 uv2 = uvs[3 * prim + 2];
value = gl_BaryCoordNoPerspNV.x * uv0 + gl_BaryCoordNoPerspNV.y * uv1 + gl_BaryCoordNoPerspNV.z * uv2;
}

View File

@ -0,0 +1,17 @@
#version 450
#extension GL_NV_fragment_shader_barycentric : require
layout(location = 0) out vec2 value;
layout(set = 0, binding = 0) readonly buffer Vertices
{
vec2 uvs[];
};
void main () {
int prim = gl_PrimitiveID;
vec2 uv0 = uvs[3 * prim + 0];
vec2 uv1 = uvs[3 * prim + 1];
vec2 uv2 = uvs[3 * prim + 2];
value = gl_BaryCoordNV.x * uv0 + gl_BaryCoordNV.y * uv1 + gl_BaryCoordNV.z * uv2;
}

View File

@ -1942,6 +1942,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
// Accumulate the variables that should appear in the interface struct.
SmallVector<SPIRVariable *> vars;
bool incl_builtins = storage == StorageClassOutput || is_tessellation_shader();
bool has_seen_barycentric = false;
ir.for_each_typed_id<SPIRVariable>([&](uint32_t var_id, SPIRVariable &var) {
if (var.storage != storage)
@ -1956,6 +1957,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
bool is_interface_block_builtin =
(bi_type == BuiltInPosition || bi_type == BuiltInPointSize || bi_type == BuiltInClipDistance ||
bi_type == BuiltInCullDistance || bi_type == BuiltInLayer || bi_type == BuiltInViewportIndex ||
bi_type == BuiltInBaryCoordNV || bi_type == BuiltInBaryCoordNoPerspNV ||
bi_type == BuiltInFragDepth || bi_type == BuiltInFragStencilRefEXT || bi_type == BuiltInSampleMask) ||
(get_execution_model() == ExecutionModelTessellationEvaluation &&
(bi_type == BuiltInTessLevelOuter || bi_type == BuiltInTessLevelInner));
@ -1969,7 +1971,17 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
bool filter_patch_decoration = (has_decoration(var_id, DecorationPatch) || is_patch_block(type)) == patch;
if (is_active && !is_hidden_variable(var, incl_builtins) && type.pointer && filter_patch_decoration &&
bool hidden = is_hidden_variable(var, incl_builtins);
// Barycentric inputs must be emitted in stage-in, because they can have interpolation arguments.
if (is_active && (bi_type == BuiltInBaryCoordNV || bi_type == BuiltInBaryCoordNoPerspNV))
{
if (has_seen_barycentric)
SPIRV_CROSS_THROW("Cannot declare both BaryCoordNV and BaryCoordNoPerspNV in same shader in MSL.");
has_seen_barycentric = true;
hidden = false;
}
if (is_active && !hidden && type.pointer && filter_patch_decoration &&
(!is_builtin || is_interface_block_builtin))
{
vars.push_back(&var);
@ -5557,7 +5569,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
// Fragment function inputs
if (execution.model == ExecutionModelFragment && type.storage == StorageClassInput)
{
string quals = "";
string quals;
if (is_builtin)
{
switch (builtin)
@ -5568,7 +5580,10 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
case BuiltInSampleId:
case BuiltInSampleMask:
case BuiltInLayer:
case BuiltInBaryCoordNV:
case BuiltInBaryCoordNoPerspNV:
quals = builtin_qualifier(builtin);
break;
default:
break;
@ -5586,6 +5601,19 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
quals = string("user(locn") + convert_to_string(locn) + ")";
}
}
if (builtin == BuiltInBaryCoordNV || builtin == BuiltInBaryCoordNoPerspNV)
{
if (has_member_decoration(type.self, index, DecorationFlat) ||
has_member_decoration(type.self, index, DecorationCentroid) ||
has_member_decoration(type.self, index, DecorationSample) ||
has_member_decoration(type.self, index, DecorationNoPerspective))
{
// NoPerspective is baked into the builtin type.
SPIRV_CROSS_THROW("Flat, Centroid, Sample, NoPerspective decorations are not supported for BaryCoord inputs.");
}
}
// Don't bother decorating integers with the 'flat' attribute; it's
// the default (in fact, the only option). Also don't bother with the
// FragCoord builtin; it's always noperspective on Metal.
@ -5622,6 +5650,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
quals += "center_no_perspective";
}
}
if (!quals.empty())
return " [[" + quals + "]]";
}
@ -5929,13 +5958,14 @@ void CompilerMSL::entry_point_args_builtin(string &ep_args)
bi_type != BuiltInPatchVertices && bi_type != BuiltInTessLevelInner &&
bi_type != BuiltInTessLevelOuter && bi_type != BuiltInPosition && bi_type != BuiltInPointSize &&
bi_type != BuiltInClipDistance && bi_type != BuiltInCullDistance && bi_type != BuiltInSubgroupEqMask &&
bi_type != BuiltInBaryCoordNV && bi_type != BuiltInBaryCoordNoPerspNV &&
bi_type != BuiltInSubgroupGeMask && bi_type != BuiltInSubgroupGtMask &&
bi_type != BuiltInSubgroupLeMask && bi_type != BuiltInSubgroupLtMask)
{
if (!ep_args.empty())
ep_args += ", ";
ep_args += builtin_type_decl(bi_type) + " " + to_expression(var_id);
ep_args += builtin_type_decl(bi_type, var_id) + " " + to_expression(var_id);
ep_args += " [[" + builtin_qualifier(bi_type) + "]]";
}
}
@ -6504,7 +6534,7 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
if (var.basevariable == stage_in_ptr_var_id || var.basevariable == stage_out_ptr_var_id)
decl += type_to_glsl(type, arg.id);
else if (builtin)
decl += builtin_type_decl(static_cast<BuiltIn>(get_decoration(arg.id, DecorationBuiltIn)));
decl += builtin_type_decl(static_cast<BuiltIn>(get_decoration(arg.id, DecorationBuiltIn)), arg.id);
else if ((storage == StorageClassUniform || storage == StorageClassStorageBuffer) && is_array(type))
decl += join(type_to_glsl(type, arg.id), "*");
else
@ -7541,6 +7571,12 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
break;
case BuiltInBaryCoordNV:
case BuiltInBaryCoordNoPerspNV:
if (storage == StorageClassInput && current_function && (current_function->self == ir.default_entry_point))
return stage_in_var_name + "." + CompilerGLSL::builtin_to_glsl(builtin, storage);
break;
case BuiltInTessLevelOuter:
if (get_execution_model() == ExecutionModelTessellationEvaluation)
{
@ -7633,6 +7669,12 @@ string CompilerMSL::builtin_qualifier(BuiltIn builtin)
return "threadgroup_position_in_grid";
case ExecutionModelTessellationEvaluation:
return "patch_id";
case ExecutionModelFragment:
if (msl_options.is_ios())
SPIRV_CROSS_THROW("PrimitiveId is not supported in fragment on iOS.");
else if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 2))
SPIRV_CROSS_THROW("PrimitiveId on macOS requires MSL 2.2.");
return "primitive_id";
default:
SPIRV_CROSS_THROW("PrimitiveId is not supported in this execution model.");
}
@ -7716,13 +7758,29 @@ string CompilerMSL::builtin_qualifier(BuiltIn builtin)
// Shouldn't be reached.
SPIRV_CROSS_THROW("Subgroup ballot masks are handled specially in MSL.");
case BuiltInBaryCoordNV:
// TODO: AMD barycentrics as well? Seem to have different swizzle and 2 components rather than 3.
if (msl_options.is_ios())
SPIRV_CROSS_THROW("Barycentrics not supported on iOS.");
else if (!msl_options.supports_msl_version(2, 2))
SPIRV_CROSS_THROW("Barycentrics are only supported in MSL 2.2 and above on macOS.");
return "barycentric_coord, center_perspective";
case BuiltInBaryCoordNoPerspNV:
// TODO: AMD barycentrics as well? Seem to have different swizzle and 2 components rather than 3.
if (msl_options.is_ios())
SPIRV_CROSS_THROW("Barycentrics not supported on iOS.");
else if (!msl_options.supports_msl_version(2, 2))
SPIRV_CROSS_THROW("Barycentrics are only supported in MSL 2.2 and above on macOS.");
return "barycentric_coord, center_no_perspective";
default:
return "unsupported-built-in";
}
}
// Returns an MSL string type declaration for a SPIR-V builtin
string CompilerMSL::builtin_type_decl(BuiltIn builtin)
string CompilerMSL::builtin_type_decl(BuiltIn builtin, uint32_t id)
{
const SPIREntryPoint &execution = get_entry_point();
switch (builtin)
@ -7822,6 +7880,11 @@ string CompilerMSL::builtin_type_decl(BuiltIn builtin)
case BuiltInHelperInvocation:
return "bool";
case BuiltInBaryCoordNV:
case BuiltInBaryCoordNoPerspNV:
// Use the type as declared, can be 1, 2 or 3 components.
return type_to_glsl(get_variable_data_type(get<SPIRVariable>(id)));
default:
return "unsupported-built-in-type";
}
@ -8360,6 +8423,7 @@ void CompilerMSL::bitcast_from_builtin_load(uint32_t source_id, std::string &exp
case BuiltInLayer:
case BuiltInViewportIndex:
case BuiltInFragStencilRefEXT:
case BuiltInPrimitiveId:
expected_type = SPIRType::UInt;
break;
@ -8401,6 +8465,7 @@ void CompilerMSL::bitcast_to_builtin_store(uint32_t target_id, std::string &expr
case BuiltInLayer:
case BuiltInViewportIndex:
case BuiltInFragStencilRefEXT:
case BuiltInPrimitiveId:
expected_type = SPIRType::UInt;
break;

View File

@ -471,7 +471,7 @@ protected:
std::string to_swizzle_expression(uint32_t id);
std::string to_buffer_size_expression(uint32_t id);
std::string builtin_qualifier(spv::BuiltIn builtin);
std::string builtin_type_decl(spv::BuiltIn builtin);
std::string builtin_type_decl(spv::BuiltIn builtin, uint32_t id = 0);
std::string built_in_func_arg(spv::BuiltIn builtin, bool prefix_comma);
std::string member_attribute_qualifier(const SPIRType &type, uint32_t index);
std::string argument_decl(const SPIRFunction::Parameter &arg);