Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion include/dxc/dxcapi.internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ enum LEGAL_INTRINSIC_COMPTYPES {

#ifdef ENABLE_SPIRV_CODEGEN
LICOMPTYPE_VK_BUFFER_POINTER = 54,
LICOMPTYPE_COUNT = 55
LICOMPTYPE_VK_SAMPLED_TEXTURE2D = 55,
LICOMPTYPE_COUNT = 56
#else
LICOMPTYPE_COUNT = 54
#endif
Expand Down
5 changes: 5 additions & 0 deletions tools/clang/include/clang/AST/HlslTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,11 @@ clang::CXXRecordDecl *
DeclareVkBufferPointerType(clang::ASTContext &context,
clang::DeclContext *declContext);

clang::CXXRecordDecl *DeclareVkSampledTextureType(
clang::ASTContext &context, clang::DeclContext *declContext,
llvm::StringRef hlslTypeName, clang::QualType defaultParamType,
clang::QualType coordinateType, clang::QualType offsetType);

clang::CXXRecordDecl *DeclareInlineSpirvType(clang::ASTContext &context,
clang::DeclContext *declContext,
llvm::StringRef typeName,
Expand Down
3 changes: 3 additions & 0 deletions tools/clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -7610,6 +7610,9 @@ def err_hlsl_unsupported_typedbuffer_template_parameter : Error<
"elements of typed buffers and textures must be scalars or vectors">;
def err_hlsl_unsupported_typedbuffer_template_parameter_size : Error<
"elements of typed buffers and textures must fit in four 32-bit quantities">;
def err_hlsl_unsupported_vk_sampledtexture_template_parameter
: Error<"%0 cannot be used as a type parameter where it must be of type "
"float, int, uint or their vector equivalents">;
def err_hlsl_unsupported_payload_access_qualifier : Error<
"payload access qualifiers are only allowed for member variables of a payload structure">;
def err_hlsl_unsupported_payload_access_qualifier_struct : Error<
Expand Down
3 changes: 3 additions & 0 deletions tools/clang/include/clang/SPIRV/AstTypeProbe.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ bool isTexture(QualType);
/// Texture2DMSArray type.
bool isTextureMS(QualType);

/// \brief Returns true if the given type is an HLSL SampledTexture type.
bool isSampledTexture(QualType);

/// \brief Returns true if the given type is an HLSL RWTexture type.
bool isRWTexture(QualType);

Expand Down
4 changes: 4 additions & 0 deletions tools/clang/include/clang/SPIRV/SpirvBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@ class SpirvBuilder {
/// If compareVal is given a non-zero value, *Dref* variants of OpImageSample*
/// will be generated.
///
/// If the of `image` is a sampled image, then that image will be sampled.
/// In this case, `sampler` must be `nullptr`. If `image` is not a sampled
/// image, a sampled image will be create by combining `image` and `sampler`.
///
/// If lod or grad is given a non-zero value, *ExplicitLod variants of
/// OpImageSample* will be generated; otherwise, *ImplicitLod variant will
/// be generated.
Expand Down
89 changes: 89 additions & 0 deletions tools/clang/lib/AST/ASTContextHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1369,6 +1369,95 @@ CXXRecordDecl *hlsl::DeclareNodeOrRecordType(
}

#ifdef ENABLE_SPIRV_CODEGEN
static void AddSampleFunction(ASTContext &context, CXXRecordDecl *recordDecl,
QualType returnType, QualType coordinateType,
QualType offsetType) {
QualType floatType = context.FloatTy;
QualType uintType = context.UnsignedIntTy;

// Sample(location)
CXXMethodDecl *sampleDecl = CreateObjectFunctionDeclarationWithParams(
context, recordDecl, returnType, ArrayRef<QualType>(coordinateType),
ArrayRef<StringRef>(StringRef("location")),
context.DeclarationNames.getIdentifier(&context.Idents.get("Sample")),
/*isConst*/ true);
sampleDecl->addAttr(HLSLIntrinsicAttr::CreateImplicit(
context, "op", "", static_cast<int>(hlsl::IntrinsicOp::MOP_Sample)));
sampleDecl->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context));

// Sample(location, offset)
QualType params2[] = {coordinateType, offsetType};
StringRef names2[] = {"location", "offset"};
CXXMethodDecl *sampleDecl2 = CreateObjectFunctionDeclarationWithParams(
context, recordDecl, returnType, params2, names2,
context.DeclarationNames.getIdentifier(&context.Idents.get("Sample")),
/*isConst*/ true);
sampleDecl2->addAttr(HLSLIntrinsicAttr::CreateImplicit(
context, "op", "", static_cast<int>(hlsl::IntrinsicOp::MOP_Sample)));
sampleDecl2->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context));

