Browse Source

[spirv] Add support for -fvk-invert-y (#967)

This is to accommodate Vulkan's coordinate system, which is different
from DX's.
Lei Zhang 7 years ago
parent
commit
f9d613b795

+ 3 - 0
docs/SPIR-V.rst

@@ -2316,6 +2316,9 @@ codegen for Vulkan:
 - ``-fvk-ignore-unused-resources``: Avoids emitting SPIR-V code for resources
 - ``-fvk-ignore-unused-resources``: Avoids emitting SPIR-V code for resources
   defined but not statically referenced by the call tree of the entry point
   defined but not statically referenced by the call tree of the entry point
   in question.
   in question.
+- ``-fvk-invert-y``: Inverts SV_Position.y before writing to stage output.
+  Used to accommodate the difference between Vulkan's coordinate system and
+  DirectX's. Only allowed in VS/DS/GS.
 - ``-fvk-stage-io-order={alpha|decl}``: Assigns the stage input/output variable
 - ``-fvk-stage-io-order={alpha|decl}``: Assigns the stage input/output variable
   location number according to alphabetical order or declaration order. See
   location number according to alphabetical order or declaration order. See
   `HLSL semantic and Vulkan Location`_ for more details.
   `HLSL semantic and Vulkan Location`_ for more details.

+ 1 - 0
include/dxc/Support/HLSLOptions.h

@@ -159,6 +159,7 @@ public:
 #ifdef ENABLE_SPIRV_CODEGEN
 #ifdef ENABLE_SPIRV_CODEGEN
   bool GenSPIRV; // OPT_spirv
   bool GenSPIRV; // OPT_spirv
   bool VkIgnoreUnusedResources; // OPT_fvk_ignore_used_resources
   bool VkIgnoreUnusedResources; // OPT_fvk_ignore_used_resources
+  bool VkInvertY; // OPT_fvk_invert_y
   llvm::StringRef VkStageIoOrder; // OPT_fvk_stage_io_order
   llvm::StringRef VkStageIoOrder; // OPT_fvk_stage_io_order
   llvm::SmallVector<uint32_t, 4> VkBShift; // OPT_fvk_b_shift
   llvm::SmallVector<uint32_t, 4> VkBShift; // OPT_fvk_b_shift
   llvm::SmallVector<uint32_t, 4> VkTShift; // OPT_fvk_t_shift
   llvm::SmallVector<uint32_t, 4> VkTShift; // OPT_fvk_t_shift

+ 2 - 0
include/dxc/Support/HLSLOptions.td

@@ -248,6 +248,8 @@ def fvk_s_shift : MultiArg<["-"], "fvk-s-shift", 2>, MetaVarName<"<shift> <space
   HelpText<"Specify Vulkan binding number shift for s-type register">;
   HelpText<"Specify Vulkan binding number shift for s-type register">;
 def fvk_u_shift : MultiArg<["-"], "fvk-u-shift", 2>, MetaVarName<"<shift> <space>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
 def fvk_u_shift : MultiArg<["-"], "fvk-u-shift", 2>, MetaVarName<"<shift> <space>">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
   HelpText<"Specify Vulkan binding number shift for u-type register">;
   HelpText<"Specify Vulkan binding number shift for u-type register">;
+def fvk_invert_y: Flag<["-"], "fvk-invert-y">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
+  HelpText<"Invert SV_Position.y in VS/DS/GS to accommodate Vulkan's coordinate system">;
 // SPIRV Change Ends
 // SPIRV Change Ends
 
 
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////

+ 2 - 0
lib/DxcSupport/HLSLOptions.cpp

@@ -481,6 +481,7 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
   // SPIRV Change Starts
   // SPIRV Change Starts
 #ifdef ENABLE_SPIRV_CODEGEN
 #ifdef ENABLE_SPIRV_CODEGEN
   const bool genSpirv = opts.GenSPIRV = Args.hasFlag(OPT_spirv, OPT_INVALID, false);
   const bool genSpirv = opts.GenSPIRV = Args.hasFlag(OPT_spirv, OPT_INVALID, false);
+  opts.VkInvertY = Args.hasFlag(OPT_fvk_invert_y, OPT_INVALID, false);
   opts.VkIgnoreUnusedResources = Args.hasFlag(OPT_fvk_ignore_unused_resources, OPT_INVALID, false);
   opts.VkIgnoreUnusedResources = Args.hasFlag(OPT_fvk_ignore_unused_resources, OPT_INVALID, false);
 
 
   // Collects the arguments for -fvk-{b|s|t|u}-shift.
   // Collects the arguments for -fvk-{b|s|t|u}-shift.
@@ -519,6 +520,7 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
   }
   }
 #else
 #else
   if (Args.hasFlag(OPT_spirv, OPT_INVALID, false) ||
   if (Args.hasFlag(OPT_spirv, OPT_INVALID, false) ||
+      Args.hasFlag(OPT_fvk_invert_y, OPT_INVALID, false) ||
       Args.hasFlag(OPT_fvk_ignore_unused_resources, OPT_INVALID, false) ||
       Args.hasFlag(OPT_fvk_ignore_unused_resources, OPT_INVALID, false) ||
       !Args.getLastArgValue(OPT_fvk_stage_io_order_EQ).empty() ||
       !Args.getLastArgValue(OPT_fvk_stage_io_order_EQ).empty() ||
       !Args.getLastArgValue(OPT_fvk_b_shift).empty() ||
       !Args.getLastArgValue(OPT_fvk_b_shift).empty() ||

+ 1 - 0
tools/clang/include/clang/SPIRV/EmitSPIRVOptions.h

@@ -19,6 +19,7 @@ struct EmitSPIRVOptions {
   bool codeGenHighLevel;
   bool codeGenHighLevel;
   bool defaultRowMajor;
   bool defaultRowMajor;
   bool disableValidation;
   bool disableValidation;
+  bool invertY;
   bool ignoreUnusedResources;
   bool ignoreUnusedResources;
   bool enable16BitTypes;
   bool enable16BitTypes;
   llvm::StringRef stageIoOrder;
   llvm::StringRef stageIoOrder;

+ 10 - 3
tools/clang/include/clang/SPIRV/ModuleBuilder.h

@@ -111,6 +111,13 @@ public:
   uint32_t createCompositeExtract(uint32_t resultType, uint32_t composite,
   uint32_t createCompositeExtract(uint32_t resultType, uint32_t composite,
                                   llvm::ArrayRef<uint32_t> indexes);
                                   llvm::ArrayRef<uint32_t> indexes);
 
 
+  /// \brief Creates a composite insert instruction. The given object will
+  /// replace the component in the composite at the given indices. Returns the
+  /// <result-id> for the new composite.
+  uint32_t createCompositeInsert(uint32_t resultType, uint32_t composite,
+                                 llvm::ArrayRef<uint32_t> indices,
+                                 uint32_t object);
+
   /// \brief Creates a vector shuffle instruction of selecting from the two
   /// \brief Creates a vector shuffle instruction of selecting from the two
   /// vectors using selectors and returns the <result-id> of the result vector.
   /// vectors using selectors and returns the <result-id> of the result vector.
   uint32_t createVectorShuffle(uint32_t resultType, uint32_t vector1,
   uint32_t createVectorShuffle(uint32_t resultType, uint32_t vector1,
@@ -211,9 +218,9 @@ public:
   /// If compareVal is given a non-zero value, OpImageDrefGather or
   /// If compareVal is given a non-zero value, OpImageDrefGather or
   /// OpImageSparseDrefGather will be generated; otherwise, OpImageGather or
   /// OpImageSparseDrefGather will be generated; otherwise, OpImageGather or
   /// OpImageSparseGather will be generated.
   /// OpImageSparseGather will be generated.
-  /// If residencyCodeId is not zero, the sparse version of the instructions will
-  /// be used, and the SPIR-V instruction for storing the resulting residency
-  /// code will also be emitted.
+  /// If residencyCodeId is not zero, the sparse version of the instructions
+  /// will be used, and the SPIR-V instruction for storing the resulting
+  /// residency code will also be emitted.
   uint32_t createImageGather(uint32_t texelType, uint32_t imageType,
   uint32_t createImageGather(uint32_t texelType, uint32_t imageType,
                              uint32_t image, uint32_t sampler,
                              uint32_t image, uint32_t sampler,
                              uint32_t coordinate, uint32_t component,
                              uint32_t coordinate, uint32_t component,

+ 12 - 0
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -1343,6 +1343,18 @@ bool DeclResultIdMapper::writeBackOutputStream(const ValueDecl *decl,
     // We should have recorded its stage output variable previously.
     // We should have recorded its stage output variable previously.
     assert(found != stageVarIds.end());
     assert(found != stageVarIds.end());
 
 
+    // Negate SV_Position.y if requested
+    if (spirvOptions.invertY &&
+        semanticInfo.semantic->GetKind() == hlsl::Semantic::Kind::Position) {
+
+      const auto f32Type = theBuilder.getFloat32Type();
+      const auto v4f32Type = theBuilder.getVecType(f32Type, 4);
+      const auto oldY = theBuilder.createCompositeExtract(f32Type, value, {1});
+      const auto newY =
+          theBuilder.createUnaryOp(spv::Op::OpFNegate, f32Type, oldY);
+      value = theBuilder.createCompositeInsert(v4f32Type, value, {1}, newY);
+    }
+
     theBuilder.createStore(found->second, value);
     theBuilder.createStore(found->second, value);
     return true;
     return true;
   }
   }

+ 1 - 1
tools/clang/lib/SPIRV/DeclResultIdMapper.h

@@ -599,7 +599,7 @@ DeclResultIdMapper::DeclResultIdMapper(const hlsl::ShaderModel &model,
       astContext(context), diags(context.getDiagnostics()),
       astContext(context), diags(context.getDiagnostics()),
       typeTranslator(context, builder, diags, options), entryFunctionId(0),
       typeTranslator(context, builder, diags, options), entryFunctionId(0),
       needsLegalization(false),
       needsLegalization(false),
-      glPerVertex(model, context, builder, typeTranslator) {}
+      glPerVertex(model, context, builder, typeTranslator, options.invertY) {}
 
 
 bool DeclResultIdMapper::decorateStageIOLocations() {
 bool DeclResultIdMapper::decorateStageIOLocations() {
   // Try both input and output even if input location assignment failed
   // Try both input and output even if input location assignment failed

+ 20 - 7
tools/clang/lib/SPIRV/GlPerVertex.cpp

@@ -57,12 +57,14 @@ inline bool hasGSPrimitiveTypeQualifier(const DeclaratorDecl *decl) {
 } // anonymous namespace
 } // anonymous namespace
 
 
 GlPerVertex::GlPerVertex(const hlsl::ShaderModel &sm, ASTContext &context,
 GlPerVertex::GlPerVertex(const hlsl::ShaderModel &sm, ASTContext &context,
-                         ModuleBuilder &builder, TypeTranslator &translator)
+                         ModuleBuilder &builder, TypeTranslator &translator,
+                         bool negateY)
     : shaderModel(sm), astContext(context), theBuilder(builder),
     : shaderModel(sm), astContext(context), theBuilder(builder),
-      typeTranslator(translator), inIsGrouped(true), outIsGrouped(true),
-      inBlockVar(0), outBlockVar(0), inClipVar(0), inCullVar(0), outClipVar(0),
-      outCullVar(0), inArraySize(0), outArraySize(0), inClipArraySize(1),
-      outClipArraySize(1), inCullArraySize(1), outCullArraySize(1) {}
+      typeTranslator(translator), invertY(negateY), inIsGrouped(true),
+      outIsGrouped(true), inBlockVar(0), outBlockVar(0), inClipVar(0),
+      inCullVar(0), outClipVar(0), outCullVar(0), inArraySize(0),
+      outArraySize(0), inClipArraySize(1), outClipArraySize(1),
+      inCullArraySize(1), outCullArraySize(1) {}
 
 
 void GlPerVertex::generateVars(uint32_t inArrayLen, uint32_t outArrayLen) {
 void GlPerVertex::generateVars(uint32_t inArrayLen, uint32_t outArrayLen) {
   // Calling this method twice is an internal error.
   // Calling this method twice is an internal error.
@@ -624,8 +626,7 @@ bool GlPerVertex::readField(hlsl::Semantic::Kind semanticKind,
 }
 }
 
 
 void GlPerVertex::writePositionOrPointSize(
 void GlPerVertex::writePositionOrPointSize(
-    bool isPosition, llvm::Optional<uint32_t> invocationId,
-    uint32_t value) const {
+    bool isPosition, llvm::Optional<uint32_t> invocationId, uint32_t value) {
   // We do not handle stand-alone Position/PointSize builtin here.
   // We do not handle stand-alone Position/PointSize builtin here.
   assert(outIsGrouped);
   assert(outIsGrouped);
 
 
@@ -643,6 +644,17 @@ void GlPerVertex::writePositionOrPointSize(
     // locate the Position/PointSize builtin.
     // locate the Position/PointSize builtin.
     const uint32_t ptr =
     const uint32_t ptr =
         theBuilder.createAccessChain(ptrType, outBlockVar, {fieldIndex});
         theBuilder.createAccessChain(ptrType, outBlockVar, {fieldIndex});
+
+    if (isPosition && invertY) {
+      if (shaderModel.IsVS() || shaderModel.IsDS()) {
+        const auto oldY =
+            theBuilder.createCompositeExtract(f32Type, value, {1});
+        const auto newY =
+            theBuilder.createUnaryOp(spv::Op::OpFNegate, f32Type, oldY);
+        value = theBuilder.createCompositeInsert(fieldType, value, {1}, newY);
+      }
+    }
+
     theBuilder.createStore(ptr, value);
     theBuilder.createStore(ptr, value);
     return;
     return;
   }
   }
@@ -661,6 +673,7 @@ void GlPerVertex::writePositionOrPointSize(
   // and the second one is the struct index.
   // and the second one is the struct index.
   const uint32_t ptr = theBuilder.createAccessChain(ptrType, outBlockVar,
   const uint32_t ptr = theBuilder.createAccessChain(ptrType, outBlockVar,
                                                     {arrayIndex, fieldIndex});
                                                     {arrayIndex, fieldIndex});
+
   theBuilder.createStore(ptr, value);
   theBuilder.createStore(ptr, value);
 }
 }
 
 

+ 6 - 2
tools/clang/lib/SPIRV/GlPerVertex.h

@@ -57,7 +57,7 @@ namespace spirv {
 class GlPerVertex {
 class GlPerVertex {
 public:
 public:
   GlPerVertex(const hlsl::ShaderModel &sm, ASTContext &context,
   GlPerVertex(const hlsl::ShaderModel &sm, ASTContext &context,
-              ModuleBuilder &builder, TypeTranslator &translator);
+              ModuleBuilder &builder, TypeTranslator &translator, bool negateY);
 
 
   /// Records a declaration of SV_ClipDistance/SV_CullDistance so later
   /// Records a declaration of SV_ClipDistance/SV_CullDistance so later
   /// we can caculate the ClipDistance/CullDistance array layout.
   /// we can caculate the ClipDistance/CullDistance array layout.
@@ -140,7 +140,7 @@ private:
   /// Emits SPIR-V instructions for writing the Position/PointSize builtin.
   /// Emits SPIR-V instructions for writing the Position/PointSize builtin.
   void writePositionOrPointSize(bool isPosition,
   void writePositionOrPointSize(bool isPosition,
                                 llvm::Optional<uint32_t> invocationId,
                                 llvm::Optional<uint32_t> invocationId,
-                                uint32_t value) const;
+                                uint32_t value);
   /// Emits SPIR-V instructions for writing data into the ClipDistance/
   /// Emits SPIR-V instructions for writing data into the ClipDistance/
   /// CullDistance builtin starting from offset. The value to be written is
   /// CullDistance builtin starting from offset. The value to be written is
   /// fromValue, whose type is fromType. Necessary transformations will be
   /// fromValue, whose type is fromType. Necessary transformations will be
@@ -166,6 +166,10 @@ private:
   ModuleBuilder &theBuilder;
   ModuleBuilder &theBuilder;
   TypeTranslator &typeTranslator;
   TypeTranslator &typeTranslator;
 
 
+  /// Indicates whether to invert SV_Position.y to accommodate Vulkan's
+  /// coordinate system
+  bool invertY;
+
   /// We can have Position, ClipDistance, and CullDistance either grouped (G)
   /// We can have Position, ClipDistance, and CullDistance either grouped (G)
   /// into the gl_PerVertex struct, or separated (S) as stand-alone variables.
   /// into the gl_PerVertex struct, or separated (S) as stand-alone variables.
   /// The following table shows for each shader stage, which one is used:
   /// The following table shows for each shader stage, which one is used:

+ 19 - 8
tools/clang/lib/SPIRV/ModuleBuilder.cpp

@@ -148,6 +148,19 @@ ModuleBuilder::createCompositeExtract(uint32_t resultType, uint32_t composite,
   return resultId;
   return resultId;
 }
 }
 
 
+uint32_t ModuleBuilder::createCompositeInsert(uint32_t resultType,
+                                              uint32_t composite,
+                                              llvm::ArrayRef<uint32_t> indices,
+                                              uint32_t object) {
+  assert(insertPoint && "null insert point");
+  const uint32_t resultId = theContext.takeNextId();
+  instBuilder
+      .opCompositeInsert(resultType, resultId, object, composite, indices)
+      .x();
+  insertPoint->appendInstruction(std::move(constructSite));
+  return resultId;
+}
+
 uint32_t
 uint32_t
 ModuleBuilder::createVectorShuffle(uint32_t resultType, uint32_t vector1,
 ModuleBuilder::createVectorShuffle(uint32_t resultType, uint32_t vector1,
                                    uint32_t vector2,
                                    uint32_t vector2,
@@ -773,14 +786,13 @@ void ModuleBuilder::decorate(uint32_t targetId, spv::Decoration decoration) {
 }
 }
 
 
 #define IMPL_GET_PRIMITIVE_TYPE(ty)                                            \
 #define IMPL_GET_PRIMITIVE_TYPE(ty)                                            \
-  \
-uint32_t ModuleBuilder::get##ty##Type() {                                      \
+                                                                               \
+  uint32_t ModuleBuilder::get##ty##Type() {                                    \
     const Type *type = Type::get##ty(theContext);                              \
     const Type *type = Type::get##ty(theContext);                              \
     const uint32_t typeId = theContext.getResultIdForType(type);               \
     const uint32_t typeId = theContext.getResultIdForType(type);               \
     theModule.addType(type, typeId);                                           \
     theModule.addType(type, typeId);                                           \
     return typeId;                                                             \
     return typeId;                                                             \
-  \
-}
+  }
 
 
 IMPL_GET_PRIMITIVE_TYPE(Void)
 IMPL_GET_PRIMITIVE_TYPE(Void)
 IMPL_GET_PRIMITIVE_TYPE(Bool)
 IMPL_GET_PRIMITIVE_TYPE(Bool)
@@ -1049,16 +1061,15 @@ uint32_t ModuleBuilder::getConstantBool(bool value) {
 }
 }
 
 
 #define IMPL_GET_PRIMITIVE_CONST(builderTy, cppTy)                             \
 #define IMPL_GET_PRIMITIVE_CONST(builderTy, cppTy)                             \
-  \
-uint32_t ModuleBuilder::getConstant##builderTy(cppTy value) {                  \
+                                                                               \
+  uint32_t ModuleBuilder::getConstant##builderTy(cppTy value) {                \
     const uint32_t typeId = get##builderTy##Type();                            \
     const uint32_t typeId = get##builderTy##Type();                            \
     const Constant *constant =                                                 \
     const Constant *constant =                                                 \
         Constant::get##builderTy(theContext, typeId, value);                   \
         Constant::get##builderTy(theContext, typeId, value);                   \
     const uint32_t constId = theContext.getResultIdForConstant(constant);      \
     const uint32_t constId = theContext.getResultIdForConstant(constant);      \
     theModule.addConstant(constant, constId);                                  \
     theModule.addConstant(constant, constId);                                  \
     return constId;                                                            \
     return constId;                                                            \
-  \
-}
+  }
 
 
 IMPL_GET_PRIMITIVE_CONST(Int16, int16_t)
 IMPL_GET_PRIMITIVE_CONST(Int16, int16_t)
 IMPL_GET_PRIMITIVE_CONST(Int32, int32_t)
 IMPL_GET_PRIMITIVE_CONST(Int32, int32_t)

+ 3 - 0
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -395,6 +395,9 @@ SPIRVEmitter::SPIRVEmitter(CompilerInstance &ci,
       seenPushConstantAt(), needsLegalization(false) {
       seenPushConstantAt(), needsLegalization(false) {
   if (shaderModel.GetKind() == hlsl::ShaderModel::Kind::Invalid)
   if (shaderModel.GetKind() == hlsl::ShaderModel::Kind::Invalid)
     emitError("unknown shader module: %0", {}) << shaderModel.GetName();
     emitError("unknown shader module: %0", {}) << shaderModel.GetName();
+  if (options.invertY &&
+      !(shaderModel.IsVS() || shaderModel.IsDS() || shaderModel.IsGS()))
+    emitError("-fvk-invert-y can only be used in VS/DS/GS", {});
 }
 }
 
 
 void SPIRVEmitter::HandleTranslationUnit(ASTContext &context) {
 void SPIRVEmitter::HandleTranslationUnit(ASTContext &context) {

+ 33 - 0
tools/clang/test/CodeGenSPIRV/vk.cloption.invert-y.ds.hlsl

@@ -0,0 +1,33 @@
+// Run: %dxc -T ds_6_0 -E main -fvk-invert-y
+
+// HS PCF output
+struct HsPcfOut {
+  float  outTessFactor[4]   : SV_TessFactor;
+  float  inTessFactor[2]    : SV_InsideTessFactor;
+};
+
+// Per-vertex input structs
+struct DsCpIn {
+    float4 pos : SV_Position;
+};
+
+// Per-vertex output structs
+struct DsCpOut {
+    float4 pos : SV_Position;
+};
+
+[domain("quad")]
+DsCpOut main(OutputPatch<DsCpIn, 3> patch,
+             HsPcfOut pcfData) {
+  DsCpOut dsOut;
+  dsOut = (DsCpOut)0;
+  return dsOut;
+}
+
+// CHECK:      [[call:%\d+]] = OpFunctionCall %DsCpIn %src_main %param_var_patch %param_var_pcfData
+// CHECK-NEXT:  [[val:%\d+]] = OpCompositeExtract %v4float [[call]] 0
+// CHECK-NEXT:  [[ptr:%\d+]] = OpAccessChain %_ptr_Output_v4float %gl_PerVertexOut %uint_0
+// CHECK-NEXT: [[oldY:%\d+]] = OpCompositeExtract %float [[val]] 1
+// CHECK-NEXT: [[newY:%\d+]] = OpFNegate %float [[oldY]]
+// CHECK-NEXT:  [[pos:%\d+]] = OpCompositeInsert %v4float [[newY]] [[val]] 1
+// CHECK-NEXT:                 OpStore [[ptr]] [[pos]]

+ 29 - 0
tools/clang/test/CodeGenSPIRV/vk.cloption.invert-y.gs.hlsl

@@ -0,0 +1,29 @@
+// Run: %dxc -T gs_6_0 -E main -fvk-invert-y
+
+// GS per-vertex input
+struct GsVIn {
+    float4 pos : SV_Position;
+};
+
+// GS per-vertex output
+struct GsVOut {
+    float4 pos : SV_Position;
+};
+
+[maxvertexcount(2)]
+void main(in    line GsVIn              inData[2],
+          inout      LineStream<GsVOut> outData) {
+
+    GsVOut vertex;
+    vertex = (GsVOut)0;
+// CHECK:      [[vert:%\d+]] = OpLoad %GsVIn %vertex
+// CHECK-NEXT:  [[val:%\d+]] = OpCompositeExtract %v4float [[vert]] 0
+// CHECK-NEXT: [[oldY:%\d+]] = OpCompositeExtract %float [[val]] 1
+// CHECK-NEXT: [[newY:%\d+]] = OpFNegate %float [[oldY]]
+// CHECK-NEXT:  [[pos:%\d+]] = OpCompositeInsert %v4float [[newY]] [[val]] 1
+// CHECK-NEXT:                 OpStore %gl_Position [[pos]]
+    outData.Append(vertex);
+
+    outData.RestartStrip();
+}
+

+ 12 - 0
tools/clang/test/CodeGenSPIRV/vk.cloption.invert-y.vs.hlsl

@@ -0,0 +1,12 @@
+// Run: %dxc -T vs_6_0 -E main -fvk-invert-y
+
+float4 main(float4 a : A) : SV_Position {
+    return a;
+}
+
+// CHECK:         [[a:%\d+]] = OpFunctionCall %v4float %src_main %param_var_a
+// CHECK-NEXT:  [[ptr:%\d+]] = OpAccessChain %_ptr_Output_v4float %gl_PerVertexOut %uint_0
+// CHECK-NEXT: [[oldY:%\d+]] = OpCompositeExtract %float [[a]] 1
+// CHECK-NEXT: [[newY:%\d+]] = OpFNegate %float [[oldY]]
+// CHECK-NEXT:  [[pos:%\d+]] = OpCompositeInsert %v4float [[newY]] [[a]] 1
+// CHECK-NEXT:                 OpStore [[ptr]] [[pos]]

+ 0 - 0
tools/clang/test/vk.cloption.invert-y.vs.hlsl


+ 1 - 0
tools/clang/tools/dxcompiler/dxcompilerobj.cpp

@@ -467,6 +467,7 @@ public:
 
 
           spirvOpts.codeGenHighLevel = opts.CodeGenHighLevel;
           spirvOpts.codeGenHighLevel = opts.CodeGenHighLevel;
           spirvOpts.disableValidation = opts.DisableValidation;
           spirvOpts.disableValidation = opts.DisableValidation;
+          spirvOpts.invertY = opts.VkInvertY;
           spirvOpts.ignoreUnusedResources = opts.VkIgnoreUnusedResources;
           spirvOpts.ignoreUnusedResources = opts.VkIgnoreUnusedResources;
           spirvOpts.defaultRowMajor = opts.DefaultRowMajor;
           spirvOpts.defaultRowMajor = opts.DefaultRowMajor;
           spirvOpts.stageIoOrder = opts.VkStageIoOrder;
           spirvOpts.stageIoOrder = opts.VkStageIoOrder;

+ 10 - 0
tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

@@ -1023,6 +1023,16 @@ TEST_F(FileTest, VulkanCLOptionIgnoreUnusedResources) {
   runFileTest("vk.cloption.ignore-unused-resources.hlsl");
   runFileTest("vk.cloption.ignore-unused-resources.hlsl");
 }
 }
 
 
+TEST_F(FileTest, VulkanCLOptionInvertYVS) {
+  runFileTest("vk.cloption.invert-y.vs.hlsl");
+}
+TEST_F(FileTest, VulkanCLOptionInvertYDS) {
+  runFileTest("vk.cloption.invert-y.ds.hlsl");
+}
+TEST_F(FileTest, VulkanCLOptionInvertYGS) {
+  runFileTest("vk.cloption.invert-y.gs.hlsl");
+}
+
 // Vulkan specific
 // Vulkan specific
 TEST_F(FileTest, VulkanLocation) { runFileTest("vk.location.hlsl"); }
 TEST_F(FileTest, VulkanLocation) { runFileTest("vk.location.hlsl"); }
 TEST_F(FileTest, VulkanLocationInputExplicitOutputImplicit) {
 TEST_F(FileTest, VulkanLocationInputExplicitOutputImplicit) {