Sfoglia il codice sorgente

[spirv] GetDimensions for several types. (#636)

* [spirv] GetDimensions for several types.

GetDimensions function implementation for:
Textures, RWTextures,
Buffers, RWBuffers,
ByteAddressBuffers, RWByteAddressBuffers,
StructuredBuffers, RWStructuredBuffers.

* Address comments.

* Update the doc file.
Ehsan 8 anni fa
parent
commit
00b7505afd

+ 157 - 1
docs/SPIR-V.rst

@@ -1036,7 +1036,7 @@ The following intrinsic HLSL functions are currently supported:
 - ``dot`` : performs dot product of two vectors, each containing floats or
   integers. If the two parameters are vectors of floats, we use SPIR-V's
   ``OpDot`` instruction to perform the translation. If the two parameters are
-  vectors of integers, we multiply corresponding vector elementes using
+  vectors of integers, we multiply corresponding vector elements using
   ``OpIMul`` and accumulate the results using ``OpIAdd`` to compute the dot
   product.
 - ``mul``: performs multiplications. Each argument may be a scalar, vector,
@@ -1148,3 +1148,159 @@ will be used as the index for reading the new element. E.g., for
       %ptr = OpAccessChain %_ptr_Uniform_v4float %buf %uint_0 %index
       %val = OpLoad %v4float %vec
              OpStore %ptr %val
+
+
+``Buffer``
+--------------------------
+
+``.GetDimensions()``
+~~~~~~~~~~~~~~~~~~~~
+Since Buffers are represented as ``OpTypeImage`` with dimension of ``Buffer``,
+``OpImageQuerySize`` is used to perform this operation.
+
+``RWBuffer``
+--------------------------
+
+``.GetDimensions()``
+~~~~~~~~~~~~~~~~~~~~
+Since RWBuffers are represented as ``OpTypeImage`` with dimension of ``Buffer``,
+``OpImageQuerySize`` is used to perform this operation.
+
+``StructuredBuffer``
+--------------------------
+
+``.GetDimensions()``
+~~~~~~~~~~~~~~~~~~~~
+Since StructuredBuffers are represented as a struct with one member that is a
+runtime array of structures, ``OpArrayLength`` is invoked on the runtime array in
+order to find the dimension.
+
+``RWStructuredBuffer``
+--------------------------
+
+``.GetDimensions()``
+~~~~~~~~~~~~~~~~~~~~
+Similar to StructuredBuffers, since RWStructuredBuffers are represented as a struct
+with one member that is a runtime array of structures, ``OpArrayLength`` is invoked
+on the runtime array in order to find the dimension.
+
+``ByteAddressBuffer``
+--------------------------
+
+``.GetDimensions()``
+~~~~~~~~~~~~~~~~~~~~
+Since ByteAddressBuffers are represented as a struct with one member that is a
+runtime array of unsigned integers, ``OpArrayLength`` is invoked on the runtime array
+in order to find the number of unsigned integers. This is then multiplied by 4 to find
+the number of bytes.
+
+``RWByteAddressBuffer``
+--------------------------
+
+``.GetDimensions()``
+~~~~~~~~~~~~~~~~~~~~
+Since RWByteAddressBuffers are represented as a struct with one member that is a
+runtime array of unsigned integers, ``OpArrayLength`` is invoked on the runtime array
+in order to find the number of unsigned integers. This is then multiplied by 4 to find
+the number of bytes.
+
+``Texture1D``
+--------------------------
+
+``.GetDimensions(width)`` or ``.GetDimensions(MipLevel, width, NumLevels)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Since Texture1D is represented as ``OpTypeImage``, the ``OpImageQuerySizeLod`` instruction
+is used for translation. If a ``MipLevel`` argument is passed to ``GetDimensions``, it will
+be used as the ``Lod`` parameter of the query instruction. Otherwise, ``Lod`` of ``0`` be used.
+
+
+``Texture1DArray``
+--------------------------
+
+``.GetDimensions(width, elements)`` or ``.GetDimensions(MipLevel, width, elements, NumLevels)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Since Texture1DArray is represented as ``OpTypeImage``, the ``OpImageQuerySizeLod`` instruction
+is used for translation. If a ``MipLevel`` argument is present, it will be used as the
+``Lod`` parameter of the query instruction. Otherwise, ``Lod`` of ``0`` be used.
+
+``Texture2D``
+--------------------------
+
+``.GetDimensions(width, height)`` or ``.GetDimensions(MipLevel, width, height, NumLevels)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Since Texture2D is represented as ``OpTypeImage``, the ``OpImageQuerySizeLod`` instruction
+is used for translation. If a ``MipLevel`` argument is present, it will be used as the
+``Lod`` parameter of the query instruction. Otherwise, ``Lod`` of ``0`` be used.
+
+``Texture2DArray``
+--------------------------
+
+``.GetDimensions(width, height, elements)`` or ``.GetDimensions(MipLevel, width, height, elements, NumLevels)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Since Texture2DArray is represented as ``OpTypeImage``, the ``OpImageQuerySizeLod`` instruction
+is used for translation. If a ``MipLevel`` argument is present, it will be used as the
+``Lod`` parameter of the query instruction. Otherwise, ``Lod`` of ``0`` be used.
+
+``Texture3D``
+--------------------------
+
+``.GetDimensions(width, height, depth)`` or ``.GetDimensions(MipLevel, width, height, depth, NumLevels)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Since Texture3D is represented as ``OpTypeImage``, the ``OpImageQuerySizeLod`` instruction
+is used for translation. If a ``MipLevel`` argument is present, it will be used as the
+``Lod`` parameter of the query instruction. Otherwise, ``Lod`` of ``0`` be used.
+
+``Texture2DMS``
+--------------------------
+
+``.GetDimensions(width, height, numSamples)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+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.
+
+``Texture2DMSArray``
+--------------------------
+
+``.GetDimensions(width, height, elements, numSamples)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+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.
+
+``RWTexture1D``
+--------------------------
+
+``.GetDimensions(width)``
+~~~~~~~~~~~~~~~~~~~~~~~~~
+The ``OpImageQuerySize`` instruction is used to find the width.
+
+``RWTexture1DArray``
+--------------------------
+
+``.GetDimensions(width, elements)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The ``OpImageQuerySize`` instruction is used to get a uint2. The first element is the width, and the second
+is the elements.
+
+``RWTexture2D``
+--------------------------
+
+``.GetDimensions(width, height)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The ``OpImageQuerySize`` instruction is used to get a uint2. The first element is the width, and the second
+element is the height.
+
+``RWTexture2DArray``
+--------------------------
+
+``.GetDimensions(width, height, elements)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The ``OpImageQuerySize`` instruction is used to get a uint3. The first element is the width, the second
+element is the height, and the third is the elements.
+
+``RWTexture3D``
+--------------------------
+
+``.GetDimensions(width, height, depth)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The ``OpImageQuerySize`` instruction is used to get a uint3. The first element is the width, the second
+element is the height, and the third element is the depth.

+ 168 - 12
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -110,15 +110,6 @@ bool isFloatOrVecMatOfFloatType(QualType type) {
           hlsl::GetHLSLMatElementType(type)->isFloatingType());
 }
 
