spirv-reader: support textureStore

Fixes: tint:381
Change-Id: I401cebcd94b92365037228e0bd50b67bc8d12ab8
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/34424
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Auto-Submit: David Neto <dneto@google.com>
This commit is contained in:
David Neto 2020-12-01 20:33:07 +00:00 committed by Commit Bot service account
parent 2478ae78bb
commit 91ad3961df
5 changed files with 933 additions and 79 deletions

View File

@ -56,6 +56,7 @@
#include "src/ast/type/depth_texture_type.h" #include "src/ast/type/depth_texture_type.h"
#include "src/ast/type/f32_type.h" #include "src/ast/type/f32_type.h"
#include "src/ast/type/pointer_type.h" #include "src/ast/type/pointer_type.h"
#include "src/ast/type/storage_texture_type.h"
#include "src/ast/type/texture_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"
@ -2665,12 +2666,8 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
return false; return false;
} }
if (IsSampledImageAccess(inst.opcode())) { if (IsSampledImageAccess(inst.opcode()) || IsRawImageAccess(inst.opcode())) {
return EmitSampledImageAccess(inst); return EmitImageAccess(inst);
}
if (IsRawImageAccess(inst.opcode())) {
return Fail() << "raw image access is not implemented yet:"
<< inst.PrettyPrint();
} }
switch (inst.opcode()) { switch (inst.opcode()) {
@ -2891,6 +2888,21 @@ ast::IdentifierExpression* FunctionEmitter::Swizzle(uint32_t i) {
return ast_module_.create<ast::IdentifierExpression>(names[i & 3]); return ast_module_.create<ast::IdentifierExpression>(names[i & 3]);
} }
ast::IdentifierExpression* FunctionEmitter::PrefixSwizzle(uint32_t n) {
switch (n) {
case 1:
return ast_module_.create<ast::IdentifierExpression>("x");
case 2:
return ast_module_.create<ast::IdentifierExpression>("xy");
case 3:
return ast_module_.create<ast::IdentifierExpression>("xyz");
default:
break;
}
Fail() << "invalid swizzle prefix count: " << n;
return nullptr;
}
TypedExpression FunctionEmitter::MakeAccessChain( TypedExpression FunctionEmitter::MakeAccessChain(
const spvtools::opt::Instruction& inst) { const spvtools::opt::Instruction& inst) {
if (inst.NumInOperands() < 1) { if (inst.NumInOperands() < 1) {
@ -3626,30 +3638,47 @@ void FunctionEmitter::ApplySourceForInstruction(
} }
} }
bool FunctionEmitter::EmitSampledImageAccess( bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
const spvtools::opt::Instruction& inst) { uint32_t arg_index = 0; // The SPIR-V input argument index
auto* result_type = parser_impl_.ConvertType(inst.type_id());
// The sampled image operand is always first.
const auto sampled_image_id = inst.GetSingleWordInOperand(0);
const auto* sampler =
parser_impl_.GetMemoryObjectDeclarationForHandle(sampled_image_id, false);
const auto* image =
parser_impl_.GetMemoryObjectDeclarationForHandle(sampled_image_id, true);
if (!sampler) {
return Fail() << "interal error: couldn't find sampler for "
<< inst.PrettyPrint();
}
if (!image) {
return Fail() << "interal error: couldn't find image for "
<< inst.PrettyPrint();
}
ast::ExpressionList params; ast::ExpressionList params;
const auto image_or_sampled_image_operand_id =
inst.GetSingleWordInOperand(arg_index);
// Form the texture operand.
const auto* image = parser_impl_.GetMemoryObjectDeclarationForHandle(
image_or_sampled_image_operand_id, true);
if (!image) {
return Fail() << "internal error: couldn't find image for "
<< inst.PrettyPrint();
}
params.push_back( params.push_back(
create<ast::IdentifierExpression>(namer_.Name(image->result_id()))); create<ast::IdentifierExpression>(namer_.Name(image->result_id())));
if (IsSampledImageAccess(inst.opcode())) {
// Form the sampler operand.
const auto* sampler = parser_impl_.GetMemoryObjectDeclarationForHandle(
image_or_sampled_image_operand_id, false);
if (!sampler) {
return Fail() << "internal error: couldn't find sampler for "
<< inst.PrettyPrint();
}
params.push_back( params.push_back(
create<ast::IdentifierExpression>(namer_.Name(sampler->result_id()))); create<ast::IdentifierExpression>(namer_.Name(sampler->result_id())));
}
ast::type::Pointer* texture_ptr_type =
parser_impl_.GetTypeForHandleVar(*image);
if (!texture_ptr_type) {
return Fail();
}
ast::type::Texture* texture_type =
texture_ptr_type->type()->As<ast::type::Texture>();
if (!texture_type) {
return Fail();
}
// We're done with the first SPIR-V operand. Move on to the next.
arg_index++;
// Push the coordinates operands. // 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
@ -3662,7 +3691,9 @@ bool FunctionEmitter::EmitSampledImageAccess(
return false; return false;
} }
params.insert(params.end(), coords.begin(), coords.end()); params.insert(params.end(), coords.begin(), coords.end());
uint32_t arg_index = 2; // Skip over texture and coordinate params // Skip the coordinates operand.
arg_index++;
const auto num_args = inst.NumInOperands(); const auto num_args = inst.NumInOperands();
std::string builtin_name; std::string builtin_name;
@ -3688,6 +3719,23 @@ bool FunctionEmitter::EmitSampledImageAccess(
return Fail() << " image gather is not yet supported"; return Fail() << " image gather is not yet supported";
case SpvOpImageQueryLod: case SpvOpImageQueryLod:
return Fail() << " image query Lod is not yet supported"; return Fail() << " image query Lod is not yet supported";
case SpvOpImageWrite:
builtin_name = "textureStore";
if (arg_index < num_args) {
auto texel = MakeOperand(inst, arg_index);
auto* converted_texel =
ConvertTexelForStorage(inst, texel, texture_type);
if (!converted_texel) {
return false;
}
params.push_back(converted_texel);
arg_index++;
} else {
return Fail() << "image write is missing a Texel operand: "
<< inst.PrettyPrint();
}
break;
default: default:
return Fail() << "internal error: sampled image access"; return Fail() << "internal error: sampled image access";
} }
@ -3711,17 +3759,12 @@ bool FunctionEmitter::EmitSampledImageAccess(
auto* lod_operand = MakeOperand(inst, arg_index).expr; auto* lod_operand = MakeOperand(inst, arg_index).expr;
// When sampling from a depth texture, the Lod operand must be an unsigned // When sampling from a depth texture, the Lod operand must be an unsigned
// integer. // integer.
if (ast::type::Pointer* type = parser_impl_.GetTypeForHandleVar(*image)) {
if (ast::type::Texture* texture_type =
type->type()->As<ast::type::Texture>()) {
if (texture_type->Is<ast::type::DepthTexture>()) { if (texture_type->Is<ast::type::DepthTexture>()) {
// Convert it to an unsigned integer type. // Convert it to an unsigned integer type.
lod_operand = ast_module_.create<ast::TypeConstructorExpression>( lod_operand = ast_module_.create<ast::TypeConstructorExpression>(
ast_module_.create<ast::type::U32>(), ast_module_.create<ast::type::U32>(),
ast::ExpressionList{lod_operand}); ast::ExpressionList{lod_operand});
} }
}
}
params.push_back(lod_operand); params.push_back(lod_operand);
image_operands_mask ^= SpvImageOperandsLodMask; image_operands_mask ^= SpvImageOperandsLodMask;
arg_index++; arg_index++;
@ -3747,7 +3790,18 @@ bool FunctionEmitter::EmitSampledImageAccess(
auto* ident = create<ast::IdentifierExpression>(builtin_name); auto* ident = create<ast::IdentifierExpression>(builtin_name);
auto* call_expr = create<ast::CallExpression>(ident, std::move(params)); auto* call_expr = create<ast::CallExpression>(ident, std::move(params));
return EmitConstDefOrWriteToHoistedVar(inst, {result_type, call_expr});
if (inst.type_id() != 0) {
// It returns a value.
auto* result_type = parser_impl_.ConvertType(inst.type_id());
// TODO(dneto): Convert result signedness if needed. crbug.com/tint/382
EmitConstDefOrWriteToHoistedVar(inst, {result_type, call_expr});
} else {
// It's an image write. No value is returned, so make a statement out
// of the call.
AddStatementForInstruction(create<ast::CallStatement>(call_expr), inst);
}
return success();
} }
ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess( ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
@ -3828,10 +3882,11 @@ ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
assert(num_axes <= 3); assert(num_axes <= 3);
const auto num_coords_required = num_axes + (is_arrayed ? 1 : 0); const auto num_coords_required = num_axes + (is_arrayed ? 1 : 0);
uint32_t num_coords_supplied = 0; uint32_t num_coords_supplied = 0;
if (raw_coords.type->Is<ast::type::F32>()) { if (raw_coords.type->is_float_scalar() ||
raw_coords.type->is_integer_scalar()) {
num_coords_supplied = 1; num_coords_supplied = 1;
} else if (raw_coords.type->Is<ast::type::Vector>()) { } else if (auto* vec_ty = raw_coords.type->As<ast::type::Vector>()) {
num_coords_supplied = raw_coords.type->As<ast::type::Vector>()->size(); num_coords_supplied = vec_ty->size();
} }
if (num_coords_supplied == 0) { if (num_coords_supplied == 0) {
Fail() << "bad or unsupported coordinate type for image access: " Fail() << "bad or unsupported coordinate type for image access: "
@ -3845,20 +3900,15 @@ ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
return {}; 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; ast::ExpressionList result;
// TODO(dneto): Convert component type if needed. // TODO(dneto): Convert coordinate component type if needed.
if (is_arrayed) { if (is_arrayed) {
// The source must be a vector, because it has enough components and has an // 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` // array component. Use a vector swizzle to get the first `num_axes`
// components. // components.
result.push_back(ast_module_.create<ast::MemberAccessorExpression>( result.push_back(ast_module_.create<ast::MemberAccessorExpression>(
raw_coords.expr, prefix_swizzle(num_axes))); raw_coords.expr, PrefixSwizzle(num_axes)));
// Now get the array index. // Now get the array index.
ast::Expression* array_index = ast::Expression* array_index =
@ -3876,12 +3926,97 @@ ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
// There are more coordinates supplied than needed. So the source type is // 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. // a vector. Use a vector swizzle to get the first `num_axes` components.
result.push_back(ast_module_.create<ast::MemberAccessorExpression>( result.push_back(ast_module_.create<ast::MemberAccessorExpression>(
raw_coords.expr, prefix_swizzle(num_axes))); raw_coords.expr, PrefixSwizzle(num_axes)));
} }
} }
return result; return result;
} }
ast::Expression* FunctionEmitter::ConvertTexelForStorage(
const spvtools::opt::Instruction& inst,
TypedExpression texel,
ast::type::Texture* texture_type) {
auto* storage_texture_type = texture_type->As<ast::type::StorageTexture>();
auto* src_type = texel.type;
if (!storage_texture_type) {
Fail() << "writing to other than storage texture: " << inst.PrettyPrint();
return nullptr;
}
const auto format = storage_texture_type->image_format();
auto* dest_type = parser_impl_.GetTexelTypeForFormat(format);
if (!dest_type) {
Fail();
return nullptr;
}
if (src_type == dest_type) {
return texel.expr;
}
const uint32_t dest_count =
dest_type->is_scalar() ? 1 : dest_type->As<ast::type::Vector>()->size();
if (dest_count == 3) {
Fail() << "3-channel storage textures are not supported: "
<< inst.PrettyPrint();
return nullptr;
}
const uint32_t src_count =
src_type->is_scalar() ? 1 : src_type->As<ast::type::Vector>()->size();
if (src_count < dest_count) {
Fail() << "texel has too few components for storage texture: " << src_count
<< " provided but " << dest_count
<< " required, in: " << inst.PrettyPrint();
return nullptr;
}
// If the texel has more components than necessary, then we will ignore the
// higher-numbered components.
auto* texel_prefix = (src_count == dest_count)
? texel.expr
: ast_module_.create<ast::MemberAccessorExpression>(
texel.expr, PrefixSwizzle(dest_count));
if (!(dest_type->is_float_scalar_or_vector() ||
dest_type->is_unsigned_scalar_or_vector() ||
dest_type->is_signed_scalar_or_vector())) {
Fail() << "invalid destination type for storage texture write: "
<< dest_type->type_name();
return nullptr;
}
if (!(src_type->is_float_scalar_or_vector() ||
src_type->is_unsigned_scalar_or_vector() ||
src_type->is_signed_scalar_or_vector())) {
Fail() << "invalid texel type for storage texture write: "
<< inst.PrettyPrint();
return nullptr;
}
if (dest_type->is_float_scalar_or_vector() &&
!src_type->is_float_scalar_or_vector()) {
Fail() << "can only write float or float vector to a storage image with "
"floating texel format: "
<< inst.PrettyPrint();
return nullptr;
}
if (!dest_type->is_float_scalar_or_vector() &&
src_type->is_float_scalar_or_vector()) {
Fail()
<< "float or float vector can only be written to a storage image with "
"floating texel format: "
<< inst.PrettyPrint();
return nullptr;
}
if (dest_type->is_float_scalar_or_vector()) {
return texel_prefix;
}
// The only remaining cases are signed/unsigned source, and signed/unsigned
// destination.
if (dest_type->is_unsigned_scalar_or_vector() ==
src_type->is_unsigned_scalar_or_vector()) {
return texel_prefix;
}
// We must do a bitcast conversion.
return ast_module_.create<ast::BitcastExpression>(dest_type, texel_prefix);
}
} // namespace spirv } // namespace spirv
} // namespace reader } // namespace reader
} // namespace tint } // namespace tint

