[llvm-branch-commits] [clang] [NFC][HLSL] Refactor texture type declaration (PR #219561)

Deric C. via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Aug 28 11:51:53 PDT 2026


https://github.com/Icohedron created https://github.com/llvm/llvm-project/pull/219561

Fixes https://github.com/llvm/llvm-project/issues/219542

Refactors texture type declaration in `HLSLExternalSemaSource.cpp` so that new
texture types can more easily be added without adding a bunch of new helper
functions.

This is accomplished with the introduction of a new `TextureTypeInfo` struct and
to record the properties of each texture type, as well as its capabilities
indicated by the `TexCap` bitmask enum.

Adding a new texture type to be declared should, in most cases, only require
appending a new entry to the static `TextureTypes` array of `TextureTypeInfo`.

Assisted by: Claude Opus 5

---

<sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>

>From b08dda47d66d4b20494fc824d6b740bb4ca90370 Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Fri, 28 Aug 2026 11:40:44 -0700
Subject: [PATCH] [NFC][HLSL] Refactor texture type declaration

---
 clang/lib/Sema/HLSLExternalSemaSource.cpp | 363 +++++++++-------------
 1 file changed, 150 insertions(+), 213 deletions(-)

diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index e8a6147e20a3f..14442876a0467 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -24,6 +24,7 @@
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaHLSL.h"
+#include "llvm/ADT/BitmaskEnum.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
 
@@ -254,99 +255,134 @@ static BuiltinTypeDeclBuilder setupSamplerType(CXXRecordDecl *Decl, Sema &S) {
       .addStaticInitializationFunctions(false);
 }
 
-/// Set up common members and attributes for texture types
+namespace {
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
+/// Which members a texture type has. Overloads within a member family
+/// (e.g., offset overloads for samplers) follow from ResourceDimension.
+enum class TexCap : uint32_t {
+  Load = 1u << 0,      // Load(int<N+1>) taking a mip level
+  LoadMS = 1u << 1,    // Load(int<N>, int sampleIndex) on a multisampled type
+  LoadRW = 1u << 2,    // Load(int<N>) on a writable texture
+  Subscript = 1u << 3, // operator[]
+  Mips = 1u << 4,      // mips[]
+  Sample = 1u << 5,    // Sample, SampleBias, SampleGrad, SampleLevel
+  SampleCmp = 1u << 6, // SampleCmp, SampleCmpLevelZero
+  Gather = 1u << 7,    // Gather*, GatherCmp*
+  CalcLOD = 1u << 8,   // CalculateLevelOfDetail, ...Unclamped
+  GetDims = 1u << 9,   // GetDimensions
+
+  // TODO: multisampled types need an MS-specific GetDimensions
+  // https://github.com/llvm/wg-hlsl/issues/347
+
+  LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/GetDims)
+};
+
+/// How a type's template parameters are spelled. Independent of its
+/// capabilities; also decides which types get a vector partial specialization.
+enum class TemplateShape {
+  ElementType,               // template<typename T = float4>
+  ElementTypeAndSampleCount, // template<typename T, uint N>
+};
+
+struct TextureTypeInfo {
+  const char *Name;
+  ResourceClass RC;
+  ResourceDimension Dim;
+  bool IsArray;
+  bool IsROV;
+  TemplateShape Shape;
+  TexCap Caps;
+
+  bool has(TexCap C) const { return (Caps & C) != TexCap{}; }
+  bool hasSampleCount() const {
+    return Shape == TemplateShape::ElementTypeAndSampleCount;
+  }
+};
+} // namespace
+
+static const TextureTypeInfo TextureTypes[] = {
+    {"Texture2D", ResourceClass::SRV, ResourceDimension::Dim2D,
+     /*IsArray=*/false, /*IsROV=*/false, TemplateShape::ElementType,
+     TexCap::Load | TexCap::Subscript | TexCap::Mips | TexCap::Sample |
+         TexCap::SampleCmp | TexCap::CalcLOD | TexCap::Gather |
+         TexCap::GetDims},
+    {"RWTexture2D", ResourceClass::UAV, ResourceDimension::Dim2D,
+     /*IsArray=*/false, /*IsROV=*/false, TemplateShape::ElementType,
+     TexCap::LoadRW | TexCap::Subscript | TexCap::GetDims},
+    {"Texture2DArray", ResourceClass::SRV, ResourceDimension::Dim2D,
+     /*IsArray=*/true, /*IsROV=*/false, TemplateShape::ElementType,
+     TexCap::Load | TexCap::Subscript | TexCap::Mips | TexCap::Sample |
+         TexCap::SampleCmp | TexCap::CalcLOD | TexCap::Gather |
+         TexCap::GetDims},
+    {"RWTexture2DArray", ResourceClass::UAV, ResourceDimension::Dim2D,
+     /*IsArray=*/true, /*IsROV=*/false, TemplateShape::ElementType,
+     TexCap::LoadRW | TexCap::Subscript | TexCap::GetDims},
+    {"Texture2DMS", ResourceClass::SRV, ResourceDimension::Dim2D,
+     /*IsArray=*/false, /*IsROV=*/false,
+     TemplateShape::ElementTypeAndSampleCount,
+     TexCap::LoadMS | TexCap::Subscript},
+    {"TextureCube", ResourceClass::SRV, ResourceDimension::Cube,
+     /*IsArray=*/false, /*IsROV=*/false, TemplateShape::ElementType,
+     TexCap::Sample | TexCap::SampleCmp | TexCap::CalcLOD | TexCap::Gather |
+         TexCap::GetDims},
+    {"TextureCubeArray", ResourceClass::SRV, ResourceDimension::Cube,
+     /*IsArray=*/true, /*IsROV=*/false, TemplateShape::ElementType,
+     TexCap::Sample | TexCap::SampleCmp | TexCap::CalcLOD | TexCap::Gather |
+         TexCap::GetDims},
+};
+
 static BuiltinTypeDeclBuilder setupTextureType(CXXRecordDecl *Decl, Sema &S,
-                                               ResourceClass RC, bool IsROV,
-                                               bool IsArray,
-                                               ResourceDimension Dim) {
-  return BuiltinTypeDeclBuilder(S, Decl)
-      .addTextureHandle(RC, IsROV, IsArray, Dim)
-      .addTextureLoadMethods(Dim, IsArray)
-      .addArraySubscriptOperators(Dim, IsArray)
-      .addMipsMember(Dim)
-      .addDefaultHandleConstructor()
-      .addCopyConstructor()
-      .addCopyAssignmentOperator()
-      .addStaticInitializationFunctions(false)
-      .addSampleMethods(Dim, IsArray)
-      .addSampleBiasMethods(Dim, IsArray)
-      .addSampleGradMethods(Dim, IsArray)
-      .addSampleLevelMethods(Dim, IsArray)
-      .addSampleCmpMethods(Dim, IsArray)
-      .addSampleCmpLevelZeroMethods(Dim, IsArray)
-      .addCalculateLodMethods(Dim)
-      .addGetDimensionsMethods(Dim)
-      .addGatherMethods(Dim, IsArray)
-      .addGatherCmpMethods(Dim, IsArray);
-}
+                                               const TextureTypeInfo &T) {
+  const ResourceDimension Dim = T.Dim;
+  const bool IsArray = T.IsArray;
+
+  Expr *SampleCountExpr = nullptr;
+  if (T.hasSampleCount()) {
+    ClassTemplateDecl *CTD = Decl->getDescribedClassTemplate();
+    assert(CTD && "multisampled texture must be a class template");
+    // Parameter 1 is the N in Texture2DMS<T, N>.
+    auto *NTTP = cast<NonTypeTemplateParmDecl>(
+        CTD->getTemplateParameters()->getParam(1));
+    SampleCountExpr =
+        S.BuildDeclRefExpr(NTTP, NTTP->getType(), VK_PRValue, SourceLocation());
+  }
 
-/// Set up RWTexture type: UAV texture with only operator[] (uint2, read/write),
-/// Load and GetDimensions (no sample/gather/mips/LOD).
-static BuiltinTypeDeclBuilder setupRWTextureType(CXXRecordDecl *Decl, Sema &S,
-                                                 bool IsArray,
-                                                 ResourceDimension Dim) {
-  return BuiltinTypeDeclBuilder(S, Decl)
-      .addTextureHandle(ResourceClass::UAV, /*IsROV=*/false, IsArray, Dim)
-      .addRWTextureLoadMethods(Dim, IsArray)
-      .addArraySubscriptOperators(Dim, IsArray)
-      .addGetDimensionsMethods(Dim)
-      .addDefaultHandleConstructor()
+  BuiltinTypeDeclBuilder B(S, Decl);
+  B.addTextureHandle(T.RC, T.IsROV, IsArray, Dim, SampleCountExpr);
+
+  if (T.has(TexCap::Load))
+    B.addTextureLoadMethods(Dim, IsArray);
+  if (T.has(TexCap::LoadMS))
+    B.addTextureLoadMSMethods(Dim, IsArray);
+  if (T.has(TexCap::LoadRW))
+    B.addRWTextureLoadMethods(Dim, IsArray);
+  if (T.has(TexCap::Subscript))
+    B.addArraySubscriptOperators(Dim, IsArray);
+  if (T.has(TexCap::Mips))
+    B.addMipsMember(Dim);
+
+  B.addDefaultHandleConstructor()
       .addCopyConstructor()
       .addCopyAssignmentOperator()
       .addStaticInitializationFunctions(false);
-}
 
-/// Set up TextureCube and TextureCubeArray types: SRV cube textures. Locations
-/// are direction vectors into the cube rather than texel coordinates, so cube
-/// textures have no Load, no operator[] and no mips member. Their sampling and
-/// gather methods also have no offset overloads.
-static BuiltinTypeDeclBuilder setupTextureCubeType(CXXRecordDecl *Decl, Sema &S,
-                                                   bool IsArray) {
-  const ResourceDimension Dim = ResourceDimension::Cube;
-  return BuiltinTypeDeclBuilder(S, Decl)
-      .addTextureHandle(ResourceClass::SRV, /*IsROV=*/false, IsArray, Dim)
-      .addDefaultHandleConstructor()
-      .addCopyConstructor()
-      .addCopyAssignmentOperator()
-      .addStaticInitializationFunctions(false)
-      .addSampleMethods(Dim, IsArray)
-      .addSampleBiasMethods(Dim, IsArray)
-      .addSampleGradMethods(Dim, IsArray)
-      .addSampleLevelMethods(Dim, IsArray)
-      .addSampleCmpMethods(Dim, IsArray)
-      .addSampleCmpLevelZeroMethods(Dim, IsArray)
-      .addCalculateLodMethods(Dim)
-      .addGetDimensionsMethods(Dim)
-      .addGatherMethods(Dim, IsArray)
-      .addGatherCmpMethods(Dim, IsArray);
-}
-
-/// Set up Texture2DMS (multisampled) type: SRV texture with only operator[]
-/// and sample-indexed Load. It does not support sampling, gather, LOD, or mips.
-static BuiltinTypeDeclBuilder setupMSTextureType(CXXRecordDecl *Decl, Sema &S,
-                                                 bool IsArray,
-                                                 ResourceDimension Dim) {
-  ClassTemplateDecl *CTD = Decl->getDescribedClassTemplate();
-  assert(CTD && "multisampled texture must be a class template");
-
-  // Parameter 1 is the N in Texture2DMS<T, N>.
-  auto *NTTP =
-      cast<NonTypeTemplateParmDecl>(CTD->getTemplateParameters()->getParam(1));
-  Expr *SampleCountExpr =
-      S.BuildDeclRefExpr(NTTP, NTTP->getType(), VK_PRValue, SourceLocation());
-
-  return BuiltinTypeDeclBuilder(S, Decl)
-      .addTextureHandle(ResourceClass::SRV, /*IsROV=*/false, IsArray, Dim,
-                        SampleCountExpr)
-      .addTextureLoadMSMethods(Dim, IsArray)
-      .addArraySubscriptOperators(Dim, IsArray)
-      // TODO: Add MS-specific GetDimensions (with a NumberOfSamples output);
-      // the generic addGetDimensionsMethods is mip-based and unsuitable here.
-      // https://github.com/llvm/wg-hlsl/issues/347
-      .addDefaultHandleConstructor()
-      .addCopyConstructor()
-      .addCopyAssignmentOperator()
-      .addStaticInitializationFunctions(false);
+  if (T.has(TexCap::Sample))
+    B.addSampleMethods(Dim, IsArray)
+        .addSampleBiasMethods(Dim, IsArray)
+        .addSampleGradMethods(Dim, IsArray)
+        .addSampleLevelMethods(Dim, IsArray);
+  if (T.has(TexCap::SampleCmp))
+    B.addSampleCmpMethods(Dim, IsArray)
+        .addSampleCmpLevelZeroMethods(Dim, IsArray);
+  if (T.has(TexCap::CalcLOD))
+    B.addCalculateLodMethods(Dim);
+  if (T.has(TexCap::GetDims))
+    B.addGetDimensionsMethods(Dim);
+  if (T.has(TexCap::Gather))
+    B.addGatherMethods(Dim, IsArray).addGatherCmpMethods(Dim, IsArray);
+
+  return B;
 }
 
 // Add a partial specialization for a template. The `TextureTemplate` is
@@ -742,132 +778,33 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
   });
 
   QualType Float4Ty = AST.getExtVectorType(AST.FloatTy, 4);
