spirv-reader: split off last coordinate for arrayed texture access

Also ensure that the number of texture coordinates is exactly
the right number required for the given texture dimension.
I think SPIR-V is looser in this respect.

Assumes coordinates are floating point.

Bug: tint:349
Change-Id: I4512c333fada3647c66f13ef31897b2d73b46cf0
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/33982
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: David Neto <dneto@google.com>
Auto-Submit: David Neto <dneto@google.com>
This commit is contained in:
David Neto 2020-11-27 16:17:03 +00:00 committed by Commit Bot service account
parent 456aad3bce
commit 3f305ac294
5 changed files with 607 additions and 11 deletions

View File

@ -16,6 +16,7 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cassert>
#include <sstream> #include <sstream>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
@ -53,6 +54,7 @@
#include "src/ast/switch_statement.h" #include "src/ast/switch_statement.h"
#include "src/ast/type/bool_type.h" #include "src/ast/type/bool_type.h"
#include "src/ast/type/pointer_type.h" #include "src/ast/type/pointer_type.h"
#include "src/ast/type/texture_type.h"
#include "src/ast/type/type.h" #include "src/ast/type/type.h"
#include "src/ast/type/u32_type.h" #include "src/ast/type/u32_type.h"
#include "src/ast/type/vector_type.h" #include "src/ast/type/vector_type.h"
@ -3630,7 +3632,6 @@ bool FunctionEmitter::EmitSampledImageAccess(
parser_impl_.GetMemoryObjectDeclarationForHandle(sampled_image_id, false); parser_impl_.GetMemoryObjectDeclarationForHandle(sampled_image_id, false);
const auto* image = const auto* image =
parser_impl_.GetMemoryObjectDeclarationForHandle(sampled_image_id, true); parser_impl_.GetMemoryObjectDeclarationForHandle(sampled_image_id, true);
if (!sampler) { if (!sampler) {
return Fail() << "interal error: couldn't find sampler for " return Fail() << "interal error: couldn't find sampler for "
<< inst.PrettyPrint(); << inst.PrettyPrint();
@ -3646,12 +3647,18 @@ bool FunctionEmitter::EmitSampledImageAccess(
params.push_back( params.push_back(
create<ast::IdentifierExpression>(namer_.Name(sampler->result_id()))); create<ast::IdentifierExpression>(namer_.Name(sampler->result_id())));
// Push the coordinates operand. // Push the coordinates operands.
// TODO(dneto): For explicit-Lod variations, we may have to convert from // TODO(dneto): For explicit-Lod variations, we may have to convert from
// integral coordinates to floating point coordinates. // integral coordinates to floating point coordinates.
// TODO(dneto): For arrayed access, split off the array layer. // In WGSL, integer (unnormalized) coordinates are only used for texture
params.push_back(MakeOperand(inst, 1).expr); // fetch (textureLoad on sampled image) or textureLoad or textureStore
uint32_t arg_index = 2; // on storage images.
auto coords = MakeCoordinateOperandsForImageAccess(inst);
if (coords.empty()) {
return false;
}
params.insert(params.end(), coords.begin(), coords.end());
uint32_t arg_index = 2; // Skip over texture and coordinate params
const auto num_args = inst.NumInOperands(); const auto num_args = inst.NumInOperands();
std::string builtin_name; std::string builtin_name;
@ -3725,6 +3732,137 @@ bool FunctionEmitter::EmitSampledImageAccess(
return EmitConstDefOrWriteToHoistedVar(inst, {result_type, call_expr}); return EmitConstDefOrWriteToHoistedVar(inst, {result_type, call_expr});
} }
ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
const spvtools::opt::Instruction& inst) {
if (!parser_impl_.success()) {
Fail();
return {};
}
if (inst.NumInOperands() == 0) {
Fail() << "internal error: not an image access instruction: "
<< inst.PrettyPrint();
return {};
}
const auto sampled_image_id = inst.GetSingleWordInOperand(0);
const auto* image =
parser_impl_.GetMemoryObjectDeclarationForHandle(sampled_image_id, true);
if (!image) {
Fail() << "internal error: couldn't find image for " << inst.PrettyPrint();
return {};
}
if (image->NumInOperands() < 1) {
Fail() << "image access is missing a coordinate parameter: "
<< inst.PrettyPrint();
return {};
}
// The coordinates parameter is always in position 1.
TypedExpression raw_coords(MakeOperand(inst, 1));
if (!raw_coords.type) {
return {};
}
ast::type::PointerType* type = parser_impl_.GetTypeForHandleVar(*image);
if (!parser_impl_.success()) {
Fail();
return {};
}
if (!type || !type->type()->IsTexture()) {
Fail() << "invalid texture type for " << image->PrettyPrint();
return {};
}
ast::type::TextureDimension dim = type->type()->AsTexture()->dim();
// Number of regular coordinates.
uint32_t num_axes = 0;
bool is_arrayed = false;
switch (dim) {
case ast::type::TextureDimension::k1d:
num_axes = 1;
break;
case ast::type::TextureDimension::k1dArray:
num_axes = 1;
is_arrayed = true;
break;
case ast::type::TextureDimension::k2d:
num_axes = 2;
break;
case ast::type::TextureDimension::k2dArray:
num_axes = 2;
is_arrayed = true;
break;
case ast::type::TextureDimension::k3d:
num_axes = 3;
break;
case ast::type::TextureDimension::kCube:
// For cubes, 3 coordinates form a direction vector.
num_axes = 3;
break;
case ast::type::TextureDimension::kCubeArray:
// For cubes, 3 coordinates form a direction vector.
num_axes = 3;
is_arrayed = true;
break;
default:
Fail() << "unsupported image dimensionality for " << type->type_name()
<< " prompted by " << inst.PrettyPrint();
return {};
}
assert(num_axes <= 3);
const auto num_coords_required = num_axes + (is_arrayed ? 1 : 0);
uint32_t num_coords_supplied = 0;
if (raw_coords.type->IsF32()) {
num_coords_supplied = 1;
} else if (raw_coords.type->IsVector()) {
num_coords_supplied = raw_coords.type->AsVector()->size();
}
if (num_coords_supplied == 0) {
Fail() << "bad or unsupported coordinate type for image access: "
<< inst.PrettyPrint();
return {};
}
if (num_coords_required > num_coords_supplied) {
Fail() << "image access required " << num_coords_required
<< " coordinate components, but only " << num_coords_supplied
<< " provided, in: " << inst.PrettyPrint();
return {};
}
auto prefix_swizzle = [this](uint32_t i) {
const char* prefix_name[] = {"", "x", "xy", "xyz"};
return ast_module_.create<ast::IdentifierExpression>(prefix_name[i & 3]);
};
ast::ExpressionList result;
// TODO(dneto): Convert component type if needed.
if (is_arrayed) {
// The source must be a vector, because it has enough components and has an
// array component. Use a vector swizzle to get the first `num_axes`
// components.
result.push_back(ast_module_.create<ast::MemberAccessorExpression>(
raw_coords.expr, prefix_swizzle(num_axes)));
// Now get the array index.
ast::Expression* array_index =
ast_module_.create<ast::MemberAccessorExpression>(raw_coords.expr,
Swizzle(num_axes));
// Convert it to an unsigned integer type.
result.push_back(ast_module_.create<ast::TypeConstructorExpression>(
ast_module_.create<ast::type::U32Type>(),
ast::ExpressionList{array_index}));
} else {
if (num_coords_supplied == num_coords_required) {
// Pass the value through.
result.push_back(std::move(raw_coords.expr));
} else {
// There are more coordinates supplied than needed. So the source type is
// a vector. Use a vector swizzle to get the first `num_axes` components.
result.push_back(ast_module_.create<ast::MemberAccessorExpression>(
raw_coords.expr, prefix_swizzle(num_axes)));
}
}
return result;
}
} // namespace spirv } // namespace spirv
} // namespace reader } // namespace reader
} // namespace tint } // namespace tint