-/// Returns true if the given type is a (RW)StructuredBuffer type.
-bool isStructuredBuffer(QualType type) {
-  const auto *recordType = type->getAs<RecordType>();
-  if (!recordType)
-    return false;
-  const auto name = recordType->getDecl()->getName();
-  return name == "StructuredBuffer" || name == "RWStructuredBuffer";
-}
-
 bool isSpirvMatrixOp(spv::Op opcode) {
   switch (opcode) {
   case spv::Op::OpMatrixTimesMatrix:
@@ -146,7 +137,7 @@ const Expr *isStructuredBufferLoad(const Expr *expr, const Expr **index) {
     if (GetIntrinsicOp(callee, opcode, group)) {
       if (static_cast<IntrinsicOp>(opcode) == IntrinsicOp::MOP_Load) {
         const auto *object = indexing->getImplicitObjectArgument();
-        if (isStructuredBuffer(object->getType())) {
+        if (TypeTranslator::isStructuredBuffer(object->getType())) {
           *index = indexing->getArg(0);
           return indexing->getImplicitObjectArgument();
         }
@@ -1387,6 +1378,155 @@ uint32_t SPIRVEmitter::doConditionalOperator(const ConditionalOperator *expr) {
   return theBuilder.createSelect(type, condition, trueBranch, falseBranch);
 }
 
+uint32_t SPIRVEmitter::processByteAddressBufferStructuredBufferGetDimensions(
+    const CXXMemberCallExpr *expr) {
+  const auto *object = expr->getImplicitObjectArgument();
+  const auto objectId = loadIfGLValue(object);
+  const auto type = object->getType();
+  const bool isByteAddressBuffer = TypeTranslator::isByteAddressBuffer(type) ||
+                                   TypeTranslator::isRWByteAddressBuffer(type);
+  const bool isStructuredBuffer = TypeTranslator::isStructuredBuffer(type);
+  assert(isByteAddressBuffer || isStructuredBuffer);
+
+  // (RW)ByteAddressBuffers/(RW)StructuredBuffers are represented as a structure
+  // with only one member that is a runtime array. We need to perform
+  // OpArrayLength on member 0.
+  const auto uintType = theBuilder.getUint32Type();
+  uint32_t length =
+      theBuilder.createBinaryOp(spv::Op::OpArrayLength, uintType, objectId, 0);
+  // For (RW)ByteAddressBuffers, GetDimensions() must return the array length
+  // in bytes, but OpArrayLength returns the number of uints in the runtime
+  // array. Therefore we must multiply the results by 4.
+  if (isByteAddressBuffer) {
+    length = theBuilder.createBinaryOp(spv::Op::OpIMul, uintType, length,
+                                       theBuilder.getConstantUint32(4u));
+  }
+  theBuilder.createStore(doExpr(expr->getArg(0)), length);
+
+  if (isStructuredBuffer) {
+    // For (RW)StructuredBuffer, the stride of the runtime array (which is the
+    // size of the struct) must also be written to the second argument.
+    uint32_t size = 0, stride = 0;
+    std::tie(std::ignore, size) = typeTranslator.getAlignmentAndSize(
+        type, LayoutRule::GLSLStd430, /*isRowMajor*/ false, &stride);
+    const auto sizeId = theBuilder.getConstantUint32(size);
+    theBuilder.createStore(doExpr(expr->getArg(1)), sizeId);
+  }
+
+  return 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();
+  const auto *recType = type->getAs<RecordType>();
+  assert(recType);
+  const auto typeName = recType->getDecl()->getName();
+  const auto numArgs = expr->getNumArgs();
+  const Expr *mipLevel = nullptr, *numLevels = nullptr, *numSamples = nullptr;
+
+  assert(TypeTranslator::isTexture(type) || TypeTranslator::isRWTexture(type) ||
+         TypeTranslator::isBuffer(type) || TypeTranslator::isRWBuffer(type));
+
+  // For Texture1D, arguments are either:
+  // a) width
+  // b) MipLevel, width, NumLevels
+
+  // For Texture1DArray, arguments are either:
+  // a) width, elements
+  // b) MipLevel, width, elements, NumLevels
+
+  // For Texture2D, arguments are either:
+  // a) width, height
+  // b) MipLevel, width, height, NumLevels
+
+  // For Texture2DArray, arguments are either:
+  // a) width, height, elements
+  // b) MipLevel, width, height, elements, NumLevels
+
+  // For Texture3D, arguments are either:
+  // a) width, height, depth
+  // b) MipLevel, width, height, depth, NumLevels
+
+  // For Texture2DMS, arguments are: width, height, NumSamples
+
+  // For Texture2DMSArray, arguments are: width, height, elements, NumSamples
+
+  if ((typeName == "Texture1D" && numArgs > 1) ||
+      (typeName == "Texture2D" && numArgs > 2) ||
+      (typeName == "Texture3D" && numArgs > 3) ||
+      (typeName == "Texture1DArray" && numArgs > 2) ||
+      (typeName == "Texture2DArray" && numArgs > 3)) {
+    mipLevel = expr->getArg(0);
+    numLevels = expr->getArg(numArgs - 1);
+  }
+  if (TypeTranslator::isTextureMS(type)) {
+    numSamples = expr->getArg(numArgs - 1);
+  }
+
+  uint32_t querySize = numArgs;
+  // If numLevels arg is present, mipLevel must also be present. These are not
+  // queried via ImageQuerySizeLod.
+  if (numLevels)
+    querySize -= 2;
+  // If numLevels arg is present, mipLevel must also be present.
+  else if (numSamples)
+    querySize -= 1;
+
+  const uint32_t uintId = theBuilder.getUint32Type();
+  const uint32_t resultTypeId =
+      querySize == 1 ? uintId : theBuilder.getVecType(uintId, querySize);
+
+  // Only Texture types use ImageQuerySizeLod.
+  // TextureMS, RWTexture, Buffers, RWBuffers use ImageQuerySize.
+  uint32_t lod = 0;
+  if (TypeTranslator::isTexture(type) && !numSamples) {
+    if (mipLevel) {
+      // For Texture types when mipLevel argument is present.
+      lod = doExpr(mipLevel);
+    } else {
+      // For Texture types when mipLevel argument is omitted.
+      lod = theBuilder.getConstantInt32(0);
+    }
+  }
+
+  const uint32_t query =
+      lod ? theBuilder.createBinaryOp(spv::Op::OpImageQuerySizeLod,
+                                      resultTypeId, objectId, lod)
+          : theBuilder.createUnaryOp(spv::Op::OpImageQuerySize, resultTypeId,
+                                     objectId);
+
+  if (querySize == 1) {
+    const uint32_t argIndex = mipLevel ? 1 : 0;
+    theBuilder.createStore(doExpr(expr->getArg(argIndex)), query);
+  } else {
+    for (uint32_t i = 0; i < querySize; ++i) {
+      const uint32_t component =
+          theBuilder.createCompositeExtract(uintId, query, {i});
+      // If the first arg is the mipmap level, we must write the results
+      // starting from Arg(i+1), not Arg(i).
+      const uint32_t argIndex = mipLevel ? i + 1 : i;
+      theBuilder.createStore(doExpr(expr->getArg(argIndex)), component);
+    }
+  }
+
+  if (numLevels || numSamples) {
+    const Expr *numLevelsSamplesArg = numLevels ? numLevels : numSamples;
+    const spv::Op opcode =
+        numLevels ? spv::Op::OpImageQueryLevels : spv::Op::OpImageQuerySamples;
+    const uint32_t resultType =
+        typeTranslator.translateType(numLevelsSamplesArg->getType());
+    const uint32_t numLevelsSamplesQuery =
+        theBuilder.createUnaryOp(opcode, resultType, objectId);
+    theBuilder.createStore(doExpr(numLevelsSamplesArg), numLevelsSamplesQuery);
+  }
+
+  return 0;
+}
+
 uint32_t SPIRVEmitter::processBufferTextureLoad(const Expr *object,
                                                 const uint32_t locationId,
                                                 uint32_t constOffset,
@@ -1786,7 +1926,7 @@ uint32_t SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
         typeTranslator.isByteAddressBuffer(objectType))
       return processByteAddressBufferLoadStore(expr, 1, /*doStore*/ false);
 
-    if (isStructuredBuffer(objectType))
+    if (TypeTranslator::isStructuredBuffer(objectType))
       return processStructuredBufferLoad(expr);
 
     if (TypeTranslator::isBuffer(objectType) ||
@@ -1836,6 +1976,22 @@ uint32_t SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
   case IntrinsicOp::MOP_Consume: {
     return processACSBufferAppendConsume(expr);
   }
+  case IntrinsicOp::MOP_GetDimensions: {
+    const auto objectType = expr->getImplicitObjectArgument()->getType();
+    if (TypeTranslator::isTexture(objectType) ||
+        TypeTranslator::isRWTexture(objectType) ||
+        TypeTranslator::isBuffer(objectType) ||
+        TypeTranslator::isRWBuffer(objectType)) {
+      return processBufferTextureGetDimensions(expr);
+    } else if (TypeTranslator::isByteAddressBuffer(objectType) ||
+               TypeTranslator::isRWByteAddressBuffer(objectType) ||
+               TypeTranslator::isStructuredBuffer(objectType)) {
+      return processByteAddressBufferStructuredBufferGetDimensions(expr);
+    } else {
+      emitError("GetDimensions not implmented for the given type yet.");
+      return 0;
+    }
+  }
   }
   emitError("HLSL intrinsic member call unimplemented: %0")
       << callee->getName();
@@ -3006,7 +3162,7 @@ const Expr *SPIRVEmitter::collectArrayStructIndices(
       // If the base is a StructureType, we need to push an addtional index 0
       // here. This is because we created an additional OpTypeRuntimeArray
       // in the structure.
-      if (isStructuredBuffer(thisBaseType))
+      if (TypeTranslator::isStructuredBuffer(thisBaseType))
         indices->push_back(theBuilder.getConstantInt32(0));
 
       if ((hlsl::IsHLSLVecType(thisBaseType) &&

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

@@ -467,6 +467,18 @@ private:
   /// the loaded value for .Consume; returns zero for .Append().
   uint32_t processACSBufferAppendConsume(const CXXMemberCallExpr *expr);
 
+private:
+  /// \brief Queries the given (RW)Buffer/(RW)Texture image in the given expr
+  /// for the requested information. Based on the dimension of the image, the
+  /// following info can be queried: width, height, depth, number of mipmap
+  /// levels.
+  uint32_t processBufferTextureGetDimensions(const CXXMemberCallExpr *);
+
+  /// \brief Processes the GetDimensions intrinsic function call on a
+  /// (RW)ByteAddressBuffer by querying the image in the given expr.
+  uint32_t processByteAddressBufferStructuredBufferGetDimensions(
+      const CXXMemberCallExpr *);
+
 private:
   /// \brief Wrapper method to create an error message and report it
   /// in the diagnostic engine associated with this consumer.

+ 8 - 0
tools/clang/lib/SPIRV/TypeTranslator.cpp

@@ -263,6 +263,14 @@ bool TypeTranslator::isRWByteAddressBuffer(QualType type) {
   return false;
 }
 
+bool TypeTranslator::isStructuredBuffer(QualType type) {
+  const auto *recordType = type->getAs<RecordType>();
+  if (!recordType)
+    return false;
+  const auto name = recordType->getDecl()->getName();
+  return name == "StructuredBuffer" || name == "RWStructuredBuffer";
+}
+
 bool TypeTranslator::isByteAddressBuffer(QualType type) {
   if (const auto *rt = type->getAs<RecordType>()) {
     return rt->getDecl()->getName() == "ByteAddressBuffer";

+ 13 - 9
tools/clang/lib/SPIRV/TypeTranslator.h

@@ -59,11 +59,14 @@ public:
   /// integer value. This type will be decorated with BufferBlock.
   uint32_t getACSBufferCounter();
 
+  /// \brief Returns true if the given type is a (RW)StructuredBuffer type.
+  static bool isStructuredBuffer(QualType type);
+
   /// \brief Returns true if the given type is the HLSL ByteAddressBufferType.
-  bool isByteAddressBuffer(QualType type);
+  static bool isByteAddressBuffer(QualType type);
 
   /// \brief Returns true if the given type is the HLSL RWByteAddressBufferType.
-  bool isRWByteAddressBuffer(QualType type);
+  static bool isRWByteAddressBuffer(QualType type);
 
   /// \brief Returns true if the given type is the HLSL Buffer type.
   static bool isBuffer(QualType type);
@@ -164,6 +167,14 @@ private:
   /// instructions and returns the <result-id>. Returns 0 on failure.
   uint32_t translateResourceType(QualType type, LayoutRule rule);
 
+  /// \bried For the given sampled type, returns the corresponding image format
+  /// that can be used to create an image object.
+  spv::ImageFormat translateSampledTypeToImageFormat(QualType type);
+
+  /// \brief Returns a string name for the given type.
+  static std::string getName(QualType type);
+
+public:
   /// \brief Returns the alignment and size in bytes for the given type
   /// according to the given LayoutRule.
 
@@ -180,13 +191,6 @@ private:
                                                     bool isRowMajor,
                                                     uint32_t *stride);
 
-  /// \bried For the given sampled type, returns the corresponding image format
-  /// that can be used to create an image object.
-  spv::ImageFormat translateSampledTypeToImageFormat(QualType type);
-
-  /// \brief Returns a string name for the given type.
-  static std::string getName(QualType type);
-
 private:
   ASTContext &astContext;
   ModuleBuilder &theBuilder;

+ 20 - 0
tools/clang/test/CodeGenSPIRV/buffer.get-dimensions.hlsl

@@ -0,0 +1,20 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK: OpCapability ImageQuery
+
+Buffer<uint3> b1;
+RWBuffer<float4> b2;
+
+void main() {
+  uint dim;
+
+// CHECK:          [[b1:%\d+]] = OpLoad %type_buffer_image %b1
+// CHECK-NEXT: [[query1:%\d+]] = OpImageQuerySize %uint [[b1]]
+// CHECK-NEXT:                   OpStore %dim [[query1]]
+  b1.GetDimensions(dim);
+
+// CHECK:          [[b2:%\d+]] = OpLoad %type_buffer_image_0 %b2
+// CHECK-NEXT: [[query2:%\d+]] = OpImageQuerySize %uint [[b2]]
+// CHECK-NEXT:                   OpStore %dim [[query2]]
+  b2.GetDimensions(dim);
+}

+ 20 - 0
tools/clang/test/CodeGenSPIRV/method.byte-address-buffer.get-dimensions.hlsl

@@ -0,0 +1,20 @@
+// Run: %dxc -T ps_6_0 -E main
+
+ByteAddressBuffer   b1;
+RWByteAddressBuffer b2;
+
+void main() {
+  uint dim;
+
+// CHECK:             [[b1:%\d+]] = OpLoad %type_ByteAddressBuffer %b1
+// CHECK-NEXT:      [[dim1:%\d+]] = OpArrayLength %uint [[b1]] 0
+// CHECK-NEXT: [[numBytes1:%\d+]] = OpIMul %uint [[dim1]] %uint_4
+// CHECK-NEXT:                      OpStore %dim [[numBytes1]]
+  b1.GetDimensions(dim);
+
+// CHECK:             [[b2:%\d+]] = OpLoad %type_RWByteAddressBuffer %b2
+// CHECK-NEXT:      [[dim2:%\d+]] = OpArrayLength %uint [[b2]] 0
+// CHECK-NEXT: [[numBytes2:%\d+]] = OpIMul %uint [[dim2]] %uint_4
+// CHECK-NEXT:                      OpStore %dim [[numBytes2]]
+  b2.GetDimensions(dim);
+}

+ 25 - 0
tools/clang/test/CodeGenSPIRV/method.structured-buffer.get-dimensions.hlsl

@@ -0,0 +1,25 @@
+// Run: %dxc -T ps_6_0 -E main
+
+struct SBuffer {
+  float4   f1;
+  float2x3 f2[3];
+};
+
+  StructuredBuffer<SBuffer> mySBuffer1;
+RWStructuredBuffer<SBuffer> mySBuffer2;
+
+void main() {
+  uint numStructs, stride;
+
+// CHECK:       [[sb1:%\d+]] = OpLoad %type_StructuredBuffer_SBuffer %mySBuffer1
+// CHECK-NEXT: [[len1:%\d+]] = OpArrayLength %uint [[sb1]] 0
+// CHECK-NEXT:                 OpStore %numStructs [[len1]]
+// CHECK-NEXT:                 OpStore %stride %uint_96
+  mySBuffer1.GetDimensions(numStructs, stride);
+
+// CHECK:       [[sb2:%\d+]] = OpLoad %type_RWStructuredBuffer_SBuffer %mySBuffer2
+// CHECK-NEXT: [[len2:%\d+]] = OpArrayLength %uint [[sb2]] 0
+// CHECK-NEXT:                 OpStore %numStructs [[len2]]
+// CHECK-NEXT:                 OpStore %stride %uint_96
+  mySBuffer2.GetDimensions(numStructs, stride);
+}

+ 54 - 0
tools/clang/test/CodeGenSPIRV/rwtexture.get-dimensions.hlsl

@@ -0,0 +1,54 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK: OpCapability ImageQuery
+
+RWTexture1D        <float> t1;
+RWTexture1DArray   <float> t2;
+RWTexture2D        <float> t3;
+RWTexture2DArray   <float> t4;
+RWTexture3D        <float> t5;
+
+void main() {
+  uint width, height, depth, elements;
+
+// CHECK:            [[t1:%\d+]] = OpLoad %type_1d_image %t1
+// CHECK-NEXT:   [[query1:%\d+]] = OpImageQuerySize %uint [[t1]]
+// CHECK-NEXT:                     OpStore %width [[query1]]
+  t1.GetDimensions(width);
+
+// CHECK:            [[t2:%\d+]] = OpLoad %type_1d_image_array %t2
+// CHECK-NEXT:   [[query2:%\d+]] = OpImageQuerySize %v2uint [[t2]]
+// CHECK-NEXT: [[query2_0:%\d+]] = OpCompositeExtract %uint [[query2]] 0
+// CHECK-NEXT:                     OpStore %width [[query2_0]]
+// CHECK-NEXT: [[query2_1:%\d+]] = OpCompositeExtract %uint [[query2]] 1
+// CHECK-NEXT:                     OpStore %elements [[query2_1]]  
+  t2.GetDimensions(width, elements);
+
+// CHECK:            [[t3:%\d+]] = OpLoad %type_2d_image %t3
+// CHECK-NEXT:   [[query3:%\d+]] = OpImageQuerySize %v2uint [[t3]]
+// CHECK-NEXT: [[query3_0:%\d+]] = OpCompositeExtract %uint [[query3]] 0
+// CHECK-NEXT:                     OpStore %width [[query3_0]]
+// CHECK-NEXT: [[query3_1:%\d+]] = OpCompositeExtract %uint [[query3]] 1
+// CHECK-NEXT:                     OpStore %height [[query3_1]]  
+  t3.GetDimensions(width, height);
+
+// CHECK:            [[t4:%\d+]] = OpLoad %type_2d_image_array %t4
+// CHECK-NEXT:   [[query4:%\d+]] = OpImageQuerySize %v3uint [[t4]]
+// CHECK-NEXT: [[query4_0:%\d+]] = OpCompositeExtract %uint [[query4]] 0
+// CHECK-NEXT:                     OpStore %width [[query4_0]]
+// CHECK-NEXT: [[query4_1:%\d+]] = OpCompositeExtract %uint [[query4]] 1
+// CHECK-NEXT:                     OpStore %height [[query4_1]]
+// CHECK-NEXT: [[query4_2:%\d+]] = OpCompositeExtract %uint [[query4]] 2
+// CHECK-NEXT:                     OpStore %elements [[query4_2]]
+  t4.GetDimensions(width, height, elements);
+
+// CHECK:            [[t5:%\d+]] = OpLoad %type_3d_image %t5
+// CHECK-NEXT:   [[query5:%\d+]] = OpImageQuerySize %v3uint [[t5]]
+// CHECK-NEXT: [[query5_0:%\d+]] = OpCompositeExtract %uint [[query5]] 0
+// CHECK-NEXT:                     OpStore %width [[query5_0]]
+// CHECK-NEXT: [[query5_1:%\d+]] = OpCompositeExtract %uint [[query5]] 1
+// CHECK-NEXT:                     OpStore %height [[query5_1]]
+// CHECK-NEXT: [[query5_2:%\d+]] = OpCompositeExtract %uint [[query5]] 2
+// CHECK-NEXT:                     OpStore %depth [[query5_2]]
+  t5.GetDimensions(width, height, depth);
+}

+ 135 - 0
tools/clang/test/CodeGenSPIRV/texture.get-dimensions.hlsl

@@ -0,0 +1,135 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK: OpCapability ImageQuery
+
+Texture1D        <float> t1;
+Texture1DArray   <float> t2;
+Texture2D        <float> t3;
+Texture2DArray   <float> t4;
+Texture3D        <float> t5;
+Texture2DMS      <float> t6;
+Texture2DMSArray <float> t7;
+
+void main() {
+  uint mipLevel = 1;
+  uint width, height, depth, elements, numLevels, numSamples;
+
+// CHECK:        [[t1_0:%\d+]] = OpLoad %type_1d_image %t1
+// CHECK-NEXT: [[query0:%\d+]] = OpImageQuerySizeLod %uint [[t1_0]] %int_0
+// CHECK-NEXT:                   OpStore %width [[query0]]
+  t1.GetDimensions(width);
+
+// CHECK:        [[t1_1:%\d+]] = OpLoad %type_1d_image %t1
+// CHECK-NEXT:   [[mip0:%\d+]] = OpLoad %uint %mipLevel
+// CHECK-NEXT: [[query1:%\d+]] = OpImageQuerySizeLod %uint [[t1_1]] [[mip0]]
+// CHECK-NEXT:                   OpStore %width [[query1]]
+// CHECK-NEXT: [[query2:%\d+]] = OpImageQueryLevels %uint [[t1_1]]
+// CHECK-NEXT:                   OpStore %numLevels [[query2]]
+  t1.GetDimensions(mipLevel, width, numLevels);
+
+// CHECK:          [[t2_0:%\d+]] = OpLoad %type_1d_image_array %t2
+// CHECK-NEXT:   [[query3:%\d+]] = OpImageQuerySizeLod %v2uint [[t2_0]] %int_0
+// CHECK-NEXT: [[query3_0:%\d+]] = OpCompositeExtract %uint [[query3]] 0
+// CHECK-NEXT:                     OpStore %width [[query3_0]]
+// CHECK-NEXT: [[query3_1:%\d+]] = OpCompositeExtract %uint [[query3]] 1
+// CHECK-NEXT:                     OpStore %elements [[query3_1]]
+  t2.GetDimensions(width, elements);
+
+// CHECK:          [[t2_1:%\d+]] = OpLoad %type_1d_image_array %t2
+// CHECK-NEXT:     [[mip1:%\d+]] = OpLoad %uint %mipLevel
+// CHECK-NEXT:   [[query4:%\d+]] = OpImageQuerySizeLod %v2uint [[t2_1]] [[mip1]]
+// CHECK-NEXT: [[query4_0:%\d+]] = OpCompositeExtract %uint [[query4]] 0
+// CHECK-NEXT:                     OpStore %width [[query4_0]]
+// CHECK-NEXT: [[query4_1:%\d+]] = OpCompositeExtract %uint [[query4]] 1
+// CHECK-NEXT:                     OpStore %elements [[query4_1]]
+// CHECK-NEXT:   [[query5:%\d+]] = OpImageQueryLevels %uint [[t2_1]]
+// CHECK-NEXT:                     OpStore %numLevels [[query5]]
+  t2.GetDimensions(mipLevel, width, elements, numLevels);
+
+// CHECK:          [[t3_0:%\d+]] = OpLoad %type_2d_image %t3
+// CHECK-NEXT:   [[query5:%\d+]] = OpImageQuerySizeLod %v2uint [[t3_0]] %int_0
+// CHECK-NEXT: [[query5_0:%\d+]] = OpCompositeExtract %uint [[query5]] 0
+// CHECK-NEXT:                     OpStore %width [[query5_0]]
+// CHECK-NEXT: [[query5_1:%\d+]] = OpCompositeExtract %uint [[query5]] 1
+// CHECK-NEXT:                     OpStore %height [[query5_1]]
+  t3.GetDimensions(width, height);
+  
+// CHECK:          [[t3_1:%\d+]] = OpLoad %type_2d_image %t3
+// CHECK-NEXT:     [[mip2:%\d+]] = OpLoad %uint %mipLevel
+// CHECK-NEXT:   [[query6:%\d+]] = OpImageQuerySizeLod %v2uint [[t3_1]] [[mip2]]
+// CHECK-NEXT: [[query6_0:%\d+]] = OpCompositeExtract %uint [[query6]] 0
+// CHECK-NEXT:                     OpStore %width [[query6_0]]
+// CHECK-NEXT: [[query6_1:%\d+]] = OpCompositeExtract %uint [[query6]] 1
+// CHECK-NEXT:                     OpStore %height [[query6_1]]
+// CHECK-NEXT:   [[query7:%\d+]] = OpImageQueryLevels %uint [[t3_1]]
+// CHECK-NEXT:                     OpStore %numLevels [[query7]]
+  t3.GetDimensions(mipLevel, width, height, numLevels);
+
+// CHECK:          [[t4_0:%\d+]] = OpLoad %type_2d_image_array %t4
+// CHECK-NEXT:   [[query8:%\d+]] = OpImageQuerySizeLod %v3uint [[t4_0]] %int_0
+// CHECK-NEXT: [[query8_0:%\d+]] = OpCompositeExtract %uint [[query8]] 0
+// CHECK-NEXT:                     OpStore %width [[query8_0]]
+// CHECK-NEXT: [[query8_1:%\d+]] = OpCompositeExtract %uint [[query8]] 1
+// CHECK-NEXT:                     OpStore %height [[query8_1]]
+// CHECK-NEXT: [[query8_2:%\d+]] = OpCompositeExtract %uint [[query8]] 2
+// CHECK-NEXT:                     OpStore %elements [[query8_2]]
+  t4.GetDimensions(width, height, elements);
+  
+// CHECK:          [[t4_1:%\d+]] = OpLoad %type_2d_image_array %t4
+// CHECK-NEXT:     [[mip3:%\d+]] = OpLoad %uint %mipLevel
+// CHECK-NEXT:   [[query9:%\d+]] = OpImageQuerySizeLod %v3uint [[t4_1]] [[mip3]]
+// CHECK-NEXT: [[query9_0:%\d+]] = OpCompositeExtract %uint [[query9]] 0
+// CHECK-NEXT:                     OpStore %width [[query9_0]]
+// CHECK-NEXT: [[query9_1:%\d+]] = OpCompositeExtract %uint [[query9]] 1
+// CHECK-NEXT:                     OpStore %height [[query9_1]]
+// CHECK-NEXT: [[query9_2:%\d+]] = OpCompositeExtract %uint [[query9]] 2
+// CHECK-NEXT:                     OpStore %elements [[query9_2]]
+// CHECK-NEXT:  [[query10:%\d+]] = OpImageQueryLevels %uint [[t4_1]]
+// CHECK-NEXT:                     OpStore %numLevels [[query10]]
+  t4.GetDimensions(mipLevel, width, height, elements, numLevels);
+
+// CHECK:           [[t5_0:%\d+]] = OpLoad %type_3d_image %t5
+// CHECK-NEXT:   [[query11:%\d+]] = OpImageQuerySizeLod %v3uint [[t5_0]] %int_0
+// CHECK-NEXT: [[query11_0:%\d+]] = OpCompositeExtract %uint [[query11]] 0
+// CHECK-NEXT:                      OpStore %width [[query11_0]]
+// CHECK-NEXT: [[query11_1:%\d+]] = OpCompositeExtract %uint [[query11]] 1
+// CHECK-NEXT:                      OpStore %height [[query11_1]]
+// CHECK-NEXT: [[query11_2:%\d+]] = OpCompositeExtract %uint [[query11]] 2
+// CHECK-NEXT:                      OpStore %depth [[query11_2]]
+  t5.GetDimensions(width, height, depth);
+
+// CHECK:           [[t5_1:%\d+]] = OpLoad %type_3d_image %t5
+// CHECK-NEXT:      [[mip4:%\d+]] = OpLoad %uint %mipLevel
+// CHECK-NEXT:   [[query12:%\d+]] = OpImageQuerySizeLod %v3uint [[t5_1]] [[mip4]]
+// CHECK-NEXT: [[query12_0:%\d+]] = OpCompositeExtract %uint [[query12]] 0
+// CHECK-NEXT:                      OpStore %width [[query12_0]]
+// CHECK-NEXT: [[query12_1:%\d+]] = OpCompositeExtract %uint [[query12]] 1
+// CHECK-NEXT:                      OpStore %height [[query12_1]]
+// CHECK-NEXT: [[query12_2:%\d+]] = OpCompositeExtract %uint [[query12]] 2
+// CHECK-NEXT:                      OpStore %depth [[query12_2]]
+// CHECK-NEXT:   [[query13:%\d+]] = OpImageQueryLevels %uint [[t5_1]]
+// CHECK-NEXT:                      OpStore %numLevels [[query13]]
+  t5.GetDimensions(mipLevel, width, height, depth, numLevels);
+
+// CHECK:             [[t6:%\d+]] = OpLoad %type_2d_image_0 %t6
+// CHECK-NEXT:   [[query14:%\d+]] = OpImageQuerySize %v2uint [[t6]]
+// CHECK-NEXT: [[query14_0:%\d+]] = OpCompositeExtract %uint [[query14]] 0
+// CHECK-NEXT:                      OpStore %width [[query14_0]]
+// CHECK-NEXT: [[query14_1:%\d+]] = OpCompositeExtract %uint [[query14]] 1
+// CHECK-NEXT:                      OpStore %height [[query14_1]]
+// CHECK-NEXT:   [[query15:%\d+]] = OpImageQuerySamples %uint [[t6]]
+// CHECK-NEXT:                      OpStore %numSamples [[query15]]
+  t6.GetDimensions(width, height, numSamples);
+
+// CHECK:             [[t7:%\d+]] = OpLoad %type_2d_image_array_0 %t7
+// CHECK-NEXT:   [[query16:%\d+]] = OpImageQuerySize %v3uint [[t7]]
+// CHECK-NEXT: [[query16_0:%\d+]] = OpCompositeExtract %uint [[query16]] 0
+// CHECK-NEXT:                      OpStore %width [[query16_0]]
+// CHECK-NEXT: [[query16_1:%\d+]] = OpCompositeExtract %uint [[query16]] 1
+// CHECK-NEXT:                      OpStore %height [[query16_1]]
+// CHECK-NEXT: [[query16_2:%\d+]] = OpCompositeExtract %uint [[query16]] 2
+// CHECK-NEXT:                      OpStore %elements [[query16_2]]
+// CHECK-NEXT:   [[query17:%\d+]] = OpImageQuerySamples %uint [[t7]]
+// CHECK-NEXT:                      OpStore %numSamples [[query17]]
+  t7.GetDimensions(width, height, elements, numSamples);
+}

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

@@ -368,6 +368,9 @@ TEST_F(FileTest, TextureArraySample) {
 }
 TEST_F(FileTest, TextureLoad) { runFileTest("texture.load.hlsl"); }
 TEST_F(FileTest, TextureArrayLoad) { runFileTest("texture.array.load.hlsl"); }
+TEST_F(FileTest, TextureGetDimensions) {
+  runFileTest("texture.get-dimensions.hlsl");
+}
 TEST_F(FileTest, TextureGather) { runFileTest("texture.gather.hlsl"); }
 TEST_F(FileTest, TextureArrayGather) {
   runFileTest("texture.array.gather.hlsl");
@@ -390,12 +393,16 @@ TEST_F(FileTest, TextureArraySampleGrad) {
 TEST_F(FileTest, StructuredBufferLoad) {
   runFileTest("method.structured-buffer.load.hlsl");
 }
+TEST_F(FileTest, StructuredBufferGetDimensions) {
+  runFileTest("method.structured-buffer.get-dimensions.hlsl");
+}
 TEST_F(FileTest, AppendStructuredBufferAppend) {
   runFileTest("method.append-structured-buffer.append.hlsl");
 }
 TEST_F(FileTest, ConsumeStructuredBufferConsume) {
   runFileTest("method.consume-structured-buffer.consume.hlsl");
 }
+
 // For ByteAddressBuffer methods
 TEST_F(FileTest, ByteAddressBufferLoad) {
   runFileTest("method.byte-address-buffer.load.hlsl");
@@ -403,14 +410,23 @@ TEST_F(FileTest, ByteAddressBufferLoad) {
 TEST_F(FileTest, ByteAddressBufferStore) {
   runFileTest("method.byte-address-buffer.store.hlsl");
 }
+TEST_F(FileTest, ByteAddressBufferGetDimensions) {
+  runFileTest("method.byte-address-buffer.get-dimensions.hlsl");
+}
 
 // For Buffer/RWBuffer methods
 TEST_F(FileTest, BufferLoad) { runFileTest("buffer.load.hlsl"); }
 TEST_F(FileTest, BufferWrite) { runFileTest("buffer.write.hlsl"); }
+TEST_F(FileTest, BufferGetDimensions) {
+  runFileTest("buffer.get-dimensions.hlsl");
+}
 
 // For RWTexture methods
 TEST_F(FileTest, RWTextureLoad) { runFileTest("rwtexture.load.hlsl"); }
 TEST_F(FileTest, RWTextureWrite) { runFileTest("rwtexture.write.hlsl"); }
+TEST_F(FileTest, RWTextureGetDimensions) {
+  runFileTest("rwtexture.get-dimensions.hlsl");
+}
 
 // For intrinsic functions
 TEST_F(FileTest, IntrinsicsDot) { runFileTest("intrinsics.dot.hlsl"); }