// Sample(location, offset, clamp)
QualType params3[] = {coordinateType, offsetType, floatType};
StringRef names3[] = {"location", "offset", "clamp"};
CXXMethodDecl *sampleDecl3 = CreateObjectFunctionDeclarationWithParams(
context, recordDecl, returnType, params3, names3,
context.DeclarationNames.getIdentifier(&context.Idents.get("Sample")),
/*isConst*/ true);
sampleDecl3->addAttr(HLSLIntrinsicAttr::CreateImplicit(
context, "op", "", static_cast<int>(hlsl::IntrinsicOp::MOP_Sample)));
sampleDecl3->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context));

// Sample(location, offset, clamp, status)
QualType params4[] = {coordinateType, offsetType, floatType,
context.getLValueReferenceType(uintType)};
StringRef names4[] = {"location", "offset", "clamp", "status"};
CXXMethodDecl *sampleDecl4 = CreateObjectFunctionDeclarationWithParams(
context, recordDecl, returnType, params4, names4,
context.DeclarationNames.getIdentifier(&context.Idents.get("Sample")),
/*isConst*/ true);
sampleDecl4->addAttr(HLSLIntrinsicAttr::CreateImplicit(
context, "op", "", static_cast<int>(hlsl::IntrinsicOp::MOP_Sample)));
sampleDecl4->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context));
}

static void AddCalculateLevelOfDetailFunction(ASTContext &context,
CXXRecordDecl *recordDecl,
QualType coordinateType) {
QualType floatType = context.FloatTy;

CXXMethodDecl *lodDecl = CreateObjectFunctionDeclarationWithParams(
context, recordDecl, floatType, ArrayRef<QualType>(coordinateType),
ArrayRef<StringRef>(StringRef("location")),
context.DeclarationNames.getIdentifier(
&context.Idents.get("CalculateLevelOfDetail")),
/*isConst*/ true);
lodDecl->addAttr(HLSLIntrinsicAttr::CreateImplicit(
context, "op", "",
static_cast<int>(hlsl::IntrinsicOp::MOP_CalculateLevelOfDetail)));
lodDecl->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context));
}

CXXRecordDecl *hlsl::DeclareVkSampledTextureType(
ASTContext &context, DeclContext *declContext, llvm::StringRef hlslTypeName,
QualType defaultParamType, QualType coordinateType, QualType offsetType) {
BuiltinTypeDeclBuilder Builder(declContext, hlslTypeName,
TagDecl::TagKind::TTK_Struct);

TemplateTypeParmDecl *TyParamDecl =
Builder.addTypeTemplateParam("SampledTextureType", defaultParamType);

Builder.startDefinition();

QualType paramType = QualType(TyParamDecl->getTypeForDecl(), 0);
CXXRecordDecl *recordDecl = Builder.getRecordDecl();

AddSampleFunction(context, recordDecl, paramType, coordinateType, offsetType);
AddCalculateLevelOfDetailFunction(context, recordDecl, coordinateType);

Builder.completeDefinition();
return recordDecl;
}