View File

@ -679,6 +679,20 @@ class FunctionEmitter {
/// @returns the identifier expression for the @p i'th component /// @returns the identifier expression for the @p i'th component
ast::IdentifierExpression* Swizzle(uint32_t i); ast::IdentifierExpression* Swizzle(uint32_t i);
/// Converts SPIR-V image coordinates from an image access instruction
/// (e.g. OpImageSampledImplicitLod) into an expression list consisting of
/// the texture coordinates, and an integral array index if the texture is
/// arrayed. The texture coordinate is a scalar for 1D textures, a vector of
/// 2 elements for a 2D texture, and a vector of 3 elements for a 3D or
/// Cube texture. Excess components are ignored, e.g. if the SPIR-V
/// coordinate is a 4-element vector but the image is a 2D non-arrayed
/// texture then the 3rd and 4th components are ignored.
/// On failure, issues an error and returns an empty expression list.
/// @param image_access the image access instruction
/// @returns an ExpressionList of the coordinate and array index (if any)
ast::ExpressionList MakeCoordinateOperandsForImageAccess(
const spvtools::opt::Instruction& image_access);
private: private:
/// @returns the store type for the OpVariable instruction, or /// @returns the store type for the OpVariable instruction, or
/// null on failure. /// null on failure.

View File

@ -1600,8 +1600,16 @@ ParserImpl::GetMemoryObjectDeclarationForHandle(uint32_t id,
} }
} }
ast::type::Type* ParserImpl::GetTypeForHandleVar( ast::type::PointerType* ParserImpl::GetTypeForHandleVar(
const spvtools::opt::Instruction& var) { const spvtools::opt::Instruction& var) {
if (!success()) {
return nullptr;
}
auto where = handle_type_.find(&var);
if (where != handle_type_.end()) {
return where->second;
}
// The WGSL handle type is determined by looking at information from // The WGSL handle type is determined by looking at information from
// several sources: // several sources:
// - the usage of the handle by image access instructions // - the usage of the handle by image access instructions
@ -1637,7 +1645,7 @@ ast::type::Type* ParserImpl::GetTypeForHandleVar(
<< var.PrettyPrint(); << var.PrettyPrint();
return nullptr; return nullptr;
default: default:
Fail() << "invalid type for image or sampler variable " Fail() << "invalid type for image or sampler variable: "
<< var.PrettyPrint(); << var.PrettyPrint();
return nullptr; return nullptr;
} }
@ -1774,8 +1782,11 @@ ast::type::Type* ParserImpl::GetTypeForHandleVar(
} }
// Form the pointer type. // Form the pointer type.
return ast_module_.create<ast::type::PointerType>( auto* result = ast_module_.create<ast::type::PointerType>(
ast_store_type, ast::StorageClass::kUniformConstant); ast_store_type, ast::StorageClass::kUniformConstant);
// Remember it for later.
handle_type_[&var] = result;
return result;
} }
bool ParserImpl::RegisterHandleUsage() { bool ParserImpl::RegisterHandleUsage() {
@ -1931,6 +1942,11 @@ Usage ParserImpl::GetHandleUsage(uint32_t id) const {
return Usage(); return Usage();
} }
const spvtools::opt::Instruction* ParserImpl::GetInstructionForTest(
uint32_t id) const {
return def_use_mgr_ ? def_use_mgr_->GetDef(id) : nullptr;
}
} // namespace spirv } // namespace spirv
} // namespace reader } // namespace reader
} // namespace tint } // namespace tint

