spirv-reader: support textureSampleCompareLevel

- Translates from OpImageSampleDrefExplicitLod, but the Lod must
  be a constant 0, or the reader issues an error.
  The requirement for Lod 0 is a constraint from Metal, inherited
  by WGSL.

Fixed: tint:425, tint:482
Change-Id: I8fdf10dbd9c5ac3e24398519a28202c84677c38d
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/51803
Commit-Queue: David Neto <dneto@google.com>
Auto-Submit: David Neto <dneto@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
David Neto 2021-05-21 19:17:33 +00:00 committed by Tint LUCI CQ
parent a53a3b8c19
commit 9d555d1621
3 changed files with 345 additions and 28 deletions

View File

@ -3262,16 +3262,10 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
// Handle exceptional cases // Handle exceptional cases
switch (GetSkipReason(ptr_id)) { switch (GetSkipReason(ptr_id)) {
case SkipReason::kPointSizeBuiltinPointer: case SkipReason::kPointSizeBuiltinPointer:
if (const auto* c = constant_mgr_->FindDeclaredConstant(value_id)) { if (IsFloatOne(value_id)) {
// If we're writing a constant 1.0, then skip the write. That's all
// that WebGPU handles.
auto* ct = c->type();
if (ct->AsFloat() && (ct->AsFloat()->width() == 32) &&
(c->GetFloat() == 1.0f)) {
// Don't store to PointSize // Don't store to PointSize
return true; return true;
} }
}
return Fail() << "cannot store a value other than constant 1.0 to " return Fail() << "cannot store a value other than constant 1.0 to "
"PointSize builtin: " "PointSize builtin: "
<< inst.PrettyPrint(); << inst.PrettyPrint();
@ -4843,6 +4837,17 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
if (use_level_of_detail_suffix) { if (use_level_of_detail_suffix) {
builtin_name += "Level"; builtin_name += "Level";
} }
if (is_dref_sample) {
// Metal only supports Lod = 0 for comparison sampling without
// derivatives.
if (!IsFloatZero(inst.GetSingleWordInOperand(arg_index))) {
return Fail() << "WGSL comparison sampling without derivatives "
"requires level-of-detail 0.0"
<< inst.PrettyPrint();
}
// Don't generate the Lod argument.
} else {
// Generate the Lod argument.
TypedExpression lod = MakeOperand(inst, arg_index); TypedExpression lod = MakeOperand(inst, arg_index);
// When sampling from a depth texture, the Lod operand must be an I32. // When sampling from a depth texture, the Lod operand must be an I32.
if (texture_type->Is<DepthTexture>()) { if (texture_type->Is<DepthTexture>()) {
@ -4850,6 +4855,8 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
lod = ToI32(lod); lod = ToI32(lod);
} }
params.push_back(lod.expr); params.push_back(lod.expr);
}
image_operands_mask ^= SpvImageOperandsLodMask; image_operands_mask ^= SpvImageOperandsLodMask;
arg_index++; arg_index++;
} else if ((opcode == SpvOpImageFetch) && } else if ((opcode == SpvOpImageFetch) &&
@ -5466,6 +5473,28 @@ TypedExpression FunctionEmitter::Dereference(TypedExpression expr) {
}; };
} }
bool FunctionEmitter::IsFloatZero(uint32_t value_id) {
if (const auto* c = constant_mgr_->FindDeclaredConstant(value_id)) {
if (const auto* float_const = c->AsFloatConstant()) {
return 0.0f == float_const->GetFloatValue();
}
if (c->AsNullConstant()) {
// Valid SPIR-V requires it to be a float value anyway.
return true;
}
}
return false;
}
bool FunctionEmitter::IsFloatOne(uint32_t value_id) {
if (const auto* c = constant_mgr_->FindDeclaredConstant(value_id)) {
if (const auto* float_const = c->AsFloatConstant()) {
return 1.0f == float_const->GetFloatValue();
}
}
return false;
}
FunctionEmitter::FunctionDeclaration::FunctionDeclaration() = default; FunctionEmitter::FunctionDeclaration::FunctionDeclaration() = default;
FunctionEmitter::FunctionDeclaration::~FunctionDeclaration() = default; FunctionEmitter::FunctionDeclaration::~FunctionDeclaration() = default;

