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:
parent
2478ae78bb
commit
91ad3961df
|
@ -56,6 +56,7 @@
|
|||
#include "src/ast/type/depth_texture_type.h"
|
||||
#include "src/ast/type/f32_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/type.h"
|
||||
#include "src/ast/type/u32_type.h"
|
||||
|
@ -2665,12 +2666,8 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (IsSampledImageAccess(inst.opcode())) {
|
||||
return EmitSampledImageAccess(inst);
|
||||
}
|
||||
if (IsRawImageAccess(inst.opcode())) {
|
||||
return Fail() << "raw image access is not implemented yet:"
|
||||
<< inst.PrettyPrint();
|
||||
if (IsSampledImageAccess(inst.opcode()) || IsRawImageAccess(inst.opcode())) {
|
||||
return EmitImageAccess(inst);
|
||||
}
|
||||
|
||||
switch (inst.opcode()) {
|
||||
|
@ -2891,6 +2888,21 @@ ast::IdentifierExpression* FunctionEmitter::Swizzle(uint32_t i) {
|
|||
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(
|
||||
const spvtools::opt::Instruction& inst) {
|
||||
if (inst.NumInOperands() < 1) {
|
||||
|
@ -3626,30 +3638,47 @@ void FunctionEmitter::ApplySourceForInstruction(
|
|||
}
|
||||
}
|
||||
|
||||
bool FunctionEmitter::EmitSampledImageAccess(
|
||||
const spvtools::opt::Instruction& inst) {
|
||||
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();
|
||||
}
|
||||
|
||||
bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
|
||||
uint32_t arg_index = 0; // The SPIR-V input argument index
|
||||
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(
|
||||
create<ast::IdentifierExpression>(namer_.Name(image->result_id())));
|
||||
params.push_back(
|
||||
create<ast::IdentifierExpression>(namer_.Name(sampler->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(
|
||||
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.
|
||||
// TODO(dneto): For explicit-Lod variations, we may have to convert from
|
||||
|
@ -3662,7 +3691,9 @@ bool FunctionEmitter::EmitSampledImageAccess(
|
|||
return false;
|
||||
}
|
||||
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();
|
||||
|
||||
std::string builtin_name;
|
||||
|
@ -3688,6 +3719,23 @@ bool FunctionEmitter::EmitSampledImageAccess(
|
|||
return Fail() << " image gather is not yet supported";
|
||||
case SpvOpImageQueryLod:
|
||||
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:
|
||||
return Fail() << "internal error: sampled image access";
|
||||
}
|
||||
|
@ -3711,16 +3759,11 @@ bool FunctionEmitter::EmitSampledImageAccess(
|
|||
auto* lod_operand = MakeOperand(inst, arg_index).expr;
|
||||
// When sampling from a depth texture, the Lod operand must be an unsigned
|
||||
// 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>()) {
|
||||
// Convert it to an unsigned integer type.
|
||||
lod_operand = ast_module_.create<ast::TypeConstructorExpression>(
|
||||
ast_module_.create<ast::type::U32>(),
|
||||
ast::ExpressionList{lod_operand});
|
||||
}
|
||||
}
|
||||
if (texture_type->Is<ast::type::DepthTexture>()) {
|
||||
// Convert it to an unsigned integer type.
|
||||
lod_operand = ast_module_.create<ast::TypeConstructorExpression>(
|
||||
ast_module_.create<ast::type::U32>(),
|
||||
ast::ExpressionList{lod_operand});
|
||||
}
|
||||
params.push_back(lod_operand);
|
||||
image_operands_mask ^= SpvImageOperandsLodMask;
|
||||
|
@ -3747,7 +3790,18 @@ bool FunctionEmitter::EmitSampledImageAccess(
|
|||
|
||||
auto* ident = create<ast::IdentifierExpression>(builtin_name);
|
||||
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(
|
||||
|
@ -3828,10 +3882,11 @@ ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
|
|||
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->Is<ast::type::F32>()) {
|
||||
if (raw_coords.type->is_float_scalar() ||
|
||||
raw_coords.type->is_integer_scalar()) {
|
||||
num_coords_supplied = 1;
|
||||
} else if (raw_coords.type->Is<ast::type::Vector>()) {
|
||||
num_coords_supplied = raw_coords.type->As<ast::type::Vector>()->size();
|
||||
} else if (auto* vec_ty = raw_coords.type->As<ast::type::Vector>()) {
|
||||
num_coords_supplied = vec_ty->size();
|
||||
}
|
||||
if (num_coords_supplied == 0) {
|
||||
Fail() << "bad or unsupported coordinate type for image access: "
|
||||
|
@ -3845,20 +3900,15 @@ ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
|
|||
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.
|
||||
// TODO(dneto): Convert coordinate 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)));
|
||||
raw_coords.expr, PrefixSwizzle(num_axes)));
|
||||
|
||||
// Now get the 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
|
||||
// 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)));
|
||||
raw_coords.expr, PrefixSwizzle(num_axes)));
|
||||
}
|
||||
}
|
||||
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 reader
|
||||
} // namespace tint
|
||||
|
|
|
@ -680,6 +680,13 @@ class FunctionEmitter {
|
|||
/// @returns the identifier expression for the @p i'th component
|
||||
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
|
||||
/// (e.g. OpImageSampledImplicitLod) into an expression list consisting of
|
||||
/// 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);
|
||||
|
||||
/// 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
|
||||
/// @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
|
||||
/// or vectors. These translate directly to WGSL select. Otherwise, return
|
||||
|
|
|
@ -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) {
|
||||
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
|
||||
// several sources:
|
||||
// - the usage of the handle by image access instructions
|
||||
|
@ -1650,6 +1645,21 @@ ast::type::Pointer* ParserImpl::GetTypeForHandleVar(
|
|||
<< var.PrettyPrint();
|
||||
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.
|
||||
// Where possible, determine which one it is from the usage inferred
|
||||
|
@ -1790,6 +1800,115 @@ ast::type::Pointer* ParserImpl::GetTypeForHandleVar(
|
|||
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() {
|
||||
if (!success_) {
|
||||
return false;
|
||||
|
|
|
@ -427,6 +427,14 @@ class ParserImpl : Reader {
|
|||
/// @returns the handle usage, or an empty usage object.
|
||||
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
|
||||
/// for the given variable in UniformConstant storage class. Returns null and
|
||||
/// emits an error on failure.
|
||||
|
@ -436,6 +444,17 @@ class ParserImpl : Reader {
|
|||
ast::type::Pointer* GetTypeForHandleVar(
|
||||
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.
|
||||
/// @param id the SPIR-V result ID
|
||||
/// @returns the instruction, or nullptr on error
|
||||
|
|
|
@ -49,17 +49,24 @@ std::string CommonBasicTypes() {
|
|||
%uint = OpTypeInt 32 0
|
||||
%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_4 = OpConstant %int 4
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%uint_1 = OpConstant %uint 1
|
||||
%uint_2 = OpConstant %uint 2
|
||||
%uint_3 = OpConstant %uint 3
|
||||
%uint_4 = OpConstant %uint 4
|
||||
%uint_100 = OpConstant %uint 100
|
||||
|
||||
%v2int = OpTypeVector %int 2
|
||||
%v2uint = OpTypeVector %uint 2
|
||||
%v4uint = OpTypeVector %uint 4
|
||||
%v3int = OpTypeVector %int 3
|
||||
%v4int = OpTypeVector %int 4
|
||||
%v2uint = OpTypeVector %uint 2
|
||||
%v3uint = OpTypeVector %uint 3
|
||||
%v4uint = OpTypeVector %uint 4
|
||||
%v2float = OpTypeVector %float 2
|
||||
%v3float = OpTypeVector %float 3
|
||||
%v4float = OpTypeVector %float 4
|
||||
|
@ -74,10 +81,19 @@ std::string CommonBasicTypes() {
|
|||
%v3float_null = OpConstantNull %v3float
|
||||
%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_vf123 = OpConstantComposite %v3float %float_1 %float_2 %float_3
|
||||
%the_vf1234 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
|
||||
|
||||
|
||||
%depth = OpConstant %float 0.2
|
||||
)";
|
||||
}
|
||||
|
@ -219,7 +235,7 @@ TEST_F(SpvParserTest,
|
|||
%20 = OpConstantNull %ptr_f_texture_1d
|
||||
)";
|
||||
auto p = parser(test::Assemble(assembly));
|
||||
ASSERT_TRUE(p->BuildInternalModule());
|
||||
ASSERT_TRUE(p->BuildInternalModule()) << assembly;
|
||||
const auto* sampler = p->GetMemoryObjectDeclarationForHandle(10, false);
|
||||
const auto* image = p->GetMemoryObjectDeclarationForHandle(20, true);
|
||||
|
||||
|
@ -1185,8 +1201,7 @@ INSTANTIATE_TEST_SUITE_P(Images,
|
|||
__storage_texture_write_only_1d_rg32float
|
||||
})"}));
|
||||
|
||||
// Test emission of variables when we have sampled image accesses in
|
||||
// executable code.
|
||||
// Test emission of variables when we have image accesses in executable code.
|
||||
|
||||
struct ImageAccessCase {
|
||||
// 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;
|
||||
}
|
||||
|
||||
using SpvParserTest_DeclHandle_SampledImage =
|
||||
using SpvParserTest_SampledImageAccessTest =
|
||||
SpvParserTestBase<::testing::TestWithParam<ImageAccessCase>>;
|
||||
|
||||
TEST_P(SpvParserTest_DeclHandle_SampledImage, Variable) {
|
||||
TEST_P(SpvParserTest_SampledImageAccessTest, Variable) {
|
||||
const auto assembly = Preamble() + R"(
|
||||
OpEntryPoint Fragment %main "main"
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
|
@ -1281,7 +1296,7 @@ TEST_P(SpvParserTest_RegisterHandleUsage_SampledImage, DISABLED_FunctionParam) {
|
|||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
DISABLED_ImageGather,
|
||||
SpvParserTest_DeclHandle_SampledImage,
|
||||
SpvParserTest_SampledImageAccessTest,
|
||||
::testing::ValuesIn(std::vector<ImageAccessCase>{
|
||||
// TODO(dneto): OpImageGather
|
||||
// TODO(dneto): OpImageGather with ConstOffset (signed and unsigned)
|
||||
|
@ -1291,7 +1306,7 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
DISABLED_ImageDrefGather,
|
||||
SpvParserTest_DeclHandle_SampledImage,
|
||||
SpvParserTest_SampledImageAccessTest,
|
||||
::testing::ValuesIn(std::vector<ImageAccessCase>{
|
||||
// TODO(dneto): OpImageDrefGather
|
||||
// TODO(dneto): OpImageDrefGather with ConstOffset (signed and
|
||||
|
@ -1302,7 +1317,7 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ImageSampleImplicitLod,
|
||||
SpvParserTest_DeclHandle_SampledImage,
|
||||
SpvParserTest_SampledImageAccessTest,
|
||||
::testing::Values(
|
||||
|
||||
// OpImageSampleImplicitLod
|
||||
|
@ -1627,7 +1642,7 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
// sampling and depth-refernce sampling. The texture is a depth-texture,
|
||||
// and we use builtins textureSample and textureSampleCompare
|
||||
ImageSampleImplicitLod_BothDrefAndNonDref,
|
||||
SpvParserTest_DeclHandle_SampledImage,
|
||||
SpvParserTest_SampledImageAccessTest,
|
||||
::testing::Values(
|
||||
|
||||
// OpImageSampleImplicitLod
|
||||
|
@ -1705,7 +1720,7 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ImageSampleDrefImplicitLod,
|
||||
SpvParserTest_DeclHandle_SampledImage,
|
||||
SpvParserTest_SampledImageAccessTest,
|
||||
::testing::Values(
|
||||
// ImageSampleDrefImplicitLod
|
||||
ImageAccessCase{"%float 2D 0 0 0 1 Unknown",
|
||||
|
@ -1866,7 +1881,7 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ImageSampleExplicitLod,
|
||||
SpvParserTest_DeclHandle_SampledImage,
|
||||
SpvParserTest_SampledImageAccessTest,
|
||||
::testing::Values(
|
||||
|
||||
// OpImageSampleExplicitLod - using Lod
|
||||
|
@ -2206,7 +2221,7 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
// This corresponds to SPIR-V OpSampleExplicitLod and WGSL textureSampleLevel.
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ImageSampleExplicitLod_DepthTexture,
|
||||
SpvParserTest_DeclHandle_SampledImage,
|
||||
SpvParserTest_SampledImageAccessTest,
|
||||
::testing::ValuesIn(std::vector<ImageAccessCase>{
|
||||
// Test a non-depth case.
|
||||
// (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 {
|
||||
// SPIR-V image type, excluding result ID and opcode
|
||||
std::string spirv_image_type_details;
|
||||
|
@ -2626,7 +3186,7 @@ INSTANTIATE_TEST_SUITE_P(BadInstructions,
|
|||
{"%float 1D 0 0 0 1 Unknown",
|
||||
"%50 = OpCopyObject %float %float_1",
|
||||
"internal error: couldn't find image for "
|
||||
"%50 = OpCopyObject %9 %28",
|
||||
"%50 = OpCopyObject %9 %36",
|
||||
{}},
|
||||
{"%float 1D 0 0 0 1 Unknown",
|
||||
"OpStore %float_var %float_1",
|
||||
|
@ -2645,36 +3205,36 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
"%result = OpImageSampleImplicitLod "
|
||||
// bad type for coordinate: not a number
|
||||
"%v4float %sampled_image %float_var",
|
||||
"bad or unsupported coordinate type for image access: %50 = "
|
||||
"OpImageSampleImplicitLod %26 %49 %1",
|
||||
"bad or unsupported coordinate type for image access: %63 = "
|
||||
"OpImageSampleImplicitLod %34 %62 %1",
|
||||
{}},
|
||||
{"%float 1D 0 1 0 1 Unknown", // 1DArray
|
||||
"%result = OpImageSampleImplicitLod "
|
||||
// 1 component, but need 2
|
||||
"%v4float %sampled_image %f1",
|
||||
"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
|
||||
"%result = OpImageSampleImplicitLod "
|
||||
// 1 component, but need 2
|
||||
"%v4float %sampled_image %f1",
|
||||
"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
|
||||
"%result = OpImageSampleImplicitLod "
|
||||
// 2 component, but need 3
|
||||
"%v4float %sampled_image %vf12",
|
||||
"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
|
||||
"%result = OpImageSampleImplicitLod "
|
||||
// 2 components, but need 3
|
||||
"%v4float %sampled_image %vf12",
|
||||
"image access required 3 coordinate components, but only 2 provided, "
|
||||
"in: %50 = OpImageSampleImplicitLod %26 %49 %4",
|
||||
"in: %63 = OpImageSampleImplicitLod %34 %62 %4",
|
||||
{}},
|
||||
}));
|
||||
|
||||
|
|
Loading…
Reference in New Issue