CXXRecordDecl *hlsl::DeclareVkBufferPointerType(ASTContext &context,
DeclContext *declContext) {
BuiltinTypeDeclBuilder Builder(declContext, "BufferPointer",
Expand Down
11 changes: 11 additions & 0 deletions tools/clang/lib/SPIRV/AstTypeProbe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,17 @@ bool isTexture(QualType type) {
return false;
}

bool isSampledTexture(QualType type) {
if (const auto *rt = type->getAs<RecordType>()) {
const auto name = rt->getDecl()->getName();
// TODO(https://github.com/microsoft/DirectXShaderCompiler/issues/7979): Add
// other sampled texture types as needed.
if (name == "SampledTexture2D")
return true;
}
return false;
}

bool isTextureMS(QualType type) {
if (const auto *rt = type->getAs<RecordType>()) {
const auto name = rt->getDecl()->getName();
Expand Down
20 changes: 19 additions & 1 deletion tools/clang/lib/SPIRV/LowerTypeVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,23 @@ const SpirvType *LowerTypeVisitor::lowerVkTypeInVkNamespace(
assert(visitedTypeStack.size() == visitedTypeStackSize);
return pointerType;
}
if (name == "SampledTexture2D") {
const auto sampledType = hlsl::GetHLSLResourceResultType(type);
auto loweredType = lowerType(getElementType(astContext, sampledType), rule,
/*isRowMajor*/ llvm::None, srcLoc);

// Bool does not have a defined size in SPIR-V, so it cannot be
// used in the external interface.
if (loweredType == spvContext.getBoolType()) {
loweredType = spvContext.getUIntType(32);
}

const auto *imageType = spvContext.getImageType(
loweredType, spv::Dim::Dim2D, ImageType::WithDepth::No,
false /* array */, false /* ms */, ImageType::WithSampler::Yes,
spv::ImageFormat::Unknown);
return spvContext.getSampledImageType(imageType);
}
emitError("unknown type %0 in vk namespace", srcLoc) << type;
return nullptr;
}
Expand Down Expand Up @@ -892,7 +909,8 @@ LowerTypeVisitor::lowerResourceType(QualType type, SpirvLayoutRule rule,
auto loweredType =
lowerType(getElementType(astContext, sampledType), rule,
/*isRowMajor*/ llvm::None, srcLoc);
// Treat bool textures as uint for compatibility with OpTypeImage.
// Bool does not have a defined size in SPIR-V, so it cannot be
// used in the external interface.
if (loweredType == spvContext.getBoolType()) {
loweredType = spvContext.getUIntType(32);
}
Expand Down
11 changes: 9 additions & 2 deletions tools/clang/lib/SPIRV/SpirvBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -620,8 +620,15 @@ SpirvInstruction *SpirvBuilder::createImageSample(
assert(lod == nullptr || minLod == nullptr);

// An OpSampledImage is required to do the image sampling.
auto *sampledImage =
createSampledImage(imageType, image, sampler, loc, range);
// Skip creating OpSampledImage if the imageType is a sampled texture.
SpirvInstruction *sampledImage = nullptr;
if (isSampledTexture(imageType)) {
assert(!sampler &&
"sampler must be null when sampling from a sampled texture");
sampledImage = image;
} else {
sampledImage = createSampledImage(imageType, image, sampler, loc, range);
}

const auto mask = composeImageOperandsMask(
bias, lod, grad, constOffset, varOffset, constOffsets, sample, minLod);
Expand Down
77 changes: 56 additions & 21 deletions tools/clang/lib/SPIRV/SpirvEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4451,15 +4451,27 @@ SpirvEmitter::processTextureLevelOfDetail(const CXXMemberCallExpr *expr,
// Texture2D(Array).CalculateLevelOfDetail(SamplerState S, float2 xy);
// TextureCube(Array).CalculateLevelOfDetail(SamplerState S, float3 xyz);
// Texture3D.CalculateLevelOfDetail(SamplerState S, float3 xyz);
// SampledTexture2D.CalculateLevelOfDetail(float2 xy);
// Return type is always a single float (LOD).
assert(expr->getNumArgs() == 2u);
const auto *object = expr->getImplicitObjectArgument();
auto *objectInfo = loadIfGLValue(object);
auto *samplerState = doExpr(expr->getArg(0));
auto *coordinate = doExpr(expr->getArg(1));

auto *sampledImage = spvBuilder.createSampledImage(
object->getType(), objectInfo, samplerState, expr->getExprLoc());
const auto *imageExpr = expr->getImplicitObjectArgument();
const QualType imageType = imageExpr->getType();
// numarg is 1 if isSampledTexture(imageType). otherwise 2.
assert(expr->getNumArgs() == (isSampledTexture(imageType) ? 1u : 2u));

auto *objectInfo = loadIfGLValue(imageExpr);

SpirvInstruction *samplerState, *coordinate, *sampledImage;
if (isSampledTexture) {
samplerState = nullptr;
coordinate = doExpr(expr->getArg(0));
sampledImage = objectInfo;
} else {
samplerState = doExpr(expr->getArg(0));
coordinate = doExpr(expr->getArg(1));
sampledImage = spvBuilder.createSampledImage(
imageExpr->getType(), objectInfo, samplerState, expr->getExprLoc());
}

// The result type of OpImageQueryLod must be a float2.
const QualType queryResultType =
Expand Down Expand Up @@ -5812,36 +5824,59 @@ SpirvEmitter::processTextureSampleGather(const CXXMemberCallExpr *expr,
// float2|3|4 Location
// [, uint Status]);
//
// For SampledTexture2D:
// DXGI_FORMAT Object.Sample(float Location
// [, int Offset]
// [, float Clamp]
// [, out uint Status]);
//
// Other Texture types do not have a Gather method.

const auto numArgs = expr->getNumArgs();
const auto loc = expr->getExprLoc();
const auto range = expr->getSourceRange();
const bool hasStatusArg =
expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();

const auto *imageExpr = expr->getImplicitObjectArgument();
const QualType imageType = imageExpr->getType();
const bool isImageSampledTexture = isSampledTexture(imageType);

int samplerIndex, clampIndex, coordIndex, offsetIndex;
if (isImageSampledTexture) {
samplerIndex = -1; // non-existant
coordIndex = 0;
offsetIndex = 1;
clampIndex = 2;
} else {
samplerIndex = 0;
coordIndex = 1;
offsetIndex = 2;
clampIndex = 3;
}

SpirvInstruction *clamp = nullptr;
if (numArgs > 2 && expr->getArg(2)->getType()->isFloatingType())
clamp = doExpr(expr->getArg(2));
else if (numArgs > 3 && expr->getArg(3)->getType()->isFloatingType())
clamp = doExpr(expr->getArg(3));
if (numArgs > offsetIndex &&
expr->getArg(offsetIndex)->getType()->isFloatingType())
clamp = doExpr(expr->getArg(offsetIndex));
else if (numArgs > offsetIndex + 1 &&
expr->getArg(offsetIndex + 1)->getType()->isFloatingType())
clamp = doExpr(expr->getArg(offsetIndex + 1));
const bool hasClampArg = (clamp != 0);
const auto status =
hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : nullptr;

// Subtract 1 for status (if it exists), subtract 1 for clamp (if it exists),
// and subtract 2 for sampler_state and location.
const bool hasOffsetArg = numArgs - hasStatusArg - hasClampArg - 2 > 0;

const auto *imageExpr = expr->getImplicitObjectArgument();
const QualType imageType = imageExpr->getType();
auto *image = loadIfGLValue(imageExpr);
auto *sampler = doExpr(expr->getArg(0));
auto *coordinate = doExpr(expr->getArg(1));
SpirvInstruction *sampler =
samplerIndex >= 0 ? doExpr(expr->getArg(samplerIndex)) : nullptr;
auto *coordinate = doExpr(expr->getArg(coordIndex));
// .Sample()/.Gather() may have a third optional paramter for offset.
SpirvInstruction *constOffset = nullptr, *varOffset = nullptr;
// Subtract 1 for status (if it exists), subtract 1 for clamp (if it exists),
// and subtract offsetIndex for sampler_state (if exists) location.
const bool hasOffsetArg =
numArgs - hasStatusArg - hasClampArg - offsetIndex > 0;
if (hasOffsetArg)
handleOffsetInMethodCall(expr, 2, &constOffset, &varOffset);
handleOffsetInMethodCall(expr, offsetIndex, &constOffset, &varOffset);

const auto retType = expr->getDirectCallee()->getReturnType();
if (isSample) {
Expand Down
Loading