-  Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Texture2D")
-             .addSimpleTemplateParams({"element_type"}, {Float4Ty},
-                                      TypedBufferConcept)
-             .finalizeForwardDeclaration();
-
-  onCompletion(Decl, [this](CXXRecordDecl *Decl) {
-    setupTextureType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false,
-                     /*IsArray=*/false, ResourceDimension::Dim2D)
-        .completeDefinition();
-  });
-
-  auto *PartialSpec = addVectorTexturePartialSpecialization(
-      *SemaPtr, HLSLNamespace, Decl->getDescribedClassTemplate());
-  onCompletion(PartialSpec, [this](CXXRecordDecl *Decl) {
-    setupTextureType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false,
-                     /*IsArray=*/false, ResourceDimension::Dim2D)
-        .completeDefinition();
-  });
-
-  Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWTexture2D")
-             .addSimpleTemplateParams({"element_type"}, {Float4Ty},
-                                      TypedBufferConcept)
-             .finalizeForwardDeclaration();
-
-  onCompletion(Decl, [this](CXXRecordDecl *Decl) {
-    setupRWTextureType(Decl, *SemaPtr, /*IsArray=*/false,
-                       ResourceDimension::Dim2D)
-        .completeDefinition();
-  });
-
-  auto *PartialSpecRW = addVectorTexturePartialSpecialization(
-      *SemaPtr, HLSLNamespace, Decl->getDescribedClassTemplate());
-  onCompletion(PartialSpecRW, [this](CXXRecordDecl *Decl) {
-    setupRWTextureType(Decl, *SemaPtr, /*IsArray=*/false,
-                       ResourceDimension::Dim2D)
-        .completeDefinition();
-  });
-
-  // Texture2DArray — same as Texture2D but IsArray=true
-  Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Texture2DArray")
-             .addSimpleTemplateParams({"element_type"}, {Float4Ty},
-                                      TypedBufferConcept)
-             .finalizeForwardDeclaration();
-
-  onCompletion(Decl, [this](CXXRecordDecl *Decl) {
-    setupTextureType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false,
-                     /*IsArray=*/true, ResourceDimension::Dim2D)
-        .completeDefinition();
-  });
-
-  auto *PartialSpec2DA = addVectorTexturePartialSpecialization(
-      *SemaPtr, HLSLNamespace, Decl->getDescribedClassTemplate());
-  onCompletion(PartialSpec2DA, [this](CXXRecordDecl *Decl) {
-    setupTextureType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false,
-                     /*IsArray=*/true, ResourceDimension::Dim2D)
-        .completeDefinition();
-  });
-
-  // RWTexture2DArray — same as RWTexture2D but IsArray=true
-  Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWTexture2DArray")
-             .addSimpleTemplateParams({"element_type"}, {Float4Ty},
-                                      TypedBufferConcept)
-             .finalizeForwardDeclaration();
-
-  onCompletion(Decl, [this](CXXRecordDecl *Decl) {
-    setupRWTextureType(Decl, *SemaPtr, /*IsArray=*/true,
-                       ResourceDimension::Dim2D)
-        .completeDefinition();
-  });
-
-  auto *PartialSpecRW2DA = addVectorTexturePartialSpecialization(
-      *SemaPtr, HLSLNamespace, Decl->getDescribedClassTemplate());
-  onCompletion(PartialSpecRW2DA, [this](CXXRecordDecl *Decl) {
-    setupRWTextureType(Decl, *SemaPtr, /*IsArray=*/true,
-                       ResourceDimension::Dim2D)
-        .completeDefinition();
-  });
-
-  // Texture2DMS — like Texture2D but multisampled, and the element type has no
-  // default argument.
-  Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Texture2DMS")
-             .addMSTextureTemplateParams("element_type", "sample_count",
-                                         TypedBufferConcept)
-             .finalizeForwardDeclaration();
-
-  onCompletion(Decl, [this](CXXRecordDecl *Decl) {
-    setupMSTextureType(Decl, *SemaPtr, /*IsArray=*/false,
-                       ResourceDimension::Dim2D)
-        .completeDefinition();
-  });
-
-  // TextureCube — SRV cube texture. Locations are float3 direction vectors.
-  // Cube textures do not support Load, operator[], mips or offsets.
-  Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "TextureCube")
-             .addSimpleTemplateParams({"element_type"}, {Float4Ty},
-                                      TypedBufferConcept)
-             .finalizeForwardDeclaration();
-
-  onCompletion(Decl, [this](CXXRecordDecl *Decl) {
-    setupTextureCubeType(Decl, *SemaPtr, /*IsArray=*/false)
-        .completeDefinition();
-  });
-
-  auto *PartialSpecCube = addVectorTexturePartialSpecialization(
-      *SemaPtr, HLSLNamespace, Decl->getDescribedClassTemplate());
-  onCompletion(PartialSpecCube, [this](CXXRecordDecl *Decl) {
-    setupTextureCubeType(Decl, *SemaPtr, /*IsArray=*/false)
-        .completeDefinition();
-  });
-
-  // TextureCubeArray — same as TextureCube but IsArray=true, so locations gain
-  // an array slice and are float4.
-  Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "TextureCubeArray")
-             .addSimpleTemplateParams({"element_type"}, {Float4Ty},
-                                      TypedBufferConcept)
-             .finalizeForwardDeclaration();
-
-  onCompletion(Decl, [this](CXXRecordDecl *Decl) {
-    setupTextureCubeType(Decl, *SemaPtr, /*IsArray=*/true).completeDefinition();
-  });
-
-  auto *PartialSpecCubeArray = addVectorTexturePartialSpecialization(
-      *SemaPtr, HLSLNamespace, Decl->getDescribedClassTemplate());
-  onCompletion(PartialSpecCubeArray, [this](CXXRecordDecl *Decl) {
-    setupTextureCubeType(Decl, *SemaPtr, /*IsArray=*/true).completeDefinition();
-  });
+  for (const TextureTypeInfo &T : TextureTypes) {
+    BuiltinTypeDeclBuilder TexBuilder(*SemaPtr, HLSLNamespace, T.Name);
+    switch (T.Shape) {
+    case TemplateShape::ElementType:
+      TexBuilder.addSimpleTemplateParams({"element_type"}, {Float4Ty},
+                                         TypedBufferConcept);
+      break;
+    case TemplateShape::ElementTypeAndSampleCount:
+      TexBuilder.addMSTextureTemplateParams("element_type", "sample_count",
+                                            TypedBufferConcept);
+      break;
+    }
+    Decl = TexBuilder.finalizeForwardDeclaration();
+
+    onCompletion(Decl, [this, &T](CXXRecordDecl *Decl) {
+      setupTextureType(Decl, *SemaPtr, T).completeDefinition();
+    });
+
+    if (T.Shape != TemplateShape::ElementType)
+      continue;
+
+    CXXRecordDecl *PartialSpec = addVectorTexturePartialSpecialization(
+        *SemaPtr, HLSLNamespace, Decl->getDescribedClassTemplate());
+    onCompletion(PartialSpec, [this, &T](CXXRecordDecl *Decl) {
+      setupTextureType(Decl, *SemaPtr, T).completeDefinition();
+    });
+  }
 }
 
 // Build a single overload of an HLSL atomic intrinsic in the hlsl namespace.



More information about the llvm-branch-commits mailing list