Skip to content

Commit

Permalink
[SPIR-V] add -fvk-support-nonzero-base-vertex option (#6865)
Browse files Browse the repository at this point in the history
There is already a workaround for the SV_BaseInstance inconsistency
between HLSL and SPIR-V. The same issue applies to SV_BaseVertex, but no
such option is available. This commit adds the
-fvk-support-nonzero-base-vertex option, which behaves the same as the
-fvk-support-nonzero-base-instance option.
  • Loading branch information
artamonovoleg authored Sep 3, 2024
1 parent b766b43 commit bb3a0c3
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 15 deletions.
2 changes: 2 additions & 0 deletions include/dxc/Support/HLSLOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,8 @@ def fvk_use_dx_position_w: Flag<["-"], "fvk-use-dx-position-w">, Group<spirv_Gro
HelpText<"Reciprocate SV_Position.w after reading from stage input in PS to accommodate the difference between Vulkan and DirectX">;
def fvk_support_nonzero_base_instance: Flag<["-"], "fvk-support-nonzero-base-instance">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
HelpText<"Follow Vulkan spec to use gl_BaseInstance as the first vertex instance, which makes SV_InstanceID = gl_InstanceIndex - gl_BaseInstance (without this option, SV_InstanceID = gl_InstanceIndex)">;
def fvk_support_nonzero_base_vertex: Flag<["-"], "fvk-support-nonzero-base-vertex">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
HelpText<"Follow Vulkan spec to use gl_BaseVertex as the first vertex, which makes SV_VertexID = gl_VertexIndex - gl_BaseVertex (without this option, SV_VertexID = gl_VertexIndex)">;
def fvk_use_gl_layout: Flag<["-"], "fvk-use-gl-layout">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
HelpText<"Use strict OpenGL std140/std430 memory layout for Vulkan resources">;
def fvk_use_dx_layout: Flag<["-"], "fvk-use-dx-layout">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
Expand Down
1 change: 1 addition & 0 deletions include/dxc/Support/SPIRVOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ struct SpirvCodeGenOptions {
bool reduceLoadSize;
bool autoShiftBindings;
bool supportNonzeroBaseInstance;
bool supportNonzeroBaseVertex;
bool fixFuncCallArguments;
bool allowRWStructuredBufferArrays;
bool enableMaximalReconvergence;
Expand Down
2 changes: 2 additions & 0 deletions lib/DxcSupport/HLSLOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,8 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
Args.hasFlag(OPT_fvk_use_dx_position_w, OPT_INVALID, false);
opts.SpirvOptions.supportNonzeroBaseInstance =
Args.hasFlag(OPT_fvk_support_nonzero_base_instance, OPT_INVALID, false);
opts.SpirvOptions.supportNonzeroBaseVertex =
Args.hasFlag(OPT_fvk_support_nonzero_base_vertex, OPT_INVALID, false);
opts.SpirvOptions.useGlLayout =
Args.hasFlag(OPT_fvk_use_gl_layout, OPT_INVALID, false);
opts.SpirvOptions.useDxLayout =
Expand Down
58 changes: 51 additions & 7 deletions tools/clang/lib/SPIRV/DeclResultIdMapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3392,21 +3392,54 @@ SpirvVariable *DeclResultIdMapper::getInstanceIdFromIndexAndBase(
return instanceIdVar;
}

SpirvVariable *DeclResultIdMapper::getBaseInstanceVariable(
SemanticInfo *semantic, const hlsl::SigPoint *sigPoint, QualType type) {
SpirvVariable *
DeclResultIdMapper::getVertexIdFromIndexAndBase(SpirvVariable *vertexIndexVar,
SpirvVariable *baseVertexVar) {
QualType type = vertexIndexVar->getAstResultType();
auto *vertexIdVar = spvBuilder.addFnVar(
type, vertexIndexVar->getSourceLocation(), "SV_VertexID");
auto *vertexIndexValue = spvBuilder.createLoad(
type, vertexIndexVar, vertexIndexVar->getSourceLocation());
auto *baseVertexValue = spvBuilder.createLoad(
type, baseVertexVar, vertexIndexVar->getSourceLocation());
auto *vertexIdValue = spvBuilder.createBinaryOp(
spv::Op::OpISub, type, vertexIndexValue, baseVertexValue,
vertexIndexVar->getSourceLocation());
spvBuilder.createStore(vertexIdVar, vertexIdValue,
vertexIndexVar->getSourceLocation());
return vertexIdVar;
}

SpirvVariable *
DeclResultIdMapper::getBaseInstanceVariable(const hlsl::SigPoint *sigPoint,
QualType type) {
assert(type->isSpecificBuiltinType(BuiltinType::Kind::Int) ||
type->isSpecificBuiltinType(BuiltinType::Kind::UInt));
auto *baseInstanceVar = spvBuilder.addStageBuiltinVar(
type, spv::StorageClass::Input, spv::BuiltIn::BaseInstance, false,
semantic->loc);
StageVar var(sigPoint, *semantic, nullptr, type,
type, spv::StorageClass::Input, spv::BuiltIn::BaseInstance, false, {});
StageVar var(sigPoint, {}, nullptr, type,
getLocationAndComponentCount(astContext, type));
var.setSpirvInstr(baseInstanceVar);
var.setIsSpirvBuiltin();
stageVars.push_back(var);
return baseInstanceVar;
}

SpirvVariable *
DeclResultIdMapper::getBaseVertexVariable(const hlsl::SigPoint *sigPoint,
QualType type) {
assert(type->isSpecificBuiltinType(BuiltinType::Kind::Int) ||
type->isSpecificBuiltinType(BuiltinType::Kind::UInt));
auto *baseVertexVar = spvBuilder.addStageBuiltinVar(
type, spv::StorageClass::Input, spv::BuiltIn::BaseVertex, false, {});
StageVar var(sigPoint, {}, nullptr, type,
getLocationAndComponentCount(astContext, type));
var.setSpirvInstr(baseVertexVar);
var.setIsSpirvBuiltin();
stageVars.push_back(var);
return baseVertexVar;
}

SpirvVariable *DeclResultIdMapper::createSpirvInterfaceVariable(
const StageVarDataBundle &stageVarData) {
// The evalType will be the type of the interface variable in SPIR-V.
Expand Down Expand Up @@ -3493,13 +3526,24 @@ SpirvVariable *DeclResultIdMapper::createSpirvInterfaceVariable(
// The above call to createSpirvStageVar creates the gl_InstanceIndex.
// We should now manually create the gl_BaseInstance variable and do the
// subtraction.
auto *baseInstanceVar = getBaseInstanceVariable(
stageVarData.semantic, stageVarData.sigPoint, stageVarData.type);
auto *baseInstanceVar =
getBaseInstanceVariable(stageVarData.sigPoint, stageVarData.type);

// SPIR-V code for 'SV_InstanceID = gl_InstanceIndex - gl_BaseInstance'
varInstr = getInstanceIdFromIndexAndBase(varInstr, baseInstanceVar);
}

if (spirvOptions.supportNonzeroBaseVertex &&
stageVarData.semantic->getKind() == hlsl::Semantic::Kind::VertexID &&
stageVarData.sigPoint->GetKind() == hlsl::SigPoint::Kind::VSIn) {

auto *baseVertexVar =
getBaseVertexVariable(stageVarData.sigPoint, stageVarData.type);

// SPIR-V code for 'SV_VertexID = gl_VertexIndex - gl_BaseVertex'
varInstr = getVertexIdFromIndexAndBase(varInstr, baseVertexVar);
}

// We have semantics attached to this decl, which means it must be a
// function/parameter/variable. All are DeclaratorDecls.
stageVarInstructions[cast<DeclaratorDecl>(stageVarData.decl)] = varInstr;
Expand Down
32 changes: 24 additions & 8 deletions tools/clang/lib/SPIRV/DeclResultIdMapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -791,24 +791,40 @@ class DeclResultIdMapper {
SpirvVariable *getInstanceIdFromIndexAndBase(SpirvVariable *instanceIndexVar,
SpirvVariable *baseInstanceVar);

// Creates a function scope variable to represent the "SV_VertexID"
// semantic, which is not immediately available in SPIR-V. Its value will be
// set by subtracting the values of the given InstanceIndex and base instance
// variables.
//
// vertexIndexVar: The SPIR-V input variable decorated with
// vertexIndex.
//
// baseVertexVar: The SPIR-V input variable decorated with
// BaseVertex.
SpirvVariable *getVertexIdFromIndexAndBase(SpirvVariable *vertexIndexVar,
SpirvVariable *baseVertexVar);

// Creates and returns a variable that is the BaseInstance builtin input. The
// variable is also added to the list of stage variable `this->stageVars`. Its
// type will be a 32-bit integer.
//
// The semantic is a lie. We currently give it the semantic for the
// InstanceID. I'm not sure what would happen if we did not use a semantic, or
// tried to generate the correct one. I'm guessing there would be some issue
// with reflection.
// sigPoint: the signature point identifying which shader stage the variable
// will be used in.
//
// semantic: the semantic to attach to this variable
// type: The type to use for the new variable. Must be int or unsigned int.
SpirvVariable *getBaseInstanceVariable(const hlsl::SigPoint *sigPoint,
QualType type);

// Creates and returns a variable that is the BaseVertex builtin input. The
// variable is also added to the list of stage variable `this->stageVars`. Its
// type will be a 32-bit integer.
//
// sigPoint: the signature point identifying which shader stage the variable
// will be used in.
//
// type: The type to use for the new variable. Must be int or unsigned int.
SpirvVariable *getBaseInstanceVariable(SemanticInfo *semantic,
const hlsl::SigPoint *sigPoint,
QualType type);
SpirvVariable *getBaseVertexVariable(const hlsl::SigPoint *sigPoint,
QualType type);

// Creates and return a new interface variable from the information provided.
// The new variable with be add to `this->StageVars`.
Expand Down
29 changes: 29 additions & 0 deletions tools/clang/test/CodeGenSPIRV/semantic.nonzero-base-vertex.vs.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: %dxc -T vs_6_0 -E main -fvk-support-nonzero-base-vertex -fcgl %s -spirv | FileCheck %s

// CHECK: OpEntryPoint Vertex %main "main"
// CHECK-SAME: %gl_VertexIndex
// CHECK-SAME: [[gl_BaseVertex:%[0-9]+]]
// CHECK-SAME: %out_var_A

// CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
// CHECK: OpDecorate [[gl_BaseVertex]] BuiltIn BaseVertex
// CHECK: OpDecorate %out_var_A Location 0

// CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_int Input
// CHECK: [[gl_BaseVertex]] = OpVariable %_ptr_Input_int Input
// CHECK: %out_var_A = OpVariable %_ptr_Output_int Output

// CHECK: %main = OpFunction
// CHECK: %SV_VertexID = OpVariable %_ptr_Function_int Function
// CHECK: [[gl_VertexIndex:%[0-9]+]] = OpLoad %int %gl_VertexIndex
// CHECK: [[base_vertex:%[0-9]+]] = OpLoad %int [[gl_BaseVertex]]
// CHECK: [[vertex_id:%[0-9]+]] = OpISub %int [[gl_VertexIndex]] [[base_vertex]]
// CHECK: OpStore %SV_VertexID [[vertex_id]]
// CHECK: [[vertex_id_0:%[0-9]+]] = OpLoad %int %SV_VertexID
// CHECK: OpStore %param_var_input [[vertex_id_0]]
// CHECK: {{%[0-9]+}} = OpFunctionCall %int %src_main %param_var_input

int main(int input: SV_VertexID) : A {
return input;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: %dxc -T vs_6_0 -E main -fvk-support-nonzero-base-vertex -fcgl %s -spirv | FileCheck %s

// CHECK: OpEntryPoint Vertex %main "main"
// CHECK-SAME: %gl_VertexIndex
// CHECK-SAME: [[gl_BaseVertex:%[0-9]+]]
// CHECK-SAME: %out_var_A

// CHECK: OpDecorate %gl_VertexIndex BuiltIn VertexIndex
// CHECK: OpDecorate [[gl_BaseVertex]] BuiltIn BaseVertex
// CHECK: OpDecorate %out_var_A Location 0

// CHECK: %gl_VertexIndex = OpVariable %_ptr_Input_uint Input
// CHECK: [[gl_BaseVertex]] = OpVariable %_ptr_Input_uint Input
// CHECK: %out_var_A = OpVariable %_ptr_Output_uint Output

// CHECK: %main = OpFunction
// CHECK: %SV_VertexID = OpVariable %_ptr_Function_uint Function
// CHECK: [[gl_VertexIndex:%[0-9]+]] = OpLoad %uint %gl_VertexIndex
// CHECK: [[base_vertex:%[0-9]+]] = OpLoad %uint [[gl_BaseVertex]]
// CHECK: [[vertex_id:%[0-9]+]] = OpISub %uint [[gl_VertexIndex]] [[base_vertex]]
// CHECK: OpStore %SV_VertexID [[vertex_id]]
// CHECK: [[vertex_id_0:%[0-9]+]] = OpLoad %uint %SV_VertexID
// CHECK: OpStore %param_var_input [[vertex_id_0]]
// CHECK: {{%[0-9]+}} = OpFunctionCall %uint %src_main %param_var_input

unsigned int main(unsigned int input: SV_VertexID) : A {
return input;
}

0 comments on commit bb3a0c3

Please sign in to comment.