View File

@ -115,6 +115,9 @@ class ParserImpl : Reader {
return fail_stream_; return fail_stream_;
} }
/// @return true if failure has not yet occurred
bool success() const { return success_; }
/// @returns the accumulated error string /// @returns the accumulated error string
const std::string error() { return errors_.str(); } const std::string error() { return errors_.str(); }
@ -383,7 +386,7 @@ class ParserImpl : Reader {
/// @param id the SPIR-V result id. /// @param id the SPIR-V result id.
/// @return the Source record, or a default one /// @return the Source record, or a default one
Source GetSourceForResultIdForTest(uint32_t id) const; Source GetSourceForResultIdForTest(uint32_t id) const;
/// Returns the soruce record for the given instruction. /// Returns the source record for the given instruction.
/// @param inst the SPIR-V instruction /// @param inst the SPIR-V instruction
/// @return the Source record, or a default one /// @return the Source record, or a default one
Source GetSourceForInst(const spvtools::opt::Instruction* inst) const; Source GetSourceForInst(const spvtools::opt::Instruction* inst) const;
@ -428,7 +431,13 @@ class ParserImpl : Reader {
/// @param var the OpVariable instruction /// @param var the OpVariable instruction
/// @returns the Tint AST type for the poiner-to-{sampler|texture} or null on /// @returns the Tint AST type for the poiner-to-{sampler|texture} or null on
/// error /// error
ast::type::Type* GetTypeForHandleVar(const spvtools::opt::Instruction& var); ast::type::PointerType* GetTypeForHandleVar(
const spvtools::opt::Instruction& var);
/// Returns the SPIR-V instruction with the given ID, or nullptr.
/// @param id the SPIR-V result ID
/// @returns the instruction, or nullptr on error
const spvtools::opt::Instruction* GetInstructionForTest(uint32_t id) const;
private: private:
/// Converts a specific SPIR-V type to a Tint type. Integer case /// Converts a specific SPIR-V type to a Tint type. Integer case
@ -580,6 +589,9 @@ class ParserImpl : Reader {
// Maps a memory-object-declaration instruction to any sampler or texture // Maps a memory-object-declaration instruction to any sampler or texture
// usages implied by usages of the memory-object-declaration. // usages implied by usages of the memory-object-declaration.
std::unordered_map<const spvtools::opt::Instruction*, Usage> handle_usage_; std::unordered_map<const spvtools::opt::Instruction*, Usage> handle_usage_;
// The inferred pointer type for the given handle variable.
std::unordered_map<const spvtools::opt::Instruction*, ast::type::PointerType*>
handle_type_;
}; };
} // namespace spirv } // namespace spirv

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
#include <string> #include <string>
#include <vector>
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "src/reader/spirv/function.h" #include "src/reader/spirv/function.h"
@ -39,7 +40,7 @@ std::string Preamble() {
)"; )";
} }
std::string CommonTypes() { std::string CommonBasicTypes() {
return R"( return R"(
%void = OpTypeVoid %void = OpTypeVoid
%voidfn = OpTypeFunction %void %voidfn = OpTypeFunction %void
@ -50,6 +51,7 @@ std::string CommonTypes() {
%int_3 = OpConstant %uint 3 %int_3 = OpConstant %uint 3
%int_4 = OpConstant %uint 4 %int_4 = OpConstant %uint 4
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1 %uint_1 = OpConstant %uint 1
%uint_2 = OpConstant %uint 2 %uint_2 = OpConstant %uint 2
%uint_100 = OpConstant %uint 100 %uint_100 = OpConstant %uint 100
@ -63,13 +65,26 @@ std::string CommonTypes() {
%v4float = OpTypeVector %float 4 %v4float = OpTypeVector %float 4
%float_null = OpConstantNull %float %float_null = OpConstantNull %float
%float_1 = OpConstant %float 1
%float_2 = OpConstant %float 2
%float_3 = OpConstant %float 3
%float_4 = OpConstant %float 4
%float_7 = OpConstant %float 7 %float_7 = OpConstant %float 7
%v2float_null = OpConstantNull %v2float %v2float_null = OpConstantNull %v2float
%v3float_null = OpConstantNull %v3float %v3float_null = OpConstantNull %v3float
%v4float_null = OpConstantNull %v4float %v4float_null = OpConstantNull %v4float
%vf12 = OpConstantComposite %v2float %float_1 %float_2
%vf123 = OpConstantComposite %v3float %float_1 %float_2 %float_3
%vf1234 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
%depth = OpConstant %float 0.2 %depth = OpConstant %float 0.2
%offsets2d = OpConstantComposite %v2int %int_3 %int_4 %offsets2d = OpConstantComposite %v2int %int_3 %int_4
)";
}
std::string CommonImageTypes() {
return R"(
; Define types for all sampler and texture types that can map to WGSL, ; Define types for all sampler and texture types that can map to WGSL,
; modulo texel formats for storage textures. For now, we limit ; modulo texel formats for storage textures. For now, we limit
@ -194,6 +209,10 @@ std::string CommonTypes() {
)"; )";
} }
std::string CommonTypes() {
return CommonBasicTypes() + CommonImageTypes();
}
TEST_F(SpvParserTest, TEST_F(SpvParserTest,
GetMemoryObjectDeclarationForHandle_WellFormedButNotAHandle) { GetMemoryObjectDeclarationForHandle_WellFormedButNotAHandle) {
const auto assembly = Preamble() + CommonTypes() + R"( const auto assembly = Preamble() + CommonTypes() + R"(
@ -1801,6 +1820,403 @@ INSTANTIATE_TEST_SUITE_P(
) )
})"})); })"}));
struct ImageCoordsCase {
std::string spirv_image_type_details; // SPIR-V image type, excluding result
// ID and opcode
std::string spirv_image_access;
std::string expected_error;
std::vector<std::string> expected_expressions;
};
inline std::ostream& operator<<(std::ostream& out, const ImageCoordsCase& c) {
out << "ImageAccessCase(" << c.spirv_image_type_details << "\n"
<< c.spirv_image_access << "\n";
for (auto e : c.expected_expressions) {
out << e << ",";
}
out << ")" << std::endl;
return out;
}
using SpvParserTest_ImageCoordsTest =
SpvParserTestBase<::testing::TestWithParam<ImageCoordsCase>>;
TEST_P(SpvParserTest_ImageCoordsTest, MakeCoordinateOperandsForImageAccess) {
const auto assembly = Preamble() + R"(
OpEntryPoint Fragment %100 "main"
OpExecutionMode %100 OriginUpperLeft
OpName %float_var "float_var"
OpName %ptr_float "ptr_float"
OpDecorate %10 DescriptorSet 0
OpDecorate %10 Binding 0
OpDecorate %20 DescriptorSet 2
OpDecorate %20 Binding 1
OpDecorate %30 DescriptorSet 0
OpDecorate %30 Binding 1
)" + CommonBasicTypes() +
R"(
%sampler = OpTypeSampler
%ptr_sampler = OpTypePointer UniformConstant %sampler
%im_ty = OpTypeImage )" +
GetParam().spirv_image_type_details + R"(
%ptr_im_ty = OpTypePointer UniformConstant %im_ty
%si_ty = OpTypeSampledImage %im_ty
%coords = OpConstantNull %v2float
%ptr_float = OpTypePointer Function %float
%10 = OpVariable %ptr_sampler UniformConstant
%20 = OpVariable %ptr_im_ty UniformConstant
%30 = OpVariable %ptr_sampler UniformConstant ; comparison sampler, when needed
%100 = OpFunction %void None %voidfn
%entry = OpLabel
%float_var = OpVariable %ptr_float Function
; create some names that will become WGSL variables x_1, x_12, and so on.
%1 = OpCopyObject %float %float_1
%12 = OpCopyObject %v2float %vf12
%123 = OpCopyObject %v3float %vf123
%1234 = OpCopyObject %v4float %vf1234
%sam = OpLoad %sampler %10
%im = OpLoad %im_ty %20
%sampled_image = OpSampledImage %si_ty %im %sam
)" + GetParam().spirv_image_access +
R"(
; Use an anchor for the cases when the image access doesn't have a result ID.
%1000 = OpCopyObject %uint %uint_0
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
if (!p->BuildAndParseInternalModule()) {
EXPECT_THAT(p->error(), Eq(GetParam().expected_error));
} else {
EXPECT_TRUE(p->error().empty()) << p->error();
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
// We actually have to generate the module to cache expressions for the
// result IDs, particularly the OpCopyObject
fe.Emit();
const spvtools::opt::Instruction* anchor = p->GetInstructionForTest(1000);
ASSERT_NE(anchor, nullptr);
const spvtools::opt::Instruction& image_access = *(anchor->PreviousNode());
ast::ExpressionList result =
fe.MakeCoordinateOperandsForImageAccess(image_access);
if (GetParam().expected_error.empty()) {
EXPECT_TRUE(fe.success()) << p->error();
EXPECT_TRUE(p->error().empty());
std::vector<std::string> result_strings;
for (auto* expr : result) {
ASSERT_NE(expr, nullptr);
result_strings.push_back(expr->str());
}
EXPECT_THAT(result_strings,
::testing::ContainerEq(GetParam().expected_expressions));
} else {
EXPECT_FALSE(fe.success());
EXPECT_THAT(p->error(), Eq(GetParam().expected_error));
EXPECT_TRUE(result.empty());
}
}
}
INSTANTIATE_TEST_SUITE_P(Good_1D,
SpvParserTest_ImageCoordsTest,
::testing::ValuesIn(std::vector<ImageCoordsCase>{
{"%float 1D 0 0 0 1 Unknown",
"%result = OpImageSampleImplicitLod %v4float "
"%sampled_image %1",
"",
{"Identifier[not set]{x_1}\n"}},
{"%float 1D 0 0 0 1 Unknown",
"%result = OpImageSampleImplicitLod %v4float "
"%sampled_image %12", // one excess arg
"",
{R"(MemberAccessor[not set]{
Identifier[not set]{x_12}
Identifier[not set]{x}
}
)"}},
{"%float 1D 0 0 0 1 Unknown",
"%result = OpImageSampleImplicitLod %v4float "
"%sampled_image %123", // two excess args
"",
{R"(MemberAccessor[not set]{
Identifier[not set]{x_123}
Identifier[not set]{x}
}
)"}},
{"%float 1D 0 0 0 1 Unknown",
"%result = OpImageSampleImplicitLod %v4float "
"%sampled_image %1234", // three excess args
"",
{R"(MemberAccessor[not set]{
Identifier[not set]{x_1234}
Identifier[not set]{x}
}
)"}}}));
INSTANTIATE_TEST_SUITE_P(Good_1DArray,
SpvParserTest_ImageCoordsTest,
::testing::ValuesIn(std::vector<ImageCoordsCase>{
{"%float 1D 0 1 0 1 Unknown",
"%result = OpImageSampleImplicitLod %v4float "
"%sampled_image %12",
"",
{
R"(MemberAccessor[not set]{
Identifier[not set]{x_12}
Identifier[not set]{x}
}
)",
R"(TypeConstructor[not set]{
__u32
MemberAccessor[not set]{
Identifier[not set]{x_12}
Identifier[not set]{y}
}
}
)"}},
{"%float 1D 0 1 0 1 Unknown",
"%result = OpImageSampleImplicitLod %v4float "
"%sampled_image %123", // one excess arg
"",
{
R"(MemberAccessor[not set]{
Identifier[not set]{x_123}
Identifier[not set]{x}
}
)",
R"(TypeConstructor[not set]{
__u32
MemberAccessor[not set]{
Identifier[not set]{x_123}
Identifier[not set]{y}
}
}
)"}},
{"%float 1D 0 1 0 1 Unknown",
"%result = OpImageSampleImplicitLod %v4float "
"%sampled_image %1234", // two excess args
"",
{
R"(MemberAccessor[not set]{
Identifier[not set]{x_1234}
Identifier[not set]{x}
}
)",
R"(TypeConstructor[not set]{
__u32
MemberAccessor[not set]{
Identifier[not set]{x_1234}
Identifier[not set]{y}
}
}
)"}}}));
INSTANTIATE_TEST_SUITE_P(Good_2D,
SpvParserTest_ImageCoordsTest,
::testing::ValuesIn(std::vector<ImageCoordsCase>{
{"%float 2D 0 0 0 1 Unknown",
"%result = OpImageSampleImplicitLod %v4float "
"%sampled_image %12",
"",
{"Identifier[not set]{x_12}\n"}},
{"%float 2D 0 0 0 1 Unknown",
"%result = OpImageSampleImplicitLod %v4float "
"%sampled_image %123", // one excess arg
"",
{R"(MemberAccessor[not set]{
Identifier[not set]{x_123}
Identifier[not set]{xy}
}
)"}},
{"%float 2D 0 0 0 1 Unknown",
"%result = OpImageSampleImplicitLod %v4float "
"%sampled_image %1234", // two excess args
"",
{R"(MemberAccessor[not set]{
Identifier[not set]{x_1234}
Identifier[not set]{xy}
}
)"}}}));
INSTANTIATE_TEST_SUITE_P(Good_2DArray,
SpvParserTest_ImageCoordsTest,
::testing::ValuesIn(std::vector<ImageCoordsCase>{
{"%float 2D 0 1 0 1 Unknown",
"%result = OpImageSampleImplicitLod %v4float "
"%sampled_image %123",
"",
{
R"(MemberAccessor[not set]{
Identifier[not set]{x_123}
Identifier[not set]{xy}
}
)",
R"(TypeConstructor[not set]{
__u32
MemberAccessor[not set]{
Identifier[not set]{x_123}
Identifier[not set]{z}
}
}
)"}},
{"%float 2D 0 1 0 1 Unknown",
"%result = OpImageSampleImplicitLod %v4float "
"%sampled_image %1234", // one excess arg
"",
{
R"(MemberAccessor[not set]{
Identifier[not set]{x_1234}
Identifier[not set]{xy}
}
)",
R"(TypeConstructor[not set]{
__u32
MemberAccessor[not set]{
Identifier[not set]{x_1234}
Identifier[not set]{z}
}
}
)"}}}));
INSTANTIATE_TEST_SUITE_P(Good_3D,
SpvParserTest_ImageCoordsTest,
::testing::ValuesIn(std::vector<ImageCoordsCase>{
{"%float 3D 0 0 0 1 Unknown",
"%result = OpImageSampleImplicitLod "
"%v4float "
"%sampled_image %123",
"",
{"Identifier[not set]{x_123}\n"}},
{"%float 3D 0 0 0 1 Unknown",
"%result = OpImageSampleImplicitLod "
"%v4float "
"%sampled_image %1234", // one excess
// arg
"",
{R"(MemberAccessor[not set]{
Identifier[not set]{x_1234}
Identifier[not set]{xyz}
}
)"}}}));
INSTANTIATE_TEST_SUITE_P(Good_Cube,
SpvParserTest_ImageCoordsTest,
::testing::ValuesIn(std::vector<ImageCoordsCase>{
{"%float Cube 0 0 0 1 Unknown",
"%result = OpImageSampleImplicitLod "
"%v4float "
"%sampled_image %123",
"",
{"Identifier[not set]{x_123}\n"}},
{"%float Cube 0 0 0 1 Unknown",
"%result = OpImageSampleImplicitLod "
"%v4float "
"%sampled_image %1234", // one excess
// arg
"",
{R"(MemberAccessor[not set]{
Identifier[not set]{x_1234}
Identifier[not set]{xyz}
}
)"}}}));
INSTANTIATE_TEST_SUITE_P(Good_CubeArray,
SpvParserTest_ImageCoordsTest,
::testing::ValuesIn(std::vector<ImageCoordsCase>{
{"%float Cube 0 1 0 1 Unknown",
"%result = OpImageSampleImplicitLod "
"%v4float "
"%sampled_image %1234",
"",
{R"(MemberAccessor[not set]{
Identifier[not set]{x_1234}
Identifier[not set]{xyz}
}
)",
R"(TypeConstructor[not set]{
__u32
MemberAccessor[not set]{
Identifier[not set]{x_1234}
Identifier[not set]{w}
}
}
)"}}}));
INSTANTIATE_TEST_SUITE_P(BadInstructions,
SpvParserTest_ImageCoordsTest,
::testing::ValuesIn(std::vector<ImageCoordsCase>{
{"%float 1D 0 0 0 1 Unknown",
"OpNop",
"internal error: not an image access "
"instruction: OpNop",
{}},
{"%float 1D 0 0 0 1 Unknown",
"%foo = OpCopyObject %float %float_1",
"internal error: couldn't find image for "
"%50 = OpCopyObject %6 %26",
{}},
{"%float 1D 0 0 0 1 Unknown",
"OpStore %float_var %float_1",
"invalid type for image or sampler "
"variable: %2 = OpVariable %3 Function",
{}},
// An example with a missing coordinate
// won't assemble, so we skip it.
}));
INSTANTIATE_TEST_SUITE_P(
Bad_Coordinate,
SpvParserTest_ImageCoordsTest,
::testing::ValuesIn(std::vector<ImageCoordsCase>{
{"%float 2D 0 0 0 1 Unknown",
"%result = OpImageSampleImplicitLod "
// bad type for coordinate: not a number
"%v4float %sampled_image %float_var",
"bad or unsupported coordinate type for image access: %50 = "
"OpImageSampleImplicitLod %24 %49 %2",
{}},
{"%float 1D 0 1 0 1 Unknown", // 1DArray
"%result = OpImageSampleImplicitLod "
// 1 component, but need 2
"%v4float %sampled_image %1",
"image access required 2 coordinate components, but only 1 provided, "
"in: %50 = OpImageSampleImplicitLod %24 %49 %1",
{}},
{"%float 2D 0 0 0 1 Unknown", // 2D
"%result = OpImageSampleImplicitLod "
// 1 component, but need 2
"%v4float %sampled_image %1",
"image access required 2 coordinate components, but only 1 provided, "
"in: %50 = OpImageSampleImplicitLod %24 %49 %1",
{}},
{"%float 2D 0 1 0 1 Unknown", // 2DArray
"%result = OpImageSampleImplicitLod "
// 2 component, but need 3
"%v4float %sampled_image %12",
"image access required 3 coordinate components, but only 2 provided, "
"in: %50 = OpImageSampleImplicitLod %24 %49 %12",
{}},
{"%float 3D 0 0 0 1 Unknown", // 3D
"%result = OpImageSampleImplicitLod "
// 2 components, but need 3
"%v4float %sampled_image %12",
"image access required 3 coordinate components, but only 2 provided, "
"in: %50 = OpImageSampleImplicitLod %24 %49 %12",
{}},
}));
} // namespace } // namespace
} // namespace spirv } // namespace spirv
} // namespace reader } // namespace reader