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 *DeclareVkSampledTexture2DType(
clang::ASTContext &context, clang::DeclContext *declContext,
clang::QualType float2Type, clang::QualType int2Type,
clang::QualType float4Type);

clang::CXXRecordDecl *DeclareInlineSpirvType(clang::ASTContext &context,
clang::DeclContext *declContext,
llvm::StringRef typeName,
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
3 changes: 3 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,9 @@ class SpirvBuilder {
/// If compareVal is given a non-zero value, *Dref* variants of OpImageSample*
/// will be generated.
///
/// If imageType is not a sampled image type, the OpSampledImage* instructions
/// will be generated.
///
Comment on lines +289 to +291
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is confusing. Can you rephrase?

Suggested change
/// If imageType is not a sampled image type, the OpSampledImage* instructions
/// 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`.
///

You'll need to fix the formatting.

/// 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
80 changes: 80 additions & 0 deletions tools/clang/lib/AST/ASTContextHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1369,6 +1369,86 @@ CXXRecordDecl *hlsl::DeclareNodeOrRecordType(
}

#ifdef ENABLE_SPIRV_CODEGEN
CXXRecordDecl *hlsl::DeclareVkSampledTexture2DType(ASTContext &context,
DeclContext *declContext,
QualType float2Type,
QualType int2Type,
QualType float4Type) {
Comment on lines +1374 to +1376
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you planning on make this function more general? For example could float2Type become CoordinateType? Then for Textur3D it is just a float?

If not, why pass these in as parameters? Could we simplify the interface an build them in this function?

BuiltinTypeDeclBuilder Builder(declContext, "SampledTexture2D",
TagDecl::TagKind::TTK_Struct);

QualType defaultTextureType = float4Type;
TemplateTypeParmDecl *TyParamDecl =
Builder.addTypeTemplateParam("SampledTextureType", defaultTextureType);

Builder.startDefinition();

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

QualType floatType = context.FloatTy;
QualType uintType = context.UnsignedIntTy;
// Add Sample method
// Sample(location)
CXXMethodDecl *sampleDecl = CreateObjectFunctionDeclarationWithParams(
context, recordDecl, paramType, ArrayRef<QualType>(float2Type),
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[] = {float2Type, int2Type};
StringRef names2[] = {"location", "offset"};
CXXMethodDecl *sampleDecl2 = CreateObjectFunctionDeclarationWithParams(
context, recordDecl, paramType, 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[] = {float2Type, int2Type, floatType};
StringRef names3[] = {"location", "offset", "clamp"};
CXXMethodDecl *sampleDecl3 = CreateObjectFunctionDeclarationWithParams(
context, recordDecl, paramType, 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[] = {float2Type, int2Type, floatType,
context.getLValueReferenceType(uintType)};
StringRef names4[] = {"location", "offset", "clamp", "status"};
CXXMethodDecl *sampleDecl4 = CreateObjectFunctionDeclarationWithParams(
context, recordDecl, paramType, params4, names4,
Comment on lines +1402 to +1429
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function will be come very big. Could you break it up into smaller functions? This block could be its own. Call it addSampeFunction. You can group other functions in the same way.

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));

// CalculateLevelOfDetail(location)
CXXMethodDecl *lodDecl = CreateObjectFunctionDeclarationWithParams(
context, recordDecl, floatType, ArrayRef<QualType>(float2Type),
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));

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
16 changes: 16 additions & 0 deletions tools/clang/lib/SPIRV/LowerTypeVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,22 @@ 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);

// Treat bool textures as uint for compatibility with OpTypeImage.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not about 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);
}

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
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
75 changes: 60 additions & 15 deletions tools/clang/lib/SPIRV/SpirvEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4451,15 +4451,25 @@ 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);
auto *samplerState =
isSampledTexture(imageType) ? nullptr : doExpr(expr->getArg(0));
auto *coordinate = isSampledTexture(imageType) ? doExpr(expr->getArg(0))
: doExpr(expr->getArg(1));

auto *sampledImage =
isSampledTexture(imageType)
Comment on lines +4462 to +4469
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are checking isSampledTextured a lot in this code. You should just use a single if-statement.

? objectInfo
: 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,11 +5822,54 @@ 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 auto *imageExpr = expr->getImplicitObjectArgument();
const QualType imageType = imageExpr->getType();

if (isSampledTexture(imageType)) {
auto *sampledImage = loadIfGLValue(imageExpr);
auto *coordinate = doExpr(expr->getArg(0));
SpirvInstruction *constOffset = nullptr;
SpirvInstruction *varOffset = nullptr;
SpirvInstruction *clamp = nullptr;
SpirvInstruction *status = nullptr;

if (numArgs > 1) {
handleOffsetInMethodCall(expr, 1, &constOffset, &varOffset);
}
if (numArgs > 2) {
clamp = doExpr(expr->getArg(2));
}
if (numArgs > 3) {
status = doExpr(expr->getArg(3));
}
Comment on lines +5848 to +5856
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You eventually going to have to add all of the checks below when you add all of the appropriate overload. We should try to avoid adding duplicating the argument parsing here.

You could try doing:

uint offset = 0;
auto *image = loadIfGLValue(imageExpr);
auto *sampler = nullptr;

if (!isSampledTexture(imageType)) {
  sampler = doExpr(expr->getArg(0));
  offset = 1;
}

Then all of the later accesses become to expr->getArg(offset + ); For example:

auto *coordinate = doExpr(expr->getArg(offset));

This will avoid repeating the logic.


const auto retType = expr->getDirectCallee()->getReturnType();
return createImageSample(
retType, imageType, sampledImage, /*sampler*/ nullptr, coordinate,
/*compareVal*/ nullptr, /*bias*/ nullptr,
/*lod*/ nullptr, {nullptr, nullptr}, constOffset, varOffset,
/*constOffsets*/ nullptr, /*sample*/ nullptr,
/*minLod*/ clamp, status, loc, range);
}

auto *image = loadIfGLValue(imageExpr);
auto *sampler = doExpr(expr->getArg(0));
auto *coordinate = doExpr(expr->getArg(1));
// .Sample()/.Gather() may have a third optional paramter for offset.
SpirvInstruction *constOffset = nullptr, *varOffset = nullptr;

const bool hasStatusArg =
expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();

Expand All @@ -5832,14 +5885,6 @@ SpirvEmitter::processTextureSampleGather(const CXXMemberCallExpr *expr,
// 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));
// .Sample()/.Gather() may have a third optional paramter for offset.
SpirvInstruction *constOffset = nullptr, *varOffset = nullptr;
if (hasOffsetArg)
handleOffsetInMethodCall(expr, 2, &constOffset, &varOffset);

Expand Down
Loading