[spirv] Handle TessFactor size mismatches. (#771)

This commit is contained in:
Ehsan 2017-11-07 12:37:02 -08:00 коммит произвёл GitHub
Родитель dc3d2c7921
Коммит 7cb1c20853
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 246 добавлений и 21 удалений

Просмотреть файл

@ -765,12 +765,14 @@ bool DeclResultIdMapper::createStageVars(
uint32_t semanticIndex = {};
if (getStageVarSemantic(decl, &semanticStr, &semantic, &semanticIndex)) {
const auto semanticKind = semantic->GetKind();
// Found semantic attached directly to this Decl. This means we need to
// map this decl to a single stage variable.
// Error out when the given semantic is invalid in this shader model
if (hlsl::SigPoint::GetInterpretation(
semantic->GetKind(), sigPoint->GetKind(), shaderModel.GetMajor(),
if (hlsl::SigPoint::GetInterpretation(semanticKind, sigPoint->GetKind(),
shaderModel.GetMajor(),
shaderModel.GetMinor()) ==
hlsl::DXIL::SemanticInterpretationKind::NA) {
emitError("invalid semantic %0 for shader model %1")
@ -785,12 +787,24 @@ bool DeclResultIdMapper::createStageVars(
// * SV_DomainLocation can refer to a float2, whereas TessCoord is a float3.
// To ensure SPIR-V validity, we must create a float3 and extract a
// float2 from it before passing it to the main function.
if (glPerVertex.tryToAccess(semantic->GetKind(), semanticIndex,
invocationId, value, sigPoint->GetKind()))
// * SV_TessFactor is an array of size 2 for isoline patch, array of size 3
// for tri patch, and array of size 4 for quad patch, but it must always
// be an array of size 4 in SPIR-V for Vulkan.
// * SV_InsideTessFactor is a single float for tri patch, and an array of
// size 2 for a quad patch, but it must always be an array of size 2 in
// SPIR-V for Vulkan.
if (glPerVertex.tryToAccess(semanticKind, semanticIndex, invocationId,
value, sigPoint->GetKind()))
return true;
if (semantic->GetKind() == hlsl::Semantic::Kind::DomainLocation)
if (semanticKind == hlsl::Semantic::Kind::DomainLocation)
typeId = theBuilder.getVecType(theBuilder.getFloat32Type(), 3);
else if (semanticKind == hlsl::Semantic::Kind::TessFactor)
typeId = theBuilder.getArrayType(theBuilder.getFloat32Type(),
theBuilder.getConstantUint32(4));
else if (semanticKind == hlsl::Semantic::Kind::InsideTessFactor)
typeId = theBuilder.getArrayType(theBuilder.getFloat32Type(),
theBuilder.getConstantUint32(2));
// Handle the extra arrayness
const uint32_t elementTypeId = typeId;
@ -820,18 +834,94 @@ bool DeclResultIdMapper::createStageVars(
if (asInput) {
*value = theBuilder.createLoad(typeId, varId);
// Fix ups for corner cases
// Special handling of SV_TessFactor DS patch constant input.
// TessLevelOuter is always an array of size 4 in SPIR-V, but
// SV_TessFactor could be an array of size 2, 3, or 4 in HLSL. Only the
// relevant indexes must be loaded.
if (semanticKind == hlsl::Semantic::Kind::TessFactor &&
hlsl::GetArraySize(type) != 4) {
llvm::SmallVector<uint32_t, 4> components;
const auto f32TypeId = theBuilder.getFloat32Type();
const auto tessFactorSize = hlsl::GetArraySize(type);
const auto arrType = theBuilder.getArrayType(
f32TypeId, theBuilder.getConstantUint32(tessFactorSize));
for (uint32_t i = 0; i < tessFactorSize; ++i)
components.push_back(
theBuilder.createCompositeExtract(f32TypeId, *value, {i}));
*value = theBuilder.createCompositeConstruct(arrType, components);
}
// Special handling of SV_InsideTessFactor DS patch constant input.
// TessLevelInner is always an array of size 2 in SPIR-V, but
// SV_InsideTessFactor could be an array of size 1 (scalar) or size 2 in
// HLSL. If SV_InsideTessFactor is a scalar, only extract index 0 of
// TessLevelInner.
else if (semanticKind == hlsl::Semantic::Kind::InsideTessFactor &&
!type->isArrayType()) {
*value = theBuilder.createCompositeExtract(theBuilder.getFloat32Type(),
*value, {0});
}
// SV_DomainLocation can refer to a float2 or a float3, whereas TessCoord
// is always a float3. To ensure SPIR-V validity, a float3 stage variable
// is created, and we must extract a float2 from it before passing it to
// the main function.
else if (semanticKind == hlsl::Semantic::Kind::DomainLocation &&
hlsl::GetHLSLVecSize(type) != 3) {
const auto domainLocSize = hlsl::GetHLSLVecSize(type);
*value = theBuilder.createVectorShuffle(
theBuilder.getVecType(theBuilder.getFloat32Type(), domainLocSize),
*value, *value, {0, 1});
}
} else {
uint32_t ptr = varId;
if (invocationId.hasValue()) {
// Special handling of HS ouput, for which we write to only one element
// in the per-vertex data array: the one indexed by SV_ControlPointID.
// Special handling of SV_TessFactor HS patch constant output.
// TessLevelOuter is always an array of size 4 in SPIR-V, but
// SV_TessFactor could be an array of size 2, 3, or 4 in HLSL. Only the
// relevant indexes must be written to.
if (semanticKind == hlsl::Semantic::Kind::TessFactor &&
hlsl::GetArraySize(type) != 4) {
const auto f32TypeId = theBuilder.getFloat32Type();
const auto tessFactorSize = hlsl::GetArraySize(type);
for (uint32_t i = 0; i < tessFactorSize; ++i) {
const uint32_t ptrType =
theBuilder.getPointerType(f32TypeId, spv::StorageClass::Output);
ptr = theBuilder.createAccessChain(ptrType, varId,
theBuilder.getConstantUint32(i));
theBuilder.createStore(
ptr, theBuilder.createCompositeExtract(f32TypeId, *value, i));
}
}
// Special handling of SV_InsideTessFactor HS patch constant output.
// TessLevelInner is always an array of size 2 in SPIR-V, but
// SV_InsideTessFactor could be an array of size 1 (scalar) or size 2 in
// HLSL. If SV_InsideTessFactor is a scalar, only write to index 0 of
// TessLevelInner.
else if (semanticKind == hlsl::Semantic::Kind::InsideTessFactor &&
!type->isArrayType()) {
ptr = theBuilder.createAccessChain(
theBuilder.getPointerType(theBuilder.getFloat32Type(),
spv::StorageClass::Output),
varId, theBuilder.getConstantUint32(0));
theBuilder.createStore(ptr, *value);
}
// Special handling of HS ouput, for which we write to only one
// element in the per-vertex data array: the one indexed by
// SV_ControlPointID.
else if (invocationId.hasValue()) {
const uint32_t ptrType =
theBuilder.getPointerType(elementTypeId, spv::StorageClass::Output);
const uint32_t index = invocationId.getValue();
ptr = theBuilder.createAccessChain(ptrType, varId, index);
}
theBuilder.createStore(ptr, *value);
}
// For all normal cases
else {
theBuilder.createStore(ptr, *value);
}
}
return true;
}

Просмотреть файл

@ -5767,15 +5767,6 @@ bool SPIRVEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl,
if (!declIdMapper.createStageInputVar(param, &loadedValue, false))
return false;
// SV_DomainLocation can refer to a float2, whereas TessCoord is a float3.
// To ensure SPIR-V validity, a float3 stage variable is created, and we
// must extract a float2 from it before passing it to the main function.
if (hasSemantic(param, hlsl::DXIL::SemanticKind::DomainLocation) &&
hlsl::GetHLSLVecSize(param->getType()) == 2) {
loadedValue = theBuilder.createVectorShuffle(typeId, loadedValue,
loadedValue, {0, 1});
}
theBuilder.createStore(tempVar, loadedValue);
// Record the temporary variable holding SV_OutputControlPointID and

Просмотреть файл

@ -44,7 +44,7 @@ HS_CONSTANT_DATA_OUTPUT SubDToBezierConstantsHS(InputPatch<VS_CONTROL_POINT_OUTP
return Output;
}
[domain("tri")]
[domain("quad")]
[partitioning("fractional_odd")]
[outputtopology("triangle_ccw")]
[outputcontrolpoints(MAX_POINTS)]
@ -60,7 +60,7 @@ BEZIER_CONTROL_POINT SubDToBezierHS(InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POIN
// OpCapability Tessellation
// OpMemoryModel Logical GLSL450
// OpEntryPoint TessellationControl %SubDToBezierHS "SubDToBezierHS" %gl_PerVertexIn %gl_PerVertexOut %in_var_WORLDPOS %in_var_TEXCOORD0 %in_var_TANGENT %gl_InvocationID %gl_PrimitiveID %out_var_BEZIERPOS %gl_TessLevelOuter %gl_TessLevelInner %out_var_TANGENT %out_var_TEXCOORD %out_var_TANUCORNER %out_var_TANVCORNER %out_var_TANWEIGHTS
// OpExecutionMode %SubDToBezierHS Triangles
// OpExecutionMode %SubDToBezierHS Quads
// OpExecutionMode %SubDToBezierHS SpacingFractionalOdd
// OpExecutionMode %SubDToBezierHS VertexOrderCcw
// OpExecutionMode %SubDToBezierHS OutputVertices 3

Просмотреть файл

@ -0,0 +1,50 @@
// Run: %dxc -T ds_6_0 -E BezierEvalDS
// Test handling of built-in size mismatch (reading in from the built-ins):
// The HLSL SV_TessFactor is a float3, but the SPIR-V equivalent is a float4.
// The HLSL SV_InsideTessFactor is a scalar float, but the SPIR-V equivalent is a float2.
// CHECK: OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter
// CHECK: OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner
// CHECK: %HS_CONSTANT_DATA_OUTPUT = OpTypeStruct %_arr_float_uint_3 %float
// CHECK: %gl_TessLevelOuter = OpVariable %_ptr_Input__arr_float_uint_4 Input
// CHECK: %gl_TessLevelInner = OpVariable %_ptr_Input__arr_float_uint_2 Input
// CHECK: [[gl_TessLevelOuter:%\d+]] = OpLoad %_arr_float_uint_4 %gl_TessLevelOuter
// CHECK-NEXT: [[gl_TessLevelOuter0:%\d+]] = OpCompositeExtract %float [[gl_TessLevelOuter]] 0
// CHECK-NEXT: [[gl_TessLevelOuter1:%\d+]] = OpCompositeExtract %float [[gl_TessLevelOuter]] 1
// CHECK-NEXT: [[gl_TessLevelOuter2:%\d+]] = OpCompositeExtract %float [[gl_TessLevelOuter]] 2
// CHECK-NEXT: [[tessLevelOuterArr3:%\d+]] = OpCompositeConstruct %_arr_float_uint_3 [[gl_TessLevelOuter0]] [[gl_TessLevelOuter1]] [[gl_TessLevelOuter2]]
// CHECK-NEXT: [[gl_TessLevelInner:%\d+]] = OpLoad %_arr_float_uint_2 %gl_TessLevelInner
// CHECK-NEXT: [[tessLevelOuterScalar:%\d+]] = OpCompositeExtract %float [[gl_TessLevelInner]] 0
// CHECK-NEXT: {{%\d+}} = OpCompositeConstruct %HS_CONSTANT_DATA_OUTPUT [[tessLevelOuterArr3]] [[tessLevelOuterScalar]]
struct HS_CONSTANT_DATA_OUTPUT
{
float Edges[3] : SV_TessFactor;
float Inside : SV_InsideTessFactor;
};
// Output control point (output of hull shader)
struct BEZIER_CONTROL_POINT
{
float3 vPosition : BEZIERPOS;
};
// The domain shader outputs
struct DS_OUTPUT
{
float4 vPosition : SV_POSITION;
};
[domain("tri")]
DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input,
float2 UV : SV_DomainLocation,
const OutputPatch<BEZIER_CONTROL_POINT, 3> bezpatch )
{
DS_OUTPUT Output;
return Output;
}

Просмотреть файл

@ -0,0 +1,88 @@
// Run: %dxc -T hs_6_0 -E SubDToBezierHS
// Test handling of built-in size mismatch (writing out to the built-ins):
// The HLSL SV_TessFactor is a float3, but the SPIR-V equivalent is a float4.
// The HLSL SV_InsideTessFactor is a scalar float, but the SPIR-V equivalent is a float2.
// CHECK: OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter
// CHECK: OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner
// CHECK: %HS_CONSTANT_DATA_OUTPUT = OpTypeStruct %_arr_float_uint_3 %float %_arr_v3float_uint_4 %_arr_v2float_uint_4 %_arr_v3float_uint_4 %_arr_v3float_uint_4 %v4float
// CHECK: %gl_TessLevelOuter = OpVariable %_ptr_Output__arr_float_uint_4 Output
// CHECK: %gl_TessLevelInner = OpVariable %_ptr_Output__arr_float_uint_2 Output
// CHECK: [[pcfRet:%\d+]] = OpFunctionCall %HS_CONSTANT_DATA_OUTPUT %SubDToBezierConstantsHS %param_var_ip %param_var_PatchID
// CHECK-NEXT: [[pcfRetTessFactor:%\d+]] = OpCompositeExtract %_arr_float_uint_3 [[pcfRet]] 0
// CHECK-NEXT: [[gl_TessLevelOuter_loc0:%\d+]] = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_0
// CHECK-NEXT: [[pcfRetTessFactor0:%\d+]] = OpCompositeExtract %float [[pcfRetTessFactor]] 0
// CHECK-NEXT: OpStore [[gl_TessLevelOuter_loc0]] [[pcfRetTessFactor0]]
// CHECK-NEXT: [[gl_TessLevelOuter_loc1:%\d+]] = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_1
// CHECK-NEXT: [[pcfRetTessFactor1:%\d+]] = OpCompositeExtract %float [[pcfRetTessFactor]] 1
// CHECK-NEXT: OpStore [[gl_TessLevelOuter_loc1]] [[pcfRetTessFactor1]]
// CHECK-NEXT: [[gl_TessLevelOuter_loc2:%\d+]] = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_2
// CHECK-NEXT: [[pcfRetTessFactor2:%\d+]] = OpCompositeExtract %float [[pcfRetTessFactor]] 2
// CHECK-NEXT: OpStore [[gl_TessLevelOuter_loc2]] [[pcfRetTessFactor2]]
// CHECK-NEXT: [[pcfRetInsideTessFactor:%\d+]] = OpCompositeExtract %float [[pcfRet]] 1
// CHECK-NEXT: [[gl_TessLevelInner_loc0:%\d+]] = OpAccessChain %_ptr_Output_float %gl_TessLevelInner %uint_0
// CHECK-NEXT: OpStore [[gl_TessLevelInner_loc0]] [[pcfRetInsideTessFactor]]
#define MAX_POINTS 3
// Input control point
struct VS_CONTROL_POINT_OUTPUT
{
float3 vPosition : WORLDPOS;
float2 vUV : TEXCOORD0;
float3 vTangent : TANGENT;
};
// Output control point
struct BEZIER_CONTROL_POINT
{
float3 vPosition : BEZIERPOS;
};
// Output patch constant data.
struct HS_CONSTANT_DATA_OUTPUT
{
float Edges[3] : SV_TessFactor;
float Inside : SV_InsideTessFactor;
float3 vTangent[4] : TANGENT;
float2 vUV[4] : TEXCOORD;
float3 vTanUCorner[4] : TANUCORNER;
float3 vTanVCorner[4] : TANVCORNER;
float4 vCWts : TANWEIGHTS;
};
// Patch Constant Function
HS_CONSTANT_DATA_OUTPUT SubDToBezierConstantsHS(InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POINTS> ip, uint PatchID : SV_PrimitiveID) {
HS_CONSTANT_DATA_OUTPUT Output;
// Must initialize Edges and Inside; otherwise HLSL validation will fail.
Output.Edges[0] = 1.0;
Output.Edges[1] = 2.0;
Output.Edges[2] = 3.0;
Output.Inside = 4.0;
return Output;
}
[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_ccw")]
[outputcontrolpoints(MAX_POINTS)]
[patchconstantfunc("SubDToBezierConstantsHS")]
BEZIER_CONTROL_POINT SubDToBezierHS(InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POINTS> ip, uint cpid : SV_OutputControlPointID, uint PatchID : SV_PrimitiveID) {
VS_CONTROL_POINT_OUTPUT vsOutput;
BEZIER_CONTROL_POINT result;
result.vPosition = vsOutput.vPosition;
return result;
}

Просмотреть файл

@ -396,12 +396,18 @@ TEST_F(FileTest, SemanticDomainLocationDS) {
TEST_F(FileTest, SemanticTessFactorDS) {
runFileTest("semantic.tess-factor.ds.hlsl");
}
TEST_F(FileTest, SemanticTessFactorSizeMismatchDS) {
runFileTest("semantic.tess-factor.size-mismatch.ds.hlsl");
}
TEST_F(FileTest, SemanticInsideTessFactorDS) {
runFileTest("semantic.inside-tess-factor.ds.hlsl");
}
TEST_F(FileTest, SemanticTessFactorHS) {
runFileTest("semantic.tess-factor.hs.hlsl");
}
TEST_F(FileTest, SemanticTessFactorSizeMismatchHS) {
runFileTest("semantic.tess-factor.size-mismatch.hs.hlsl");
}
TEST_F(FileTest, SemanticInsideTessFactorHS) {
runFileTest("semantic.inside-tess-factor.hs.hlsl");
}