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
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 @@ -339,6 +339,10 @@ class SpirvBuilder {

/// \brief Creates SPIR-V instructions for gathering the given image.
///
/// If the of `image` is a sampled image, then that image will be gathered.
/// In this case, `sampler` must be `nullptr`. If `image` is not a sampled
/// image, a sampled image will be created by combining `image` and `sampler`.
///
/// If compareVal is given a non-null value, OpImageDrefGather or
/// OpImageSparseDrefGather will be generated; otherwise, OpImageGather or
/// OpImageSparseGather will be generated.
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 @@ -714,8 +714,15 @@ SpirvInstruction *SpirvBuilder::createImageGather(
assert(insertPoint && "null insert point");

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

// TODO: Update ImageGather to accept minLod if necessary.
const auto mask = composeImageOperandsMask(
Expand Down
22 changes: 19 additions & 3 deletions tools/clang/lib/SPIRV/SpirvEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4328,7 +4328,18 @@ SpirvEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {
const Expr *mipLevel = nullptr, *numLevels = nullptr, *numSamples = nullptr;

assert(isTexture(type) || isRWTexture(type) || isBuffer(type) ||
isRWBuffer(type));
isRWBuffer(type) || isSampledTexture(type));
if (isSampledTexture(type)) {
LowerTypeVisitor lowerTypeVisitor(astContext, spvContext, spirvOptions,
spvBuilder);
const SpirvType *spvType = lowerTypeVisitor.lowerType(
type, SpirvLayoutRule::Void, llvm::None, expr->getExprLoc());
// Get image type based on type, assuming type is a sampledimage type
const auto *sampledType = cast<SampledImageType>(spvType);
const SpirvType *imgType = sampledType->getImageType();
objectInstr = spvBuilder.createUnaryOp(spv::Op::OpImage, imgType,
objectInstr, expr->getExprLoc());
}

// For Texture1D, arguments are either:
// a) width
Expand Down Expand Up @@ -4362,6 +4373,9 @@ SpirvEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {
// a) width, height, elements
// b) MipLevel, width, height, elements, NumLevels

// SampledTexture types follow the same rules above, as
// this method doesn't require a Sampler argument.

// Note: SPIR-V Spec requires return type of OpImageQuerySize(Lod) to be a
// scalar/vector of integers. SPIR-V Spec also requires return type of
// OpImageQueryLevels and OpImageQuerySamples to be scalar integers.
Expand All @@ -4379,6 +4393,7 @@ SpirvEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {

if ((typeName == "Texture1D" && numArgs > 1) ||
(typeName == "Texture2D" && numArgs > 2) ||
(typeName == "SampledTexture2D" && numArgs > 2) ||
(typeName == "TextureCube" && numArgs > 2) ||
(typeName == "Texture3D" && numArgs > 3) ||
(typeName == "Texture1DArray" && numArgs > 2) ||
Expand Down Expand Up @@ -4417,7 +4432,7 @@ SpirvEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {
// Only Texture types use ImageQuerySizeLod.
// TextureMS, RWTexture, Buffers, RWBuffers use ImageQuerySize.
SpirvInstruction *lod = nullptr;
if (isTexture(type) && !numSamples) {
if ((isTexture(type) || isSampledTexture(type)) && !numSamples) {
if (mipLevel) {
// For Texture types when mipLevel argument is present.
lod = doExpr(mipLevel, range);
Expand Down Expand Up @@ -6433,7 +6448,8 @@ SpirvInstruction *
SpirvEmitter::processGetDimensions(const CXXMemberCallExpr *expr) {
const auto objectType = expr->getImplicitObjectArgument()->getType();
if (isTexture(objectType) || isRWTexture(objectType) ||
isBuffer(objectType) || isRWBuffer(objectType)) {
isBuffer(objectType) || isRWBuffer(objectType) ||
isSampledTexture(objectType)) {
return processBufferTextureGetDimensions(expr);
} else if (isByteAddressBuffer(objectType) ||
isRWByteAddressBuffer(objectType) ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// RUN: %dxc -T ps_6_8 -E main -fcgl %s -spirv | FileCheck %s

// CHECK: OpCapability ImageQuery

vk::SampledTexture2D<float4> t1 : register(t0);

// CHECK: %type_2d_image = OpTypeImage %float 2D 0 0 0 1 Unknown
// CHECK: %type_sampled_image = OpTypeSampledImage %type_2d_image
// CHECK: [[ptr:%[a-zA-Z0-9_]+]] = OpTypePointer UniformConstant %type_sampled_image

// CHECK: %t1 = OpVariable [[ptr]] UniformConstant

void main() {
float2 xy = float2(0.5, 0.5);

//CHECK: [[tex1:%[a-zA-Z0-9_]+]] = OpLoad %type_sampled_image %t1
//CHECK-NEXT: [[xy_load:%[a-zA-Z0-9_]+]] = OpLoad %v2float %xy
//CHECK-NEXT: [[query:%[a-zA-Z0-9_]+]] = OpImageQueryLod %v2float [[tex1]] [[xy_load]]
//CHECK-NEXT: {{%[0-9]+}} = OpCompositeExtract %float [[query]] 1
float lod1 = t1.CalculateLevelOfDetailUnclamped(xy);
}
42 changes: 42 additions & 0 deletions tools/clang/test/CodeGenSPIRV/vk.sampledtexture.gather.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// RUN: %dxc -T ps_6_7 -E main -fcgl %s -spirv | FileCheck %s

// CHECK: OpCapability SparseResidency

// CHECK: [[v2fc:%[0-9]+]] = OpConstantComposite %v2float %float_0_5 %float_0_25
// CHECK: [[v2ic:%[0-9]+]] = OpConstantComposite %v2int %int_2 %int_3

// CHECK: [[type_2d_image_1:%[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 0 0 0 1 Unknown
// CHECK: [[type_sampled_image_1:%[a-zA-Z0-9_]+]] = OpTypeSampledImage [[type_2d_image_1]]
// CHECK: [[ptr_type_1:%[a-zA-Z0-9_]+]] = OpTypePointer UniformConstant [[type_sampled_image_1]]

// CHECK: [[type_2d_image_2:%[a-zA-Z0-9_]+]] = OpTypeImage %uint 2D 0 0 0 1 Unknown
// CHECK: [[type_sampled_image_2:%[a-zA-Z0-9_]+]] = OpTypeSampledImage [[type_2d_image_2]]
// CHECK: [[ptr_type_2:%[a-zA-Z0-9_]+]] = OpTypePointer UniformConstant [[type_sampled_image_2]]

// CHECK: %SparseResidencyStruct = OpTypeStruct %uint %v4float

// CHECK: [[tex1:%[a-zA-Z0-9_]+]] = OpVariable [[ptr_type_1]] UniformConstant
// CHECK: [[tex2:%[a-zA-Z0-9_]+]] = OpVariable [[ptr_type_2]] UniformConstant

vk::SampledTexture2D<float4> tex1 : register(t1);
vk::SampledTexture2D<uint> tex2 : register(t2);

float4 main() : SV_Target {

// CHECK: [[tex1_load:%[a-zA-Z0-9_]+]] = OpLoad [[type_sampled_image_1]] [[tex1]]
// CHECK: [[val1:%[a-zA-Z0-9_]+]] = OpImageGather %v4float [[tex1_load]] [[v2fc]] %int_0 None
float4 val1 = tex1.Gather(float2(0.5, 0.25));

// CHECK: [[tex2_load:%[a-zA-Z0-9_]+]] = OpLoad [[type_sampled_image_2]] [[tex2]]
// CHECK: [[val2:%[a-zA-Z0-9_]+]] = OpImageGather %v4uint [[tex2_load]] [[v2fc]] %int_0 ConstOffset [[v2ic]]
uint4 val2 = tex2.Gather(float2(0.5, 0.25), int2(2, 3));

// CHECK: [[tex3_load:%[a-zA-Z0-9_]+]] = OpLoad [[type_sampled_image_1]] [[tex1]]
// CHECK: [[val3:%[a-zA-Z0-9_]+]] = OpImageSparseGather %SparseResidencyStruct [[tex3_load]] [[v2fc]] %int_0 ConstOffset [[v2ic]]
// CHECK: [[status_0:%[a-zA-Z0-9_]+]] = OpCompositeExtract %uint [[val3]] 0
// CHECK: OpStore %status [[status_0]]
uint status;
float4 val3 = tex1.Gather(float2(0.5, 0.25), int2(2, 3), status);

return 1.0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// RUN: %dxc -T ps_6_0 -E main -fcgl %s -spirv | FileCheck %s
// RUN: not %dxc -T ps_6_0 -E main -fcgl %s -spirv -DERROR 2>&1 | FileCheck %s --check-prefix=ERROR

// CHECK: OpCapability ImageQuery

vk::SampledTexture2D<float4> t1;

void main() {
uint mipLevel = 1;
uint width, height, numLevels;

// CHECK: [[t1_load:%[0-9]+]] = OpLoad %type_sampled_image %t1
// CHECK-NEXT: [[image1:%[0-9]+]] = OpImage %type_2d_image [[t1_load]]
// CHECK-NEXT: [[query1:%[0-9]+]] = OpImageQuerySizeLod %v2uint [[image1]] %int_0
// CHECK-NEXT: [[query1_0:%[0-9]+]] = OpCompositeExtract %uint [[query1]] 0
// CHECK-NEXT: OpStore %width [[query1_0]]
// CHECK-NEXT: [[query1_1:%[0-9]+]] = OpCompositeExtract %uint [[query1]] 1
// CHECK-NEXT: OpStore %height [[query1_1]]
t1.GetDimensions(width, height);

// CHECK: [[t1_load:%[0-9]+]] = OpLoad %type_sampled_image %t1
// CHECK-NEXT: [[image2:%[0-9]+]] = OpImage %type_2d_image [[t1_load]]
// CHECK-NEXT: [[mip:%[0-9]+]] = OpLoad %uint %mipLevel
// CHECK-NEXT: [[query2:%[0-9]+]] = OpImageQuerySizeLod %v2uint [[image2]] [[mip]]
// CHECK-NEXT: [[query2_0:%[0-9]+]] = OpCompositeExtract %uint [[query2]] 0
// CHECK-NEXT: OpStore %width [[query2_0]]
// CHECK-NEXT: [[query2_1:%[0-9]+]] = OpCompositeExtract %uint [[query2]] 1
// CHECK-NEXT: OpStore %height [[query2_1]]
// CHECK-NEXT: [[query_level_2:%[0-9]+]] = OpImageQueryLevels %uint [[image2]]
// CHECK-NEXT: OpStore %numLevels [[query_level_2]]
t1.GetDimensions(mipLevel, width, height, numLevels);

float f_width, f_height, f_numLevels;
// CHECK: [[t1_load:%[0-9]+]] = OpLoad %type_sampled_image %t1
// CHECK-NEXT: [[image1:%[0-9]+]] = OpImage %type_2d_image [[t1_load]]
// CHECK-NEXT: [[query1:%[0-9]+]] = OpImageQuerySizeLod %v2uint [[image1]] %int_0
// CHECK-NEXT: [[query1_0:%[0-9]+]] = OpCompositeExtract %uint [[query1]] 0
// CHECK-NEXT: [[f_query1_0:%[0-9]+]] = OpConvertUToF %float [[query1_0]]
// CHECK-NEXT: OpStore %f_width [[f_query1_0]]
// CHECK-NEXT: [[query1_1:%[0-9]+]] = OpCompositeExtract %uint [[query1]] 1
// CHECK-NEXT: [[f_query1_1:%[0-9]+]] = OpConvertUToF %float [[query1_1]]
// CHECK-NEXT: OpStore %f_height [[f_query1_1]]
t1.GetDimensions(f_width, f_height);

// CHECK: [[t1_load:%[0-9]+]] = OpLoad %type_sampled_image %t1
// CHECK-NEXT: [[image2:%[0-9]+]] = OpImage %type_2d_image [[t1_load]]
// CHECK-NEXT: [[mip:%[0-9]+]] = OpLoad %uint %mipLevel
// CHECK-NEXT: [[query2:%[0-9]+]] = OpImageQuerySizeLod %v2uint [[image2]] [[mip]]
// CHECK-NEXT: [[query2_0:%[0-9]+]] = OpCompositeExtract %uint [[query2]] 0
// CHECK-NEXT: [[f_query2_0:%[0-9]+]] = OpConvertUToF %float [[query2_0]]
// CHECK-NEXT: OpStore %f_width [[f_query2_0]]
// CHECK-NEXT: [[query2_1:%[0-9]+]] = OpCompositeExtract %uint [[query2]] 1
// CHECK-NEXT: [[f_query2_1:%[0-9]+]] = OpConvertUToF %float [[query2_1]]
// CHECK-NEXT: OpStore %f_height [[f_query2_1]]
// CHECK-NEXT: [[query_level_2:%[0-9]+]] = OpImageQueryLevels %uint [[image2]]
// CHECK-NEXT: [[f_query_level_2:%[0-9]+]] = OpConvertUToF %float [[query_level_2]]
// CHECK-NEXT: OpStore %f_numLevels [[f_query_level_2]]
t1.GetDimensions(mipLevel, f_width, f_height, f_numLevels);

int i_width, i_height, i_numLevels;
// CHECK: [[t1_load:%[0-9]+]] = OpLoad %type_sampled_image %t1
// CHECK-NEXT: [[image1:%[0-9]+]] = OpImage %type_2d_image [[t1_load]]
// CHECK-NEXT: [[query1:%[0-9]+]] = OpImageQuerySizeLod %v2uint [[image1]] %int_0
// CHECK-NEXT: [[query1_0:%[0-9]+]] = OpCompositeExtract %uint [[query1]] 0
// CHECK-NEXT: [[query_0_int:%[0-9]+]] = OpBitcast %int [[query1_0]]
// CHECK-NEXT: OpStore %i_width [[query_0_int]]
// CHECK-NEXT: [[query1_1:%[0-9]+]] = OpCompositeExtract %uint [[query1]] 1
// CHECK-NEXT: [[query_1_int:%[0-9]+]] = OpBitcast %int [[query1_1]]
// CHECK-NEXT: OpStore %i_height [[query_1_int]]
t1.GetDimensions(i_width, i_height);

// CHECK: [[t1_load:%[0-9]+]] = OpLoad %type_sampled_image %t1
// CHECK-NEXT: [[image2:%[0-9]+]] = OpImage %type_2d_image [[t1_load]]
// CHECK-NEXT: [[mip:%[0-9]+]] = OpLoad %uint %mipLevel
// CHECK-NEXT: [[query2:%[0-9]+]] = OpImageQuerySizeLod %v2uint [[image2]] [[mip]]
// CHECK-NEXT: [[query2_0:%[0-9]+]] = OpCompositeExtract %uint [[query2]] 0
// CHECK-NEXT: [[query_0_int:%[0-9]+]] = OpBitcast %int [[query2_0]]
// CHECK-NEXT: OpStore %i_width [[query_0_int]]
// CHECK-NEXT: [[query2_1:%[0-9]+]] = OpCompositeExtract %uint [[query2]] 1
// CHECK-NEXT: [[query_1_int:%[0-9]+]] = OpBitcast %int [[query2_1]]
// CHECK-NEXT: OpStore %i_height [[query_1_int]]
// CHECK-NEXT: [[query_level_2:%[0-9]+]] = OpImageQueryLevels %uint [[image2]]
// CHECK-NEXT: [[query_level_2_int:%[0-9]+]] = OpBitcast %int [[query_level_2]]
// CHECK-NEXT: OpStore %i_numLevels [[query_level_2_int]]
t1.GetDimensions(mipLevel, i_width, i_height, i_numLevels);

#ifdef ERROR
// ERROR: error: Output argument must be an l-value
t1.GetDimensions(mipLevel, 0, height, numLevels);

// ERROR: error: Output argument must be an l-value
t1.GetDimensions(width, 20);
#endif
}
8 changes: 8 additions & 0 deletions utils/hct/gen_intrin_main.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1239,4 +1239,12 @@ namespace VkSampledTexture2DMethods {
$classT [[ro]] Sample(in float<2> x, in int<2> o, in float clamp) : tex2d_t_o_cl;
$classT [[]] Sample(in float<2> x, in int<2> o, in float clamp, out uint_only status) : tex2d_t_o_cl_s;
float [[ro]] CalculateLevelOfDetail(in float<2> x) : tex2d_t_calc_lod;
float [[ro]] CalculateLevelOfDetailUnclamped(in float<2> x) : tex2d_t_calc_lod_unclamped;
$match<0, -1> void<4> [[ro]] Gather(in float<2> x) : tex2d_t_gather;
$match<0, -1> void<4> [[ro]] Gather(in float<2> x, in int<2> o) : tex2d_t_gather_o;
$match<0, -1> void<4> [[]] Gather(in float<2> x, in int<2> o, out uint_only status) : tex2d_t_gather_o_s;
void [[]] GetDimensions(in uint x, out uint_only width, out $type2 height, out $type2 levels) : resinfo_uint;
void [[]] GetDimensions(in uint x, out float_like width, out $type2 height, out $type2 levels) : resinfo;
void [[]] GetDimensions(out uint_only width, out $type1 height) : resinfo_uint_o;
void [[]] GetDimensions(out float_like width, out $type1 height) : resinfo_o;
} namespace