View File

@ -851,6 +851,11 @@ class FunctionEmitter {
/// @returns the value itself, or converted to signed integral /// @returns the value itself, or converted to signed integral
TypedExpression ToSignedIfUnsigned(TypedExpression value); TypedExpression ToSignedIfUnsigned(TypedExpression value);
/// Returns true if the given SPIR-V id represents a constant float 0.
bool IsFloatZero(uint32_t value_id);
/// Returns true if the given SPIR-V id represents a constant float 1.
bool IsFloatOne(uint32_t value_id);
private: private:
/// FunctionDeclaration contains the parsed information for a function header. /// FunctionDeclaration contains the parsed information for a function header.
struct FunctionDeclaration { struct FunctionDeclaration {

View File

@ -90,6 +90,7 @@ std::string CommonBasicTypes() {
%v4float = OpTypeVector %float 4 %v4float = OpTypeVector %float 4
%float_null = OpConstantNull %float %float_null = OpConstantNull %float
%float_0 = OpConstant %float 0
%float_1 = OpConstant %float 1 %float_1 = OpConstant %float 1
%float_2 = OpConstant %float 2 %float_2 = OpConstant %float 2
%float_3 = OpConstant %float 3 %float_3 = OpConstant %float 3
@ -932,8 +933,6 @@ TEST_P(SpvParserHandleTest_RegisterHandleUsage_SampledImage, Variable) {
p->DeliberatelyInvalidSpirv(); p->DeliberatelyInvalidSpirv();
} }
if (inst.find("ImageSampleDrefExplicitLod") != std::string::npos) { if (inst.find("ImageSampleDrefExplicitLod") != std::string::npos) {
// WGSL does not support querying image level of detail.
// So don't emit them as part of a "passing" corpus.
p->SkipDumpingPending("crbug.com/tint/425"); // gpuweb issue #1319 p->SkipDumpingPending("crbug.com/tint/425"); // gpuweb issue #1319
} }
} }
@ -2259,6 +2258,253 @@ INSTANTIATE_TEST_SUITE_P(
) )
})"})); })"}));
INSTANTIATE_TEST_SUITE_P(
ImageSampleDrefExplicitLod,
SpvParserHandleTest_SampledImageAccessTest,
// Lod must be float constant 0 due to a Metal constraint.
// Another test checks cases where the Lod is not float constant 0.
::testing::Values(
// 2D
ImageAccessCase{"%float 2D 1 0 0 1 Unknown",
"%result = OpImageSampleDrefExplicitLod "
"%float %sampled_image %coords12 %depth Lod %float_0",
R"(
Variable{
Decorations{
GroupDecoration{0}
BindingDecoration{0}
}
x_10
none
__sampler_comparison
}
Variable{
Decorations{
GroupDecoration{2}
BindingDecoration{1}
}
x_20
none
__depth_texture_2d
})",
R"(
Call[not set]{
Identifier[not set]{textureSampleCompareLevel}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
Identifier[not set]{coords12}
ScalarConstructor[not set]{0.200000}
)
})"},
// 2D array
ImageAccessCase{"%float 2D 1 1 0 1 Unknown",
"%result = OpImageSampleDrefExplicitLod "
"%float %sampled_image %coords123 %depth Lod %float_0",
R"(
Variable{
Decorations{
GroupDecoration{0}
BindingDecoration{0}
}
x_10
none
__sampler_comparison
}
Variable{
Decorations{
GroupDecoration{2}
BindingDecoration{1}
}
x_20
none
__depth_texture_2d_array
})",
R"(
Call[not set]{
Identifier[not set]{textureSampleCompareLevel}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
MemberAccessor[not set]{
Identifier[not set]{coords123}
Identifier[not set]{xy}
}
TypeConstructor[not set]{
__i32
MemberAccessor[not set]{
Identifier[not set]{coords123}
Identifier[not set]{z}
}
}
ScalarConstructor[not set]{0.200000}
)
})"},
// 2D, ConstOffset
ImageAccessCase{"%float 2D 1 0 0 1 Unknown",
"%result = OpImageSampleDrefExplicitLod %float "
"%sampled_image %coords12 %depth Lod|ConstOffset "
"%float_0 %offsets2d",
R"(
Variable{
Decorations{
GroupDecoration{0}
BindingDecoration{0}
}
x_10
none
__sampler_comparison
}
Variable{
Decorations{
GroupDecoration{2}
BindingDecoration{1}
}
x_20
none
__depth_texture_2d
})",
R"(
Call[not set]{
Identifier[not set]{textureSampleCompareLevel}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
Identifier[not set]{coords12}
ScalarConstructor[not set]{0.200000}
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{3}
ScalarConstructor[not set]{4}
}
)
})"},
// 2D array, ConstOffset
ImageAccessCase{"%float 2D 1 1 0 1 Unknown",
"%result = OpImageSampleDrefExplicitLod %float "
"%sampled_image %coords123 %depth Lod|ConstOffset "
"%float_0 %offsets2d",
R"(
Variable{
Decorations{
GroupDecoration{0}
BindingDecoration{0}
}
x_10
none
__sampler_comparison
}
Variable{
Decorations{
GroupDecoration{2}
BindingDecoration{1}
}
x_20
none
__depth_texture_2d_array
})",
R"(
Call[not set]{
Identifier[not set]{textureSampleCompareLevel}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
MemberAccessor[not set]{
Identifier[not set]{coords123}
Identifier[not set]{xy}
}
TypeConstructor[not set]{
__i32
MemberAccessor[not set]{
Identifier[not set]{coords123}
Identifier[not set]{z}
}
}
ScalarConstructor[not set]{0.200000}
TypeConstructor[not set]{
__vec_2__i32
ScalarConstructor[not set]{3}
ScalarConstructor[not set]{4}
}
)
})"},
// Cube
ImageAccessCase{"%float Cube 1 0 0 1 Unknown",
"%result = OpImageSampleDrefExplicitLod "
"%float %sampled_image %coords123 %depth Lod %float_0",
R"(
Variable{
Decorations{
GroupDecoration{0}
BindingDecoration{0}
}
x_10
none
__sampler_comparison
}
Variable{
Decorations{
GroupDecoration{2}
BindingDecoration{1}
}
x_20
none
__depth_texture_cube
})",
R"(
Call[not set]{
Identifier[not set]{textureSampleCompareLevel}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
Identifier[not set]{coords123}
ScalarConstructor[not set]{0.200000}
)
})"},
// Cube array
ImageAccessCase{"%float Cube 1 1 0 1 Unknown",
"%result = OpImageSampleDrefExplicitLod "
"%float %sampled_image %coords1234 %depth Lod %float_0",
R"(
Variable{
Decorations{
GroupDecoration{0}
BindingDecoration{0}
}
x_10
none
__sampler_comparison
}
Variable{
Decorations{
GroupDecoration{2}
BindingDecoration{1}
}
x_20
none
__depth_texture_cube_array
})",
R"(
Call[not set]{
Identifier[not set]{textureSampleCompareLevel}
(
Identifier[not set]{x_20}
Identifier[not set]{x_10}
MemberAccessor[not set]{
Identifier[not set]{coords1234}
Identifier[not set]{xyz}
}
TypeConstructor[not set]{
__i32
MemberAccessor[not set]{
Identifier[not set]{coords1234}
Identifier[not set]{w}
}
}
ScalarConstructor[not set]{0.200000}
)
})"}));
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
ImageSampleExplicitLod_UsingLod, ImageSampleExplicitLod_UsingLod,
SpvParserHandleTest_SampledImageAccessTest, SpvParserHandleTest_SampledImageAccessTest,
@ -3156,7 +3402,7 @@ TEST_F(SpvParserHandleTest, ImageWrite_TooFewSrcTexelComponents_1_vs_4) {
EXPECT_FALSE(p->BuildAndParseInternalModule()); EXPECT_FALSE(p->BuildAndParseInternalModule());
EXPECT_THAT(p->error(), EXPECT_THAT(p->error(),
Eq("texel has too few components for storage texture: 1 provided " Eq("texel has too few components for storage texture: 1 provided "
"but 4 required, in: OpImageWrite %53 %3 %2")) "but 4 required, in: OpImageWrite %54 %3 %2"))
<< p->error(); << p->error();
} }
@ -5365,7 +5611,7 @@ INSTANTIATE_TEST_SUITE_P(
{"Identifier[not set]{vf12}\n"}}, {"Identifier[not set]{vf12}\n"}},
{"%float 2D 1 0 0 1 Unknown", {"%float 2D 1 0 0 1 Unknown",
"%result = OpImageSampleDrefExplicitLod %float %sampled_image %vf12 " "%result = OpImageSampleDrefExplicitLod %float %sampled_image %vf12 "
"%depth Lod %f1", "%depth Lod %float_0",
"", "",
{"Identifier[not set]{vf12}\n"}}, {"Identifier[not set]{vf12}\n"}},
})); }));
@ -5434,7 +5680,7 @@ INSTANTIATE_TEST_SUITE_P(
)"}}, )"}},
{"%float 2D 1 1 0 1 Unknown", {"%float 2D 1 1 0 1 Unknown",
"%result = OpImageSampleDrefExplicitLod %float %sampled_image " "%result = OpImageSampleDrefExplicitLod %float %sampled_image "
"%vf123 %depth Lod %f1", "%vf123 %depth Lod %float_0",
"", "",
{ {
R"(MemberAccessor[not set]{ R"(MemberAccessor[not set]{
@ -5668,7 +5914,7 @@ INSTANTIATE_TEST_SUITE_P(
{"%float 1D 0 0 0 1 Unknown", {"%float 1D 0 0 0 1 Unknown",
"%50 = OpCopyObject %float %float_1", "%50 = OpCopyObject %float %float_1",
"internal error: couldn't find image for " "internal error: couldn't find image for "
"%50 = OpCopyObject %18 %44", "%50 = OpCopyObject %18 %45",
{}}, {}},
{"%float 1D 0 0 0 1 Unknown", {"%float 1D 0 0 0 1 Unknown",
"OpStore %float_var %float_1", "OpStore %float_var %float_1",
@ -5687,29 +5933,29 @@ INSTANTIATE_TEST_SUITE_P(
"%result = OpImageSampleImplicitLod " "%result = OpImageSampleImplicitLod "
// bad type for coordinate: not a number // bad type for coordinate: not a number
"%v4float %sampled_image %float_var", "%v4float %sampled_image %float_var",
"bad or unsupported coordinate type for image access: %72 = " "bad or unsupported coordinate type for image access: %73 = "
"OpImageSampleImplicitLod %42 %71 %1", "OpImageSampleImplicitLod %42 %72 %1",
{}}, {}},
{"%float 2D 0 0 0 1 Unknown", // 2D {"%float 2D 0 0 0 1 Unknown", // 2D
"%result = OpImageSampleImplicitLod " "%result = OpImageSampleImplicitLod "
// 1 component, but need 2 // 1 component, but need 2
"%v4float %sampled_image %f1", "%v4float %sampled_image %f1",
"image access required 2 coordinate components, but only 1 provided, " "image access required 2 coordinate components, but only 1 provided, "
"in: %72 = OpImageSampleImplicitLod %42 %71 %12", "in: %73 = OpImageSampleImplicitLod %42 %72 %12",
{}}, {}},
{"%float 2D 0 1 0 1 Unknown", // 2DArray {"%float 2D 0 1 0 1 Unknown", // 2DArray
"%result = OpImageSampleImplicitLod " "%result = OpImageSampleImplicitLod "
// 2 component, but need 3 // 2 component, but need 3
"%v4float %sampled_image %vf12", "%v4float %sampled_image %vf12",
"image access required 3 coordinate components, but only 2 provided, " "image access required 3 coordinate components, but only 2 provided, "
"in: %72 = OpImageSampleImplicitLod %42 %71 %13", "in: %73 = OpImageSampleImplicitLod %42 %72 %13",
{}}, {}},
{"%float 3D 0 0 0 1 Unknown", // 3D {"%float 3D 0 0 0 1 Unknown", // 3D
"%result = OpImageSampleImplicitLod " "%result = OpImageSampleImplicitLod "
// 2 components, but need 3 // 2 components, but need 3
"%v4float %sampled_image %vf12", "%v4float %sampled_image %vf12",
"image access required 3 coordinate components, but only 2 provided, " "image access required 3 coordinate components, but only 2 provided, "
"in: %72 = OpImageSampleImplicitLod %42 %71 %13", "in: %73 = OpImageSampleImplicitLod %42 %72 %13",
{}}, {}},
})); }));
@ -5751,12 +5997,12 @@ INSTANTIATE_TEST_SUITE_P(
// ImageSampleDrefExplicitLod // ImageSampleDrefExplicitLod
{"%uint 2D 0 0 0 1 Unknown", {"%uint 2D 0 0 0 1 Unknown",
"%result = OpImageSampleDrefExplicitLod %uint %sampled_image %vf12 " "%result = OpImageSampleDrefExplicitLod %uint %sampled_image %vf12 "
"%f1 Lod %f1", "%f1 Lod %float_0",
"sampled image must have float component type", "sampled image must have float component type",
{}}, {}},
{"%int 2D 0 0 0 1 Unknown", {"%int 2D 0 0 0 1 Unknown",
"%result = OpImageSampleDrefExplicitLod %int %sampled_image %vf12 " "%result = OpImageSampleDrefExplicitLod %int %sampled_image %vf12 "
"%f1 Lod %f1", "%f1 Lod %float_0",
"sampled image must have float component type", "sampled image must have float component type",
{}}})); {}}}));
@ -5845,6 +6091,43 @@ INSTANTIATE_TEST_SUITE_P(
"gradient: ", "gradient: ",
{}}})); {}}}));
INSTANTIATE_TEST_SUITE_P(
ImageSampleDrefExplicitLod_CheckForLod0,
// Metal requires comparison sampling with explicit Level-of-detail to use
// Lod 0. The SPIR-V reader requires the operand to be parsed as a constant
// 0 value. SPIR-V validation requires the Lod parameter to be a floating
// point value for non-fetch operations. So only test float values.
SpvParserHandleTest_ImageCoordsTest,
::testing::ValuesIn(std::vector<ImageCoordsCase>{
// float 0.0 works
{"%float 2D 1 0 0 1 Unknown",
"%result = OpImageSampleDrefExplicitLod %float %sampled_image %vf1234 "
"%depth Lod %float_0",
"",
{R"(MemberAccessor[not set]{
Identifier[not set]{vf1234}
Identifier[not set]{xy}
}
)"}},
// float null works
{"%float 2D 1 0 0 1 Unknown",
"%result = OpImageSampleDrefExplicitLod %float %sampled_image %vf1234 "
"%depth Lod %float_0",
"",
{R"(MemberAccessor[not set]{
Identifier[not set]{vf1234}
Identifier[not set]{xy}
}
)"}},
// float 1.0 fails.
{"%float 2D 1 0 0 1 Unknown",
"%result = OpImageSampleDrefExplicitLod %float %sampled_image %vf1234 "
"%depth Lod %float_1",
"WGSL comparison sampling without derivatives requires "
"level-of-detail "
"0.0",
{}}}));
TEST_F(SpvParserHandleTest, CombinedImageSampler_IsError) { TEST_F(SpvParserHandleTest, CombinedImageSampler_IsError) {
const auto assembly = Preamble() + R"( const auto assembly = Preamble() + R"(
OpEntryPoint Fragment %100 "main" OpEntryPoint Fragment %100 "main"