View File

@ -680,6 +680,13 @@ 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);
/// Returns an identifier expression for the swizzle name of the first
/// @p n elements of a vector. Emits an error and returns nullptr if @p n
/// is out of range, i.e. 4 or higher.
/// @param n the number of components in the swizzle
/// @returns the swizzle identifier for the first n elements of a vector
ast::IdentifierExpression* PrefixSwizzle(uint32_t n);
/// Converts SPIR-V image coordinates from an image access instruction /// Converts SPIR-V image coordinates from an image access instruction
/// (e.g. OpImageSampledImplicitLod) into an expression list consisting of /// (e.g. OpImageSampledImplicitLod) into an expression list consisting of
/// the texture coordinates, and an integral array index if the texture is /// the texture coordinates, and an integral array index if the texture is
@ -727,10 +734,24 @@ class FunctionEmitter {
TypedExpression MakeIntrinsicCall(const spvtools::opt::Instruction& inst); TypedExpression MakeIntrinsicCall(const spvtools::opt::Instruction& inst);
/// Emits a texture builtin function call for a SPIR-V instruction that /// Emits a texture builtin function call for a SPIR-V instruction that
/// accesses a sampled image. /// accesses an image or sampled image.
/// @param inst the SPIR-V instruction /// @param inst the SPIR-V instruction
/// @returns an expression /// @returns an expression
bool EmitSampledImageAccess(const spvtools::opt::Instruction& inst); bool EmitImageAccess(const spvtools::opt::Instruction& inst);
/// Converts the given texel to match the type required for the storage
/// texture with the given type. This can generate a swizzle to retain
/// only the first few components of the texel vector, and maybe a bitcast
/// to convert signedness. Returns an expression, or emits an error and
/// returns nullptr.
/// @param inst the image access instruction (used for diagnostics)
/// @param texel the texel
/// @param texture_type the type of the storage texture
/// @returns the texel, after necessary conversion.
ast::Expression* ConvertTexelForStorage(
const spvtools::opt::Instruction& inst,
TypedExpression texel,
ast::type::Texture* texture_type);
/// Returns an expression for an OpSelect, if its operands are scalars /// Returns an expression for an OpSelect, if its operands are scalars
/// or vectors. These translate directly to WGSL select. Otherwise, return /// or vectors. These translate directly to WGSL select. Otherwise, return

View File

@ -1601,16 +1601,11 @@ ParserImpl::GetMemoryObjectDeclarationForHandle(uint32_t id,
} }
} }
ast::type::Pointer* ParserImpl::GetTypeForHandleVar( const spvtools::opt::Instruction* ParserImpl::GetSpirvTypeForHandleVar(
const spvtools::opt::Instruction& var) { const spvtools::opt::Instruction& var) {
if (!success()) { if (!success()) {
return nullptr; 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
@ -1650,6 +1645,21 @@ ast::type::Pointer* ParserImpl::GetTypeForHandleVar(
<< var.PrettyPrint(); << var.PrettyPrint();
return nullptr; return nullptr;
} }
return raw_handle_type;
}
ast::type::Pointer* ParserImpl::GetTypeForHandleVar(
const spvtools::opt::Instruction& var) {
auto where = handle_type_.find(&var);
if (where != handle_type_.end()) {
return where->second;
}
const spvtools::opt::Instruction* raw_handle_type =
GetSpirvTypeForHandleVar(var);
if (!raw_handle_type) {
return nullptr;
}
// The variable could be a sampler or image. // The variable could be a sampler or image.
// Where possible, determine which one it is from the usage inferred // Where possible, determine which one it is from the usage inferred
@ -1790,6 +1800,115 @@ ast::type::Pointer* ParserImpl::GetTypeForHandleVar(
return result; return result;
} }
ast::type::Type* ParserImpl::GetComponentTypeForFormat(
ast::type::ImageFormat format) {
switch (format) {
case ast::type::ImageFormat::kR8Uint:
case ast::type::ImageFormat::kR16Uint:
case ast::type::ImageFormat::kRg8Uint:
case ast::type::ImageFormat::kR32Uint:
case ast::type::ImageFormat::kRg16Uint:
case ast::type::ImageFormat::kRgba8Uint:
case ast::type::ImageFormat::kRg32Uint:
case ast::type::ImageFormat::kRgba16Uint:
case ast::type::ImageFormat::kRgba32Uint:
return ast_module_.create<ast::type::U32>();
case ast::type::ImageFormat::kR8Sint:
case ast::type::ImageFormat::kR16Sint:
case ast::type::ImageFormat::kRg8Sint:
case ast::type::ImageFormat::kR32Sint:
case ast::type::ImageFormat::kRg16Sint:
case ast::type::ImageFormat::kRgba8Sint:
case ast::type::ImageFormat::kRg32Sint:
case ast::type::ImageFormat::kRgba16Sint:
case ast::type::ImageFormat::kRgba32Sint:
return ast_module_.create<ast::type::I32>();
case ast::type::ImageFormat::kR8Unorm:
case ast::type::ImageFormat::kRg8Unorm:
case ast::type::ImageFormat::kRgba8Unorm:
case ast::type::ImageFormat::kRgba8UnormSrgb:
case ast::type::ImageFormat::kBgra8Unorm:
case ast::type::ImageFormat::kBgra8UnormSrgb:
case ast::type::ImageFormat::kRgb10A2Unorm:
case ast::type::ImageFormat::kR8Snorm:
case ast::type::ImageFormat::kRg8Snorm:
case ast::type::ImageFormat::kRgba8Snorm:
case ast::type::ImageFormat::kR16Float:
case ast::type::ImageFormat::kR32Float:
case ast::type::ImageFormat::kRg16Float:
case ast::type::ImageFormat::kRg11B10Float:
case ast::type::ImageFormat::kRg32Float:
case ast::type::ImageFormat::kRgba16Float:
case ast::type::ImageFormat::kRgba32Float:
return ast_module_.create<ast::type::F32>();
default:
break;
}
Fail() << "unknown format " << int(format);
return nullptr;
}
ast::type::Type* ParserImpl::GetTexelTypeForFormat(
ast::type::ImageFormat format) {
auto* component_type = GetComponentTypeForFormat(format);
if (!component_type) {
return nullptr;
}
switch (format) {
case ast::type::ImageFormat::kR16Float:
case ast::type::ImageFormat::kR16Sint:
case ast::type::ImageFormat::kR16Uint:
case ast::type::ImageFormat::kR32Float:
case ast::type::ImageFormat::kR32Sint:
case ast::type::ImageFormat::kR32Uint:
case ast::type::ImageFormat::kR8Sint:
case ast::type::ImageFormat::kR8Snorm:
case ast::type::ImageFormat::kR8Uint:
case ast::type::ImageFormat::kR8Unorm:
// One channel
return component_type;
case ast::type::ImageFormat::kRg11B10Float:
case ast::type::ImageFormat::kRg16Float:
case ast::type::ImageFormat::kRg16Sint:
case ast::type::ImageFormat::kRg16Uint:
case ast::type::ImageFormat::kRg32Float:
case ast::type::ImageFormat::kRg32Sint:
case ast::type::ImageFormat::kRg32Uint:
case ast::type::ImageFormat::kRg8Sint:
case ast::type::ImageFormat::kRg8Snorm:
case ast::type::ImageFormat::kRg8Uint:
case ast::type::ImageFormat::kRg8Unorm:
// Two channels
return ast_module_.create<ast::type::Vector>(component_type, 2);
case ast::type::ImageFormat::kBgra8Unorm:
case ast::type::ImageFormat::kBgra8UnormSrgb:
case ast::type::ImageFormat::kRgb10A2Unorm:
case ast::type::ImageFormat::kRgba16Float:
case ast::type::ImageFormat::kRgba16Sint:
case ast::type::ImageFormat::kRgba16Uint:
case ast::type::ImageFormat::kRgba32Float:
case ast::type::ImageFormat::kRgba32Sint:
case ast::type::ImageFormat::kRgba32Uint:
case ast::type::ImageFormat::kRgba8Sint:
case ast::type::ImageFormat::kRgba8Snorm:
case ast::type::ImageFormat::kRgba8Uint:
case ast::type::ImageFormat::kRgba8Unorm:
case ast::type::ImageFormat::kRgba8UnormSrgb:
// Four channels
return ast_module_.create<ast::type::Vector>(component_type, 4);
default:
break;
}
Fail() << "unknown format: " << int(format);
return nullptr;
}
bool ParserImpl::RegisterHandleUsage() { bool ParserImpl::RegisterHandleUsage() {
if (!success_) { if (!success_) {
return false; return false;

View File

@ -427,6 +427,14 @@ class ParserImpl : Reader {
/// @returns the handle usage, or an empty usage object. /// @returns the handle usage, or an empty usage object.
Usage GetHandleUsage(uint32_t id) const; Usage GetHandleUsage(uint32_t id) const;
/// Returns the SPIR-V type for the sampler or image type for the given
/// variable in UniformConstant storage class. Returns null and emits an
/// error on failure.
/// @param var the OpVariable instruction
/// @returns the Tint AST type for the sampler or texture, or null on error
const spvtools::opt::Instruction* GetSpirvTypeForHandleVar(
const spvtools::opt::Instruction& var);
/// Returns the AST type for the pointer-to-sampler or pointer-to-texture type /// Returns the AST type for the pointer-to-sampler or pointer-to-texture type
/// for the given variable in UniformConstant storage class. Returns null and /// for the given variable in UniformConstant storage class. Returns null and
/// emits an error on failure. /// emits an error on failure.
@ -436,6 +444,17 @@ class ParserImpl : Reader {
ast::type::Pointer* GetTypeForHandleVar( ast::type::Pointer* GetTypeForHandleVar(
const spvtools::opt::Instruction& var); const spvtools::opt::Instruction& var);
/// Returns the channel component type corresponding to the given image
/// format.
/// @param format image texel format
/// @returns the component type, one of f32, i32, u32
ast::type::Type* GetComponentTypeForFormat(ast::type::ImageFormat format);
/// Returns texel type corresponding to the given image format.
/// @param format image texel format
/// @returns the texel format
ast::type::Type* GetTexelTypeForFormat(ast::type::ImageFormat format);
/// Returns the SPIR-V instruction with the given ID, or nullptr. /// Returns the SPIR-V instruction with the given ID, or nullptr.
/// @param id the SPIR-V result ID /// @param id the SPIR-V result ID
/// @returns the instruction, or nullptr on error /// @returns the instruction, or nullptr on error

View File

@ -49,17 +49,24 @@ std::string CommonBasicTypes() {
%uint = OpTypeInt 32 0 %uint = OpTypeInt 32 0
%int = OpTypeInt 32 1 %int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_3 = OpConstant %int 3 %int_3 = OpConstant %int 3
%int_4 = OpConstant %int 4 %int_4 = OpConstant %int 4
%uint_0 = OpConstant %uint 0 %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_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_100 = OpConstant %uint 100 %uint_100 = OpConstant %uint 100
%v2int = OpTypeVector %int 2 %v2int = OpTypeVector %int 2
%v2uint = OpTypeVector %uint 2 %v3int = OpTypeVector %int 3
%v4uint = OpTypeVector %uint 4
%v4int = OpTypeVector %int 4 %v4int = OpTypeVector %int 4
%v2uint = OpTypeVector %uint 2
%v3uint = OpTypeVector %uint 3
%v4uint = OpTypeVector %uint 4
%v2float = OpTypeVector %float 2 %v2float = OpTypeVector %float 2
%v3float = OpTypeVector %float 3 %v3float = OpTypeVector %float 3
%v4float = OpTypeVector %float 4 %v4float = OpTypeVector %float 4
@ -74,10 +81,19 @@ std::string CommonBasicTypes() {
%v3float_null = OpConstantNull %v3float %v3float_null = OpConstantNull %v3float
%v4float_null = OpConstantNull %v4float %v4float_null = OpConstantNull %v4float
%the_vi12 = OpConstantComposite %v2int %int_1 %int_2
%the_vi123 = OpConstantComposite %v3int %int_1 %int_2 %int_3
%the_vi1234 = OpConstantComposite %v4int %int_1 %int_2 %int_3 %int_4
%the_vu12 = OpConstantComposite %v2uint %uint_1 %uint_2
%the_vu123 = OpConstantComposite %v3uint %uint_1 %uint_2 %uint_3
%the_vu1234 = OpConstantComposite %v4uint %uint_1 %uint_2 %uint_3 %uint_4
%the_vf12 = OpConstantComposite %v2float %float_1 %float_2 %the_vf12 = OpConstantComposite %v2float %float_1 %float_2
%the_vf123 = OpConstantComposite %v3float %float_1 %float_2 %float_3 %the_vf123 = OpConstantComposite %v3float %float_1 %float_2 %float_3
%the_vf1234 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4 %the_vf1234 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
%depth = OpConstant %float 0.2 %depth = OpConstant %float 0.2
)"; )";
} }
@ -219,7 +235,7 @@ TEST_F(SpvParserTest,
%20 = OpConstantNull %ptr_f_texture_1d %20 = OpConstantNull %ptr_f_texture_1d
)"; )";
auto p = parser(test::Assemble(assembly)); auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildInternalModule()); ASSERT_TRUE(p->BuildInternalModule()) << assembly;
const auto* sampler = p->GetMemoryObjectDeclarationForHandle(10, false); const auto* sampler = p->GetMemoryObjectDeclarationForHandle(10, false);
const auto* image = p->GetMemoryObjectDeclarationForHandle(20, true); const auto* image = p->GetMemoryObjectDeclarationForHandle(20, true);
@ -1185,8 +1201,7 @@ INSTANTIATE_TEST_SUITE_P(Images,
__storage_texture_write_only_1d_rg32float __storage_texture_write_only_1d_rg32float
})"})); })"}));
// Test emission of variables when we have sampled image accesses in // Test emission of variables when we have image accesses in executable code.
// executable code.
struct ImageAccessCase { struct ImageAccessCase {
// SPIR-V image type, excluding result ID and opcode // SPIR-V image type, excluding result ID and opcode
@ -1203,10 +1218,10 @@ inline std::ostream& operator<<(std::ostream& out, const ImageAccessCase& c) {
return out; return out;
} }
using SpvParserTest_DeclHandle_SampledImage = using SpvParserTest_SampledImageAccessTest =
SpvParserTestBase<::testing::TestWithParam<ImageAccessCase>>; SpvParserTestBase<::testing::TestWithParam<ImageAccessCase>>;
TEST_P(SpvParserTest_DeclHandle_SampledImage, Variable) { TEST_P(SpvParserTest_SampledImageAccessTest, Variable) {
const auto assembly = Preamble() + R"( const auto assembly = Preamble() + R"(
OpEntryPoint Fragment %main "main" OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft OpExecutionMode %main OriginUpperLeft
@ -1281,7 +1296,7 @@ TEST_P(SpvParserTest_RegisterHandleUsage_SampledImage, DISABLED_FunctionParam) {
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
DISABLED_ImageGather, DISABLED_ImageGather,
SpvParserTest_DeclHandle_SampledImage, SpvParserTest_SampledImageAccessTest,
::testing::ValuesIn(std::vector<ImageAccessCase>{ ::testing::ValuesIn(std::vector<ImageAccessCase>{
// TODO(dneto): OpImageGather // TODO(dneto): OpImageGather
// TODO(dneto): OpImageGather with ConstOffset (signed and unsigned) // TODO(dneto): OpImageGather with ConstOffset (signed and unsigned)
@ -1291,7 +1306,7 @@ INSTANTIATE_TEST_SUITE_P(
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
DISABLED_ImageDrefGather, DISABLED_ImageDrefGather,
SpvParserTest_DeclHandle_SampledImage, SpvParserTest_SampledImageAccessTest,
::testing::ValuesIn(std::vector<ImageAccessCase>{ ::testing::ValuesIn(std::vector<ImageAccessCase>{
// TODO(dneto): OpImageDrefGather // TODO(dneto): OpImageDrefGather
// TODO(dneto): OpImageDrefGather with ConstOffset (signed and // TODO(dneto): OpImageDrefGather with ConstOffset (signed and
@ -1302,7 +1317,7 @@ INSTANTIATE_TEST_SUITE_P(
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
ImageSampleImplicitLod, ImageSampleImplicitLod,
SpvParserTest_DeclHandle_SampledImage, SpvParserTest_SampledImageAccessTest,
::testing::Values( ::testing::Values(
// OpImageSampleImplicitLod // OpImageSampleImplicitLod
@ -1627,7 +1642,7 @@ INSTANTIATE_TEST_SUITE_P(
// sampling and depth-refernce sampling. The texture is a depth-texture, // sampling and depth-refernce sampling. The texture is a depth-texture,
// and we use builtins textureSample and textureSampleCompare // and we use builtins textureSample and textureSampleCompare
ImageSampleImplicitLod_BothDrefAndNonDref, ImageSampleImplicitLod_BothDrefAndNonDref,
SpvParserTest_DeclHandle_SampledImage, SpvParserTest_SampledImageAccessTest,
::testing::Values( ::testing::Values(
// OpImageSampleImplicitLod // OpImageSampleImplicitLod
@ -1705,7 +1720,7 @@ INSTANTIATE_TEST_SUITE_P(
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
ImageSampleDrefImplicitLod, ImageSampleDrefImplicitLod,
SpvParserTest_DeclHandle_SampledImage, SpvParserTest_SampledImageAccessTest,
::testing::Values( ::testing::Values(
// ImageSampleDrefImplicitLod // ImageSampleDrefImplicitLod
ImageAccessCase{"%float 2D 0 0 0 1 Unknown", ImageAccessCase{"%float 2D 0 0 0 1 Unknown",
@ -1866,7 +1881,7 @@ INSTANTIATE_TEST_SUITE_P(
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
ImageSampleExplicitLod, ImageSampleExplicitLod,
SpvParserTest_DeclHandle_SampledImage, SpvParserTest_SampledImageAccessTest,
::testing::Values( ::testing::Values(
// OpImageSampleExplicitLod - using Lod // OpImageSampleExplicitLod - using Lod
@ -2206,7 +2221,7 @@ INSTANTIATE_TEST_SUITE_P(
// This corresponds to SPIR-V OpSampleExplicitLod and WGSL textureSampleLevel. // This corresponds to SPIR-V OpSampleExplicitLod and WGSL textureSampleLevel.
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
ImageSampleExplicitLod_DepthTexture, ImageSampleExplicitLod_DepthTexture,
SpvParserTest_DeclHandle_SampledImage, SpvParserTest_SampledImageAccessTest,
::testing::ValuesIn(std::vector<ImageAccessCase>{ ::testing::ValuesIn(std::vector<ImageAccessCase>{
// Test a non-depth case. // Test a non-depth case.
// (This is already tested above in the ImageSampleExplicitLod suite, // (This is already tested above in the ImageSampleExplicitLod suite,
@ -2280,6 +2295,551 @@ INSTANTIATE_TEST_SUITE_P(
) )
})"}})); })"}}));
using SpvParserTest_ImageAccessTest =
SpvParserTestBase<::testing::TestWithParam<ImageAccessCase>>;
TEST_P(SpvParserTest_ImageAccessTest, Variable) {
// In this test harness, we only create an image.
const auto assembly = Preamble() + R"(
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %f1 "f1"
OpName %vf12 "vf12"
OpName %vf123 "vf123"
OpName %vf1234 "vf1234"
OpName %u1 "u1"
OpName %vu12 "vu12"
OpName %vu123 "vu123"
OpName %vu1234 "vu1234"
OpName %i1 "i1"
OpName %vi12 "vi12"
OpName %vi123 "vi123"
OpName %vi1234 "vi1234"
OpName %offsets2d "offsets2d"
OpDecorate %20 DescriptorSet 2
OpDecorate %20 Binding 1
)" + CommonBasicTypes() +
R"(
%im_ty = OpTypeImage )" +
GetParam().spirv_image_type_details + R"(
%ptr_im_ty = OpTypePointer UniformConstant %im_ty
%20 = OpVariable %ptr_im_ty UniformConstant
%main = OpFunction %void None %voidfn
%entry = OpLabel
%f1 = OpCopyObject %float %float_1
%vf12 = OpCopyObject %v2float %the_vf12
%vf123 = OpCopyObject %v3float %the_vf123
%vf1234 = OpCopyObject %v4float %the_vf1234
%i1 = OpCopyObject %int %int_1
%vi12 = OpCopyObject %v2int %the_vi12
%vi123 = OpCopyObject %v3int %the_vi123
%vi1234 = OpCopyObject %v4int %the_vi1234
%u1 = OpCopyObject %uint %uint_1
%vu12 = OpCopyObject %v2uint %the_vu12
%vu123 = OpCopyObject %v3uint %the_vu123
%vu1234 = OpCopyObject %v4uint %the_vu1234
%value_offset = OpCompositeConstruct %v2int %int_3 %int_4
%offsets2d = OpCopyObject %v2int %value_offset
%im = OpLoad %im_ty %20
)" + GetParam().spirv_image_access +
R"(
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty()) << p->error();
const auto module = p->module().to_str();
EXPECT_THAT(module, HasSubstr(GetParam().var_decl))
<< "DECLARATIONS ARE BAD " << module;
EXPECT_THAT(module, HasSubstr(GetParam().texture_builtin))
<< "TEXTURE BUILTIN IS BAD " << module << assembly;
}
INSTANTIATE_TEST_SUITE_P(
ImageWrite_OptionalParams,
SpvParserTest_ImageAccessTest,
::testing::ValuesIn(std::vector<ImageAccessCase>{
// OpImageWrite with no extra params
{"%float 2D 0 0 0 2 Rgba32f", "OpImageWrite %im %vu12 %vf1234",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_rgba32float
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
Identifier[not set]{vf1234}
)
})"},
// OpImageWrite with ConstOffset
{"%float 2D 0 0 0 2 Rgba32f",
"OpImageWrite %im %vu12 %vf1234 ConstOffset %offsets2d",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_rgba32float
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
Identifier[not set]{vf1234}
Identifier[not set]{offsets2d}
)
})"}}));
INSTANTIATE_TEST_SUITE_P(
// SPIR-V's texel parameter is a 4-element vector with the component
// type matching the sampled type. WGSL's texel parameter might be
// scalar or vector, depending on the number of channels in the texture.
ImageWrite_ConvertTexelOperand_Arity,
SpvParserTest_ImageAccessTest,
::testing::ValuesIn(std::vector<ImageAccessCase>{
// Source 1 component, dest 1 component
{"%float 2D 0 0 0 2 R32f", "OpImageWrite %im %vu12 %f1",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_r32float
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
Identifier[not set]{f1}
)
})"},
// Source 2 component, dest 1 component
{"%float 2D 0 0 0 2 R32f", "OpImageWrite %im %vu12 %vf12",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_r32float
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
MemberAccessor[not set]{
Identifier[not set]{vf12}
Identifier[not set]{x}
}
)
})"},
// Source 3 component, dest 1 component
{"%float 2D 0 0 0 2 R32f", "OpImageWrite %im %vu12 %vf123",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_r32float
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
MemberAccessor[not set]{
Identifier[not set]{vf123}
Identifier[not set]{x}
}
)
})"},
// Source 4 component, dest 1 component
{"%float 2D 0 0 0 2 R32f", "OpImageWrite %im %vu12 %vf1234",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_r32float
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
MemberAccessor[not set]{
Identifier[not set]{vf1234}
Identifier[not set]{x}
}
)
})"},
// Source 2 component, dest 2 component
{"%float 2D 0 0 0 2 Rg32f", "OpImageWrite %im %vu12 %vf12",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_rg32float
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
Identifier[not set]{vf12}
)
})"},
// Source 3 component, dest 2 component
{"%float 2D 0 0 0 2 Rg32f", "OpImageWrite %im %vu12 %vf123",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_rg32float
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
MemberAccessor[not set]{
Identifier[not set]{vf123}
Identifier[not set]{xy}
}
)
})"},
// Source 4 component, dest 2 component
{"%float 2D 0 0 0 2 Rg32f", "OpImageWrite %im %vu12 %vf1234",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_rg32float
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
MemberAccessor[not set]{
Identifier[not set]{vf1234}
Identifier[not set]{xy}
}
)
})"},
// WGSL does not support 3-component storage textures.
// Source 4 component, dest 4 component
{"%float 2D 0 0 0 2 Rgba32f", "OpImageWrite %im %vu12 %vf1234",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_rgba32float
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
Identifier[not set]{vf1234}
)
})"}}));
TEST_F(SpvParserTest, ImageWrite_TooFewSrcTexelComponents_1_vs_4) {
const auto assembly = Preamble() + R"(
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %f1 "f1"
OpName %coords12 "coords12"
OpDecorate %20 DescriptorSet 2
OpDecorate %20 Binding 1
)" + CommonBasicTypes() +
R"(
%im_ty = OpTypeImage %void 2D 0 0 0 2 Rgba32f
%ptr_im_ty = OpTypePointer UniformConstant %im_ty
%20 = OpVariable %ptr_im_ty UniformConstant
%main = OpFunction %void None %voidfn
%entry = OpLabel
%f1 = OpCopyObject %float %float_1
%coords12 = OpCopyObject %v2float %the_vf12
%im = OpLoad %im_ty %20
OpImageWrite %im %coords12 %f1
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
EXPECT_FALSE(p->BuildAndParseInternalModule());
EXPECT_THAT(p->error(),
Eq("texel has too few components for storage texture: 1 provided "
"but 4 required, in: OpImageWrite %52 %3 %2"))
<< p->error();
}
TEST_F(SpvParserTest, ImageWrite_ThreeComponentStorageTexture_IsError) {
// SPIR-V doesn't allow a 3-element storage texture format.
const auto assembly = Preamble() + R"(
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %vf123 "vf123"
OpName %coords12 "coords12"
OpDecorate %20 DescriptorSet 2
OpDecorate %20 Binding 1
)" + CommonBasicTypes() +
R"(
%im_ty = OpTypeImage %void 2D 0 0 0 2 Rgb32f
%ptr_im_ty = OpTypePointer UniformConstant %im_ty
%20 = OpVariable %ptr_im_ty UniformConstant
%main = OpFunction %void None %voidfn
%entry = OpLabel
%vf123 = OpCopyObject %v3float %the_vf123
%coords12 = OpCopyObject %v2float %the_vf12
%im = OpLoad %im_ty %20
OpImageWrite %im %coords12 %vf123
OpReturn
OpFunctionEnd
)";
auto error = test::AssembleFailure(assembly);
EXPECT_THAT(error, HasSubstr("Invalid image format 'Rgb32f'"));
}
TEST_F(SpvParserTest, ImageWrite_FloatDest_IntegralSrc_IsError) {
const auto assembly = Preamble() + R"(
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %coords12 "coords12"
OpDecorate %20 DescriptorSet 2
OpDecorate %20 Binding 1
)" + CommonBasicTypes() +
R"(
%im_ty = OpTypeImage %void 2D 0 0 0 2 R32f
%ptr_im_ty = OpTypePointer UniformConstant %im_ty
%20 = OpVariable %ptr_im_ty UniformConstant
%main = OpFunction %void None %voidfn
%entry = OpLabel
%f1 = OpCopyObject %float %float_1
%coords12 = OpCopyObject %v2float %the_vf12
%im = OpLoad %im_ty %20
OpImageWrite %im %coords12 %uint_0
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
EXPECT_FALSE(p->BuildAndParseInternalModule());
EXPECT_THAT(p->error(),
Eq("can only write float or float vector to a storage image with "
"floating texel format: OpImageWrite %52 %2 %13"))
<< p->error();
}
TEST_F(SpvParserTest, ImageWrite_IntegralDest_FloatSrc_IsError) {
const auto assembly = Preamble() + R"(
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %coords12 "coords12"
OpDecorate %20 DescriptorSet 2
OpDecorate %20 Binding 1
)" + CommonBasicTypes() +
R"(
%im_ty = OpTypeImage %void 2D 0 0 0 2 R32ui
%ptr_im_ty = OpTypePointer UniformConstant %im_ty
%20 = OpVariable %ptr_im_ty UniformConstant
%main = OpFunction %void None %voidfn
%entry = OpLabel
%f1 = OpCopyObject %float %float_1
%coords12 = OpCopyObject %v2float %f1
%im = OpLoad %im_ty %20
OpImageWrite %im %coords12 %f1
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
EXPECT_FALSE(p->BuildAndParseInternalModule());
EXPECT_THAT(p->error(),
Eq("float or float vector can only be written to a storage image "
"with floating texel format: OpImageWrite %52 %2 %51"))
<< p->error();
}
INSTANTIATE_TEST_SUITE_P(
// Convert texel values when the sampled type of the texture is of the
// wrong signedness:
// unsigned int channel type -> signed int sampled texture
// signed int channel type -> unsigned int sampled texture
// (It is already a SPIR-V validation rule that floating point texels
// must already be used with textures of floating point sampled types)
ImageWrite_ConvertTexelOperand_Signedness,
SpvParserTest_ImageAccessTest,
::testing::ValuesIn(std::vector<ImageAccessCase>{
// Sampled type is unsigned int, texel is unsigned int
{"%uint 2D 0 0 0 2 Rgba32ui", "OpImageWrite %im %vu12 %vu1234",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_rgba32uint
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
Identifier[not set]{vu1234}
)
})"},
// Sampled type is unsigned int, texel is signed int
{"%uint 2D 0 0 0 2 Rgba32ui", "OpImageWrite %im %vu12 %vi1234",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_rgba32uint
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
Bitcast[not set]<__vec_4__u32>{
Identifier[not set]{vi1234}
}
)
})"},
// Sampled type is signed int, texel is unsigned int
{"%int 2D 0 0 0 2 Rgba32i", "OpImageWrite %im %vu12 %vu1234",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_rgba32sint
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
Bitcast[not set]<__vec_4__i32>{
Identifier[not set]{vu1234}
}
)
})"},
// Sampled type is signed int, texel is signed int
{"%int 2D 0 0 0 2 Rgba32i", "OpImageWrite %im %vu12 %vi1234",
R"(
DecoratedVariable{
Decorations{
SetDecoration{2}
BindingDecoration{1}
}
x_20
uniform_constant
__storage_texture_write_only_2d_rgba32sint
})",
R"(Call[not set]{
Identifier[not set]{textureStore}
(
Identifier[not set]{x_20}
Identifier[not set]{vu12}
Identifier[not set]{vi1234}
)
})"}}));
INSTANTIATE_TEST_SUITE_P(
// The SPIR-V result type could be integral but of different signedness
// than the sampled texel type. In these cases the result should be
// converted to match the signedness of the SPIR-V result type. This
// affects any instruction that yields texel values.
DISABLED_ImageAccess_ConvertResultSignedness,
SpvParserTest_ImageAccessTest,
::testing::ValuesIn(std::vector<ImageAccessCase>
// OpImageRead
// OpImageFetch
// OpImageGather
// OpImageSampleExplicitLod
// OpImageSampleImplicitLod
// In WGSL, depth-reference sampling only yields
// floating point results in WGSL.
{}));
struct ImageCoordsCase { struct ImageCoordsCase {
// SPIR-V image type, excluding result ID and opcode // SPIR-V image type, excluding result ID and opcode
std::string spirv_image_type_details; std::string spirv_image_type_details;
@ -2626,7 +3186,7 @@ INSTANTIATE_TEST_SUITE_P(BadInstructions,
{"%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 %9 %28", "%50 = OpCopyObject %9 %36",
{}}, {}},
{"%float 1D 0 0 0 1 Unknown", {"%float 1D 0 0 0 1 Unknown",
"OpStore %float_var %float_1", "OpStore %float_var %float_1",
@ -2645,36 +3205,36 @@ 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: %50 = " "bad or unsupported coordinate type for image access: %63 = "
"OpImageSampleImplicitLod %26 %49 %1", "OpImageSampleImplicitLod %34 %62 %1",
{}}, {}},
{"%float 1D 0 1 0 1 Unknown", // 1DArray {"%float 1D 0 1 0 1 Unknown", // 1DArray
"%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: %50 = OpImageSampleImplicitLod %26 %49 %3", "in: %63 = OpImageSampleImplicitLod %34 %62 %3",
{}}, {}},
{"%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: %50 = OpImageSampleImplicitLod %26 %49 %3", "in: %63 = OpImageSampleImplicitLod %34 %62 %3",
{}}, {}},
{"%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: %50 = OpImageSampleImplicitLod %26 %49 %4", "in: %63 = OpImageSampleImplicitLod %34 %62 %4",
{}}, {}},
{"%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: %50 = OpImageSampleImplicitLod %26 %49 %4", "in: %63 = OpImageSampleImplicitLod %34 %62 %4",
{}}, {}},
})); }));