Browse Source

[spirv] Add support for .GetSamplePosition() (#1008)

This only supports .GetSamplePosition() for standard sample
positions, i.e., sample count is 1, 2, 4, 8, or 16. For other
cases, the method will just return float2(0, 0).
Lei Zhang 7 years ago
parent
commit
6af2a123d8

+ 51 - 4
docs/SPIR-V.rst

@@ -251,7 +251,7 @@ type instructions:
 ``int16_t``                    ``-enable-16bit-types`` ``OpTypeInt 16 1`` ``Int16``
 ``uint``/``dword``/``uin32_t``                         ``OpTypeInt 32 0``
 ``uint16_t``                   ``-enable-16bit-types`` ``OpTypeInt 16 0`` ``Int16``
-``half``                                               ``OpTypeFloat 32`` 
+``half``                                               ``OpTypeFloat 32``
 ``half``/``float16_t``         ``-enable-16bit-types`` ``OpTypeFloat 16`` ``Float16`` ``SPV_AMD_gpu_shader_half_float``
 ``float``/``float32_t``                                ``OpTypeFloat 32``
 ``snorm float``                                        ``OpTypeFloat 32``
@@ -2043,6 +2043,52 @@ the instruction directly.
 Since Texture2DMS is represented as ``OpTypeImage`` with ``MS`` of ``1``, the ``OpImageQuerySize`` instruction
 is used to get the width and the height. Furthermore, ``OpImageQuerySamples`` is used to get the numSamples.
 
+``.GetSamplePosition(index)``
++++++++++++++++++++++++++++++
+There are no direct mapping SPIR-V instructions for this method. Right now, it
+is translated into the SPIR-V code for the following HLSL source code:
+
+.. code:: hlsl
+
+  // count is the number of samples in the Texture2DMS(Array)
+  // index is the index of the sample we are trying to get the position
+
+  static const float2 pos2[] = {
+      { 4.0/16.0,  4.0/16.0 }, {-4.0/16.0, -4.0/16.0 },
+  };
+
+  static const float2 pos4[] = {
+      {-2.0/16.0, -6.0/16.0 }, { 6.0/16.0, -2.0/16.0 }, {-6.0/16.0,  2.0/16.0 }, { 2.0/16.0,  6.0/16.0 },
+  };
+
+  static const float2 pos8[] = {
+      { 1.0/16.0, -3.0/16.0 }, {-1.0/16.0,  3.0/16.0 }, { 5.0/16.0,  1.0/16.0 }, {-3.0/16.0, -5.0/16.0 },
+      {-5.0/16.0,  5.0/16.0 }, {-7.0/16.0, -1.0/16.0 }, { 3.0/16.0,  7.0/16.0 }, { 7.0/16.0, -7.0/16.0 },
+  };
+
+  static const float2 pos16[] = {
+      { 1.0/16.0,  1.0/16.0 }, {-1.0/16.0, -3.0/16.0 }, {-3.0/16.0,  2.0/16.0 }, { 4.0/16.0, -1.0/16.0 },
+      {-5.0/16.0, -2.0/16.0 }, { 2.0/16.0,  5.0/16.0 }, { 5.0/16.0,  3.0/16.0 }, { 3.0/16.0, -5.0/16.0 },
+      {-2.0/16.0,  6.0/16.0 }, { 0.0/16.0, -7.0/16.0 }, {-4.0/16.0, -6.0/16.0 }, {-6.0/16.0,  4.0/16.0 },
+      {-8.0/16.0,  0.0/16.0 }, { 7.0/16.0, -4.0/16.0 }, { 6.0/16.0,  7.0/16.0 }, {-7.0/16.0, -8.0/16.0 },
+  };
+
+  float2 position = float2(0.0f, 0.0f);
+
+  if (count == 2) {
+      position = pos2[index];
+  } else if (count == 4) {
+      position = pos4[index];
+  } else if (count == 8) {
+      position = pos8[index];
+  } else if (count == 16) {
+      position = pos16[index];
+  }
+
+From the above, it's clear that the current implementation only supports standard
+sample settings, i.e., with 1, 2, 4, 8, or 16 samples. For other cases, the
+implementation will just return `(float2)0`.
+
 ``Texture2DMSArray``
 ~~~~~~~~~~~~~~~~~~~~
 
@@ -2058,6 +2104,10 @@ the instruction directly.
 Since Texture2DMS is represented as ``OpTypeImage`` with ``MS`` of ``1``, the ``OpImageQuerySize`` instruction
 is used to get the width, the height, and the elements. Furthermore, ``OpImageQuerySamples`` is used to get the numSamples.
 
+``.GetSamplePosition(index)``
++++++++++++++++++++++++++++++
+Similar to Texture2D.
+
 ``TextureCube``
 ~~~~~~~~~~~~~~~
 
@@ -2347,9 +2397,6 @@ either because of no Vulkan equivalents at the moment, or because of deprecation
 * ``.CalculateLevelOfDetailUnclamped()`` intrinsic method: no Vulkan equivalent.
   (SPIR-V ``OpImageQueryLod`` returns the clamped LOD in Vulkan.) The compiler
   will emit an error.
-* ``.GetSamplePosition()`` intrinsic method: no Vulkan equivalent.
-  (``gl_SamplePosition`` provides similar functionality but it's only for the
-  sample currently being processed.) The compiler will emit an error.
 * ``SV_InnerCoverage`` semantic does not have a Vulkan equivalent. The compiler
   will emit an error.
 * Since ``StructuredBuffer``, ``RWStructuredBuffer``, ``ByteAddressBuffer``, and

+ 13 - 0
tools/clang/lib/SPIRV/ModuleBuilder.cpp

@@ -212,6 +212,13 @@ uint32_t ModuleBuilder::createUnaryOp(spv::Op op, uint32_t resultType,
   const uint32_t id = theContext.takeNextId();
   instBuilder.unaryOp(op, resultType, id, operand).x();
   insertPoint->appendInstruction(std::move(constructSite));
+  switch (op) {
+  case spv::Op::OpImageQuerySize:
+  case spv::Op::OpImageQueryLevels:
+  case spv::Op::OpImageQuerySamples:
+    requireCapability(spv::Capability::ImageQuery);
+    break;
+  }
   return id;
 }
 
@@ -221,6 +228,12 @@ uint32_t ModuleBuilder::createBinaryOp(spv::Op op, uint32_t resultType,
   const uint32_t id = theContext.takeNextId();
   instBuilder.binaryOp(op, resultType, id, lhs, rhs).x();
   insertPoint->appendInstruction(std::move(constructSite));
+  switch (op) {
+  case spv::Op::OpImageQueryLod:
+  case spv::Op::OpImageQuerySizeLod:
+    requireCapability(spv::Capability::ImageQuery);
+    break;
+  }
   return id;
 }
 

+ 231 - 5
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -2326,9 +2326,20 @@ uint32_t SPIRVEmitter::processRWByteAddressBufferAtomicMethods(
   return 0;
 }
 
+uint32_t SPIRVEmitter::processGetSamplePosition(const CXXMemberCallExpr *expr) {
+  const auto *object = expr->getImplicitObjectArgument()->IgnoreParens();
+  const auto sampleCount = theBuilder.createUnaryOp(
+      spv::Op::OpImageQuerySamples, theBuilder.getUint32Type(),
+      loadIfGLValue(object));
+  emitWarning(
+      "GetSamplePosition only supports standard sample settings with 1, 2, 4, "
+      "8, or 16 samples and will return float2(0, 0) for other cases",
+      expr->getCallee()->getExprLoc());
+  return emitGetSamplePosition(sampleCount, doExpr(expr->getArg(0)));
+}
+
 uint32_t
 SPIRVEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {
-  theBuilder.requireCapability(spv::Capability::ImageQuery);
   const auto *object = expr->getImplicitObjectArgument();
   const auto objectId = loadIfGLValue(object);
   const auto type = object->getType();
@@ -2462,7 +2473,6 @@ SPIRVEmitter::processTextureLevelOfDetail(const CXXMemberCallExpr *expr) {
   // Texture3D.CalculateLevelOfDetail(SamplerState S, float3 xyz);
   // Return type is always a single float (LOD).
   assert(expr->getNumArgs() == 2u);
-  theBuilder.requireCapability(spv::Capability::ImageQuery);
   const auto *object = expr->getImplicitObjectArgument();
   const uint32_t objectId = loadIfGLValue(object);
   const uint32_t samplerState = doExpr(expr->getArg(0));
@@ -2993,6 +3003,221 @@ SPIRVEmitter::processStreamOutputRestart(const CXXMemberCallExpr *expr) {
   return 0;
 }
 
+uint32_t SPIRVEmitter::emitGetSamplePosition(const uint32_t sampleCount,
+                                             const uint32_t sampleIndex) {
+  struct Float2 {
+    float x;
+    float y;
+  };
+
+  static const Float2 pos2[] = {
+      {4.0 / 16.0, 4.0 / 16.0},
+      {-4.0 / 16.0, -4.0 / 16.0},
+  };
+
+  static const Float2 pos4[] = {
+      {-2.0 / 16.0, -6.0 / 16.0},
+      {6.0 / 16.0, -2.0 / 16.0},
+      {-6.0 / 16.0, 2.0 / 16.0},
+      {2.0 / 16.0, 6.0 / 16.0},
+  };
+
+  static const Float2 pos8[] = {
+      {1.0 / 16.0, -3.0 / 16.0}, {-1.0 / 16.0, 3.0 / 16.0},
+      {5.0 / 16.0, 1.0 / 16.0},  {-3.0 / 16.0, -5.0 / 16.0},
+      {-5.0 / 16.0, 5.0 / 16.0}, {-7.0 / 16.0, -1.0 / 16.0},
+      {3.0 / 16.0, 7.0 / 16.0},  {7.0 / 16.0, -7.0 / 16.0},
+  };
+
+  static const Float2 pos16[] = {
+      {1.0 / 16.0, 1.0 / 16.0},   {-1.0 / 16.0, -3.0 / 16.0},
+      {-3.0 / 16.0, 2.0 / 16.0},  {4.0 / 16.0, -1.0 / 16.0},
+      {-5.0 / 16.0, -2.0 / 16.0}, {2.0 / 16.0, 5.0 / 16.0},
+      {5.0 / 16.0, 3.0 / 16.0},   {3.0 / 16.0, -5.0 / 16.0},
+      {-2.0 / 16.0, 6.0 / 16.0},  {0.0 / 16.0, -7.0 / 16.0},
+      {-4.0 / 16.0, -6.0 / 16.0}, {-6.0 / 16.0, 4.0 / 16.0},
+      {-8.0 / 16.0, 0.0 / 16.0},  {7.0 / 16.0, -4.0 / 16.0},
+      {6.0 / 16.0, 7.0 / 16.0},   {-7.0 / 16.0, -8.0 / 16.0},
+  };
+
+  // We are emitting the SPIR-V for the following HLSL source code:
+  //
+  //   float2 position;
+  //
+  //   if (count == 2) {
+  //     position = pos2[index];
+  //   }
+  //   else if (count == 4) {
+  //     position = pos4[index];
+  //   }
+  //   else if (count == 8) {
+  //     position = pos8[index];
+  //   }
+  //   else if (count == 16) {
+  //     position = pos16[index];
+  //   }
+  //   else {
+  //     position = float2(0.0f, 0.0f);
+  //   }
+
+  const uint32_t boolType = theBuilder.getBoolType();
+  const auto v2f32Type = theBuilder.getVecType(theBuilder.getFloat32Type(), 2);
+  const uint32_t ptrType =
+      theBuilder.getPointerType(v2f32Type, spv::StorageClass::Function);
+
+  // Creates a SPIR-V function scope variable of type float2[len].
+  const auto createArray = [this, v2f32Type](const Float2 *ptr, uint32_t len) {
+    llvm::SmallVector<uint32_t, 16> components;
+    for (uint32_t i = 0; i < len; ++i) {
+      const auto x = theBuilder.getConstantFloat32(ptr[i].x);
+      const auto y = theBuilder.getConstantFloat32(ptr[i].y);
+      components.push_back(theBuilder.getConstantComposite(v2f32Type, {x, y}));
+    }
+
+    const auto arrType =
+        theBuilder.getArrayType(v2f32Type, theBuilder.getConstantUint32(len));
+    const auto val = theBuilder.getConstantComposite(arrType, components);
+
+    const std::string varName =
+        "var.GetSamplePosition.data." + std::to_string(len);
+    const auto var = theBuilder.addFnVar(arrType, varName);
+    theBuilder.createStore(var, val);
+    return var;
+  };
+
+  const uint32_t pos2Arr = createArray(pos2, 2);
+  const uint32_t pos4Arr = createArray(pos4, 4);
+  const uint32_t pos8Arr = createArray(pos8, 8);
+  const uint32_t pos16Arr = createArray(pos16, 16);
+
+  const uint32_t resultVar =
+      theBuilder.addFnVar(v2f32Type, "var.GetSamplePosition.result");
+
+  const uint32_t then2BB =
+      theBuilder.createBasicBlock("if.GetSamplePosition.then2");
+  const uint32_t then4BB =
+      theBuilder.createBasicBlock("if.GetSamplePosition.then4");
+  const uint32_t then8BB =
+      theBuilder.createBasicBlock("if.GetSamplePosition.then8");
+  const uint32_t then16BB =
+      theBuilder.createBasicBlock("if.GetSamplePosition.then16");
+
+  const uint32_t else2BB =
+      theBuilder.createBasicBlock("if.GetSamplePosition.else2");
+  const uint32_t else4BB =
+      theBuilder.createBasicBlock("if.GetSamplePosition.else4");
+  const uint32_t else8BB =
+      theBuilder.createBasicBlock("if.GetSamplePosition.else8");
+  const uint32_t else16BB =
+      theBuilder.createBasicBlock("if.GetSamplePosition.else16");
+
+  const uint32_t merge2BB =
+      theBuilder.createBasicBlock("if.GetSamplePosition.merge2");
+  const uint32_t merge4BB =
+      theBuilder.createBasicBlock("if.GetSamplePosition.merge4");
+  const uint32_t merge8BB =
+      theBuilder.createBasicBlock("if.GetSamplePosition.merge8");
+  const uint32_t merge16BB =
+      theBuilder.createBasicBlock("if.GetSamplePosition.merge16");
+
+  //   if (count == 2) {
+  const auto check2 =
+      theBuilder.createBinaryOp(spv::Op::OpIEqual, boolType, sampleCount,
+                                theBuilder.getConstantUint32(2));
+  theBuilder.createConditionalBranch(check2, then2BB, else2BB, merge2BB);
+  theBuilder.addSuccessor(then2BB);
+  theBuilder.addSuccessor(else2BB);
+  theBuilder.setMergeTarget(merge2BB);
+
+  //     position = pos2[index];
+  //   }
+  theBuilder.setInsertPoint(then2BB);
+  auto ac = theBuilder.createAccessChain(ptrType, pos2Arr, {sampleIndex});
+  theBuilder.createStore(resultVar, theBuilder.createLoad(v2f32Type, ac));
+  theBuilder.createBranch(merge2BB);
+  theBuilder.addSuccessor(merge2BB);
+
+  //   else if (count == 4) {
+  theBuilder.setInsertPoint(else2BB);
+  const auto check4 =
+      theBuilder.createBinaryOp(spv::Op::OpIEqual, boolType, sampleCount,
+                                theBuilder.getConstantUint32(4));
+  theBuilder.createConditionalBranch(check4, then4BB, else4BB, merge4BB);
+  theBuilder.addSuccessor(then4BB);
+  theBuilder.addSuccessor(else4BB);
+  theBuilder.setMergeTarget(merge4BB);
+
+  //     position = pos4[index];
+  //   }
+  theBuilder.setInsertPoint(then4BB);
+  ac = theBuilder.createAccessChain(ptrType, pos4Arr, {sampleIndex});
+  theBuilder.createStore(resultVar, theBuilder.createLoad(v2f32Type, ac));
+  theBuilder.createBranch(merge4BB);
+  theBuilder.addSuccessor(merge4BB);
+
+  //   else if (count == 8) {
+  theBuilder.setInsertPoint(else4BB);
+  const auto check8 =
+      theBuilder.createBinaryOp(spv::Op::OpIEqual, boolType, sampleCount,
+                                theBuilder.getConstantUint32(8));
+  theBuilder.createConditionalBranch(check8, then8BB, else8BB, merge8BB);
+  theBuilder.addSuccessor(then8BB);
+  theBuilder.addSuccessor(else8BB);
+  theBuilder.setMergeTarget(merge8BB);
+
+  //     position = pos8[index];
+  //   }
+  theBuilder.setInsertPoint(then8BB);
+  ac = theBuilder.createAccessChain(ptrType, pos8Arr, {sampleIndex});
+  theBuilder.createStore(resultVar, theBuilder.createLoad(v2f32Type, ac));
+  theBuilder.createBranch(merge8BB);
+  theBuilder.addSuccessor(merge8BB);
+
+  //   else if (count == 16) {
+  theBuilder.setInsertPoint(else8BB);
+  const auto check16 =
+      theBuilder.createBinaryOp(spv::Op::OpIEqual, boolType, sampleCount,
+                                theBuilder.getConstantUint32(16));
+  theBuilder.createConditionalBranch(check16, then16BB, else16BB, merge16BB);
+  theBuilder.addSuccessor(then16BB);
+  theBuilder.addSuccessor(else16BB);
+  theBuilder.setMergeTarget(merge16BB);
+
+  //     position = pos16[index];
+  //   }
+  theBuilder.setInsertPoint(then16BB);
+  ac = theBuilder.createAccessChain(ptrType, pos16Arr, {sampleIndex});
+  theBuilder.createStore(resultVar, theBuilder.createLoad(v2f32Type, ac));
+  theBuilder.createBranch(merge16BB);
+  theBuilder.addSuccessor(merge16BB);
+
+  //   else {
+  //     position = float2(0.0f, 0.0f);
+  //   }
+  theBuilder.setInsertPoint(else16BB);
+  const auto zero = theBuilder.getConstantFloat32(0);
+  const auto v2f32Zero =
+      theBuilder.getConstantComposite(v2f32Type, {zero, zero});
+  theBuilder.createStore(resultVar, v2f32Zero);
+  theBuilder.createBranch(merge16BB);
+  theBuilder.addSuccessor(merge16BB);
+
+  theBuilder.setInsertPoint(merge16BB);
+  theBuilder.createBranch(merge8BB);
+  theBuilder.addSuccessor(merge8BB);
+
+  theBuilder.setInsertPoint(merge8BB);
+  theBuilder.createBranch(merge4BB);
+  theBuilder.addSuccessor(merge4BB);
+
+  theBuilder.setInsertPoint(merge4BB);
+  theBuilder.createBranch(merge2BB);
+  theBuilder.addSuccessor(merge2BB);
+
+  theBuilder.setInsertPoint(merge2BB);
+  return theBuilder.createLoad(v2f32Type, resultVar);
+}
+
 SpirvEvalInfo SPIRVEmitter::doCXXMemberCallExpr(const CXXMemberCallExpr *expr) {
   const FunctionDecl *callee = expr->getDirectCallee();
 
@@ -3123,10 +3348,12 @@ SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
   case IntrinsicOp::MOP_InterlockedCompareStore:
     retVal = processRWByteAddressBufferAtomicMethods(opcode, expr);
     break;
+  case IntrinsicOp::MOP_GetSamplePosition:
+    retVal = processGetSamplePosition(expr);
+    break;
   case IntrinsicOp::MOP_GatherCmpGreen:
   case IntrinsicOp::MOP_GatherCmpBlue:
   case IntrinsicOp::MOP_GatherCmpAlpha:
-  case IntrinsicOp::MOP_GetSamplePosition:
   case IntrinsicOp::MOP_CalculateLevelOfDetailUnclamped:
     emitError("no equivalent for %0 intrinsic method in Vulkan",
               expr->getCallee()->getExprLoc())
@@ -7024,8 +7251,7 @@ uint32_t SPIRVEmitter::translateAPInt(const llvm::APInt &intValue,
       }
       return theBuilder.getConstantInt32(
           static_cast<int32_t>(intValue.getSExtValue()));
-    }
-    else {
+    } else {
       if (!intValue.isIntN(32)) {
         emitError("evaluating integer literal %0 as a 32-bit integer loses "
                   "inforamtion",

+ 8 - 0
tools/clang/lib/SPIRV/SPIRVEmitter.h

@@ -728,6 +728,10 @@ private:
   uint32_t processRWByteAddressBufferAtomicMethods(hlsl::IntrinsicOp opcode,
                                                    const CXXMemberCallExpr *);
 
+  /// \brief Processes the GetSamplePosition intrinsic method call on a
+  /// Texture2DMS(Array).
+  uint32_t processGetSamplePosition(const CXXMemberCallExpr *);
+
   /// \brief Generates SPIR-V instructions for the .Append()/.Consume() call on
   /// the given {Append|Consume}StructuredBuffer. Returns the <result-id> of
   /// the loaded value for .Consume; returns zero for .Append().
@@ -740,6 +744,10 @@ private:
   /// primitive in GS.
   uint32_t processStreamOutputRestart(const CXXMemberCallExpr *expr);
 
+  /// \brief Emulates GetSamplePosition() for standard sample settings, i.e.,
+  /// with 1, 2, 4, 8, or 16 samples. Returns float2(0) for other cases.
+  uint32_t emitGetSamplePosition(uint32_t sampleCount, uint32_t sampleIndex);
+
 private:
   /// \brief Takes a vector of size 4, and returns a vector of size 1 or 2 or 3
   /// or 4. Creates a CompositeExtract or VectorShuffle instruction to extract

+ 112 - 4
tools/clang/test/CodeGenSPIRV/texture.get-sample-position.hlsl

@@ -2,8 +2,116 @@
 
 Texture2DMS       <float> myTexture;
 
-void main() {
-  float2 ret = myTexture.GetSamplePosition(2);
-}
+// CHECK:   [[pos2_0:%\d+]] = OpConstantComposite %v2float %float_0_25 %float_0_25
+// CHECK:   [[pos2_1:%\d+]] = OpConstantComposite %v2float %float_n0_25 %float_n0_25
+// CHECK:     [[pos2:%\d+]] = OpConstantComposite %_arr_v2float_uint_2 [[pos2_0]] [[pos2_1]]
+
+// CHECK:   [[pos4_0:%\d+]] = OpConstantComposite %v2float %float_n0_125 %float_n0_375
+// CHECK:   [[pos4_1:%\d+]] = OpConstantComposite %v2float %float_0_375 %float_n0_125
+// CHECK:   [[pos4_2:%\d+]] = OpConstantComposite %v2float %float_n0_375 %float_0_125
+// CHECK:   [[pos4_3:%\d+]] = OpConstantComposite %v2float %float_0_125 %float_0_375
+// CHECK:     [[pos4:%\d+]] = OpConstantComposite %_arr_v2float_uint_4 [[pos4_0]] [[pos4_1]] [[pos4_2]] [[pos4_3]]
+
+// CHECK:   [[pos8_0:%\d+]] = OpConstantComposite %v2float %float_0_0625 %float_n0_1875
+// CHECK:   [[pos8_1:%\d+]] = OpConstantComposite %v2float %float_n0_0625 %float_0_1875
+// CHECK:   [[pos8_2:%\d+]] = OpConstantComposite %v2float %float_0_3125 %float_0_0625
+// CHECK:   [[pos8_3:%\d+]] = OpConstantComposite %v2float %float_n0_1875 %float_n0_3125
+// CHECK:   [[pos8_4:%\d+]] = OpConstantComposite %v2float %float_n0_3125 %float_0_3125
+// CHECK:   [[pos8_5:%\d+]] = OpConstantComposite %v2float %float_n0_4375 %float_n0_0625
+// CHECK:   [[pos8_6:%\d+]] = OpConstantComposite %v2float %float_0_1875 %float_0_4375
+// CHECK:   [[pos8_7:%\d+]] = OpConstantComposite %v2float %float_0_4375 %float_n0_4375
+// CHECK:     [[pos8:%\d+]] = OpConstantComposite %_arr_v2float_uint_8 [[pos8_0]] [[pos8_1]] [[pos8_2]] [[pos8_3]] [[pos8_4]] [[pos8_5]] [[pos8_6]] [[pos8_7]]
+
+// CHECK: [[pos16_00:%\d+]] = OpConstantComposite %v2float %float_0_0625 %float_0_0625
+// CHECK: [[pos16_01:%\d+]] = OpConstantComposite %v2float %float_n0_0625 %float_n0_1875
+// CHECK: [[pos16_02:%\d+]] = OpConstantComposite %v2float %float_n0_1875 %float_0_125
+// CHECK: [[pos16_03:%\d+]] = OpConstantComposite %v2float %float_0_25 %float_n0_0625
+// CHECK: [[pos16_04:%\d+]] = OpConstantComposite %v2float %float_n0_3125 %float_n0_125
+// CHECK: [[pos16_05:%\d+]] = OpConstantComposite %v2float %float_0_125 %float_0_3125
+// CHECK: [[pos16_06:%\d+]] = OpConstantComposite %v2float %float_0_3125 %float_0_1875
+// CHECK: [[pos16_07:%\d+]] = OpConstantComposite %v2float %float_0_1875 %float_n0_3125
+// CHECK: [[pos16_08:%\d+]] = OpConstantComposite %v2float %float_n0_125 %float_0_375
+// CHECK: [[pos16_09:%\d+]] = OpConstantComposite %v2float %float_0 %float_n0_4375
+// CHECK: [[pos16_10:%\d+]] = OpConstantComposite %v2float %float_n0_25 %float_n0_375
+// CHECK: [[pos16_11:%\d+]] = OpConstantComposite %v2float %float_n0_375 %float_0_25
+// CHECK: [[pos16_12:%\d+]] = OpConstantComposite %v2float %float_n0_5 %float_0
+// CHECK: [[pos16_13:%\d+]] = OpConstantComposite %v2float %float_0_4375 %float_n0_25
+// CHECK: [[pos16_14:%\d+]] = OpConstantComposite %v2float %float_0_375 %float_0_4375
+// CHECK: [[pos16_15:%\d+]] = OpConstantComposite %v2float %float_n0_4375 %float_n0_5
+// CHECK:    [[pos16:%\d+]] = OpConstantComposite %_arr_v2float_uint_16 [[pos16_00]] [[pos16_01]] [[pos16_02]] [[pos16_03]] [[pos16_04]] [[pos16_05]] [[pos16_06]] [[pos16_07]] [[pos16_08]] [[pos16_09]] [[pos16_10]] [[pos16_11]] [[pos16_12]] [[pos16_13]] [[pos16_14]] [[pos16_15]]
+
+// CHECK:     [[zero:%\d+]] = OpConstantComposite %v2float %float_0 %float_0
+
+void main(int index : INDEX) {
+// CHECK:  %var_GetSamplePosition_data_2 = OpVariable %_ptr_Function__arr_v2float_uint_2 Function
+// CHECK:  %var_GetSamplePosition_data_4 = OpVariable %_ptr_Function__arr_v2float_uint_4 Function
+// CHECK:  %var_GetSamplePosition_data_8 = OpVariable %_ptr_Function__arr_v2float_uint_8 Function
+// CHECK: %var_GetSamplePosition_data_16 = OpVariable %_ptr_Function__arr_v2float_uint_16 Function
+// CHECK:  %var_GetSamplePosition_result = OpVariable %_ptr_Function_v2float Function
+
+// CHECK:        [[tex:%\d+]] = OpLoad %type_2d_image %myTexture
+// CHECK-NEXT: [[count:%\d+]] = OpImageQuerySamples %uint [[tex]]
+// CHECK-NEXT: [[index:%\d+]] = OpLoad %int %index
+// CHECK-NEXT:                  OpStore %var_GetSamplePosition_data_2 [[pos2]]
+// CHECK-NEXT:                  OpStore %var_GetSamplePosition_data_4 [[pos4]]
+// CHECK-NEXT:                  OpStore %var_GetSamplePosition_data_8 [[pos8]]
+// CHECK-NEXT:                  OpStore %var_GetSamplePosition_data_16 [[pos16]]
+
+// CHECK-NEXT:   [[eq2:%\d+]] = OpIEqual %bool [[count]] %uint_2
+// CHECK-NEXT:                  OpSelectionMerge %if_GetSamplePosition_merge2 None
+// CHECK-NEXT:                  OpBranchConditional [[eq2]] %if_GetSamplePosition_then2 %if_GetSamplePosition_else2
+
+// CHECK-NEXT: %if_GetSamplePosition_then2 = OpLabel
+// CHECK-NEXT:    [[ac:%\d+]] = OpAccessChain %_ptr_Function_v2float %var_GetSamplePosition_data_2 [[index]]
+// CHECK-NEXT:   [[val:%\d+]] = OpLoad %v2float [[ac]]
+// CHECK-NEXT:                  OpStore %var_GetSamplePosition_result [[val]]
+// CHECK-NEXT:                  OpBranch %if_GetSamplePosition_merge2
 
-// CHECK: :6:26: error: no equivalent for GetSamplePosition intrinsic method in Vulkan
+// CHECK-NEXT: %if_GetSamplePosition_else2 = OpLabel
+// CHECK-NEXT:   [[eq4:%\d+]] = OpIEqual %bool [[count]] %uint_4
+// CHECK-NEXT:                  OpSelectionMerge %if_GetSamplePosition_merge4 None
+// CHECK-NEXT:                  OpBranchConditional [[eq4]] %if_GetSamplePosition_then4 %if_GetSamplePosition_else4
+
+// CHECK-NEXT: %if_GetSamplePosition_then4 = OpLabel
+// CHECK-NEXT:    [[ac:%\d+]] = OpAccessChain %_ptr_Function_v2float %var_GetSamplePosition_data_4 [[index]]
+// CHECK-NEXT:   [[val:%\d+]] = OpLoad %v2float [[ac]]
+// CHECK-NEXT:                  OpStore %var_GetSamplePosition_result [[val]]
+// CHECK-NEXT:                  OpBranch %if_GetSamplePosition_merge4
+
+// CHECK-NEXT: %if_GetSamplePosition_else4 = OpLabel
+// CHECK-NEXT:   [[eq8:%\d+]] = OpIEqual %bool [[count]] %uint_8
+// CHECK-NEXT:                  OpSelectionMerge %if_GetSamplePosition_merge8 None
+// CHECK-NEXT:                  OpBranchConditional [[eq8]] %if_GetSamplePosition_then8 %if_GetSamplePosition_else8
+
+// CHECK-NEXT: %if_GetSamplePosition_then8 = OpLabel
+// CHECK-NEXT:    [[ac:%\d+]] = OpAccessChain %_ptr_Function_v2float %var_GetSamplePosition_data_8 [[index]]
+// CHECK-NEXT:   [[val:%\d+]] = OpLoad %v2float [[ac]]
+// CHECK-NEXT:                  OpStore %var_GetSamplePosition_result [[val]]
+// CHECK-NEXT:                  OpBranch %if_GetSamplePosition_merge8
+
+// CHECK-NEXT: %if_GetSamplePosition_else8 = OpLabel
+// CHECK-NEXT:  [[eq16:%\d+]] = OpIEqual %bool [[count]] %uint_16
+// CHECK-NEXT:                  OpSelectionMerge %if_GetSamplePosition_merge16 None
+// CHECK-NEXT:                  OpBranchConditional [[eq16]] %if_GetSamplePosition_then16 %if_GetSamplePosition_else16
+
+// CHECK-NEXT: %if_GetSamplePosition_then16 = OpLabel
+// CHECK-NEXT:    [[ac:%\d+]] = OpAccessChain %_ptr_Function_v2float %var_GetSamplePosition_data_16 [[index]]
+// CHECK-NEXT:   [[val:%\d+]] = OpLoad %v2float [[ac]]
+// CHECK-NEXT:                  OpStore %var_GetSamplePosition_result [[val]]
+// CHECK-NEXT:                  OpBranch %if_GetSamplePosition_merge16
+
+// CHECK-NEXT: %if_GetSamplePosition_else16 = OpLabel
+// CHECK-NEXT:                  OpStore %var_GetSamplePosition_result [[zero]]
+// CHECK-NEXT:                  OpBranch %if_GetSamplePosition_merge16
+
+// CHECK-NEXT: %if_GetSamplePosition_merge16 = OpLabel
+// CHECK-NEXT:                  OpBranch %if_GetSamplePosition_merge8
+// CHECK-NEXT: %if_GetSamplePosition_merge8 = OpLabel
+// CHECK-NEXT:                  OpBranch %if_GetSamplePosition_merge4
+// CHECK-NEXT: %if_GetSamplePosition_merge4 = OpLabel
+// CHECK-NEXT:                  OpBranch %if_GetSamplePosition_merge2
+// CHECK-NEXT: %if_GetSamplePosition_merge2 = OpLabel
+// CHECK-NEXT:   [[val:%\d+]] = OpLoad %v2float %var_GetSamplePosition_result
+// CHECK-NEXT:                  OpStore %ret [[val]]
+  float2 ret = myTexture.GetSamplePosition(index);
+}

+ 1 - 1
tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

@@ -592,7 +592,7 @@ TEST_F(FileTest, TextureGetDimensions) {
   runFileTest("texture.get-dimensions.hlsl");
 }
 TEST_F(FileTest, TextureGetSamplePosition) {
-  runFileTest("texture.get-sample-position.hlsl", Expect::Failure);
+  runFileTest("texture.get-sample-position.hlsl");
 }
 TEST_F(FileTest, TextureCalculateLevelOfDetail) {
   runFileTest("texture.calculate-lod.hlsl");