spirv-reader: Register usage for handle vars and func parameters
Bug: tint:109 Change-Id: I03b684dc75bf20b95e0bd38a9c5e7b78836ec7db Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/33141 Commit-Queue: David Neto <dneto@google.com> Auto-Submit: David Neto <dneto@google.com> Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
parent
62c8f07015
commit
064882de2c
|
@ -57,10 +57,15 @@
|
|||
#include "src/ast/type/alias_type.h"
|
||||
#include "src/ast/type/array_type.h"
|
||||
#include "src/ast/type/bool_type.h"
|
||||
#include "src/ast/type/depth_texture_type.h"
|
||||
#include "src/ast/type/f32_type.h"
|
||||
#include "src/ast/type/i32_type.h"
|
||||
#include "src/ast/type/matrix_type.h"
|
||||
#include "src/ast/type/multisampled_texture_type.h"
|
||||
#include "src/ast/type/pointer_type.h"
|
||||
#include "src/ast/type/sampled_texture_type.h"
|
||||
#include "src/ast/type/sampler_type.h"
|
||||
#include "src/ast/type/storage_texture_type.h"
|
||||
#include "src/ast/type/struct_type.h"
|
||||
#include "src/ast/type/type.h"
|
||||
#include "src/ast/type/u32_type.h"
|
||||
|
@ -74,6 +79,7 @@
|
|||
#include "src/ast/variable_decoration.h"
|
||||
#include "src/reader/spirv/enum_converter.h"
|
||||
#include "src/reader/spirv/function.h"
|
||||
#include "src/reader/spirv/usage.h"
|
||||
#include "src/type_manager.h"
|
||||
|
||||
namespace tint {
|
||||
|
@ -438,6 +444,9 @@ bool ParserImpl::BuildInternalModule() {
|
|||
type_mgr_ = ir_context_->get_type_mgr();
|
||||
deco_mgr_ = ir_context_->get_decoration_mgr();
|
||||
|
||||
topologically_ordered_functions_ =
|
||||
FunctionTraverser(*module_).TopologicallyOrderedFunctions();
|
||||
|
||||
return success_;
|
||||
}
|
||||
|
||||
|
@ -524,6 +533,9 @@ bool ParserImpl::ParseInternalModuleExceptFunctions() {
|
|||
if (!RegisterEntryPoints()) {
|
||||
return false;
|
||||
}
|
||||
if (!RegisterHandleUsage()) {
|
||||
return false;
|
||||
}
|
||||
if (!RegisterTypes()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1479,8 +1491,7 @@ bool ParserImpl::EmitFunctions() {
|
|||
if (!success_) {
|
||||
return false;
|
||||
}
|
||||
for (const auto* f :
|
||||
FunctionTraverser(*module_).TopologicallyOrderedFunctions()) {
|
||||
for (const auto* f : topologically_ordered_functions_) {
|
||||
if (!success_) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1506,11 +1517,13 @@ bool ParserImpl::EmitFunctions() {
|
|||
const spvtools::opt::Instruction*
|
||||
ParserImpl::GetMemoryObjectDeclarationForHandle(uint32_t id,
|
||||
bool follow_image) {
|
||||
auto local_fail = [this, id,
|
||||
auto saved_id = id;
|
||||
auto local_fail = [this, saved_id, id,
|
||||
follow_image]() -> const spvtools::opt::Instruction* {
|
||||
const auto* inst = def_use_mgr_->GetDef(id);
|
||||
Fail() << "Could not find memory object declaration for the "
|
||||
<< (follow_image ? "image" : "sampler") << " underlying id " << id
|
||||
<< " (from original id " << saved_id << ") "
|
||||
<< (inst ? inst->PrettyPrint() : std::string());
|
||||
return nullptr;
|
||||
};
|
||||
|
@ -1589,6 +1602,159 @@ ParserImpl::GetMemoryObjectDeclarationForHandle(uint32_t id,
|
|||
}
|
||||
}
|
||||
|
||||
bool ParserImpl::RegisterHandleUsage() {
|
||||
if (!success_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Map a function ID to the list of its function parameter instructions, in
|
||||
// order.
|
||||
std::unordered_map<uint32_t, std::vector<const spvtools::opt::Instruction*>>
|
||||
function_params;
|
||||
for (const auto* f : topologically_ordered_functions_) {
|
||||
// Record the instructions defining this function's parameters.
|
||||
auto& params = function_params[f->result_id()];
|
||||
f->ForEachParam([¶ms](const spvtools::opt::Instruction* param) {
|
||||
params.push_back(param);
|
||||
});
|
||||
}
|
||||
|
||||
// Returns the memory object declaration for an image underlying the first
|
||||
// operand of the given image instruction.
|
||||
auto get_image = [this](const spvtools::opt::Instruction& image_inst) {
|
||||
return this->GetMemoryObjectDeclarationForHandle(
|
||||
image_inst.GetSingleWordInOperand(0), true);
|
||||
};
|
||||
// Returns the memory object declaration for a sampler underlying the first
|
||||
// operand of the given image instruction.
|
||||
auto get_sampler = [this](const spvtools::opt::Instruction& image_inst) {
|
||||
return this->GetMemoryObjectDeclarationForHandle(
|
||||
image_inst.GetSingleWordInOperand(0), false);
|
||||
};
|
||||
|
||||
// Scan the bodies of functions for image operations, recording their implied
|
||||
// usage properties on the memory object declarations (i.e. variables or
|
||||
// function parameters). We scan the functions in an order so that callees
|
||||
// precede callers. That way the usage on a function parameter is already
|
||||
// computed before we see the call to that function. So when we reach
|
||||
// a function call, we can add the usage from the callee formal parameters.
|
||||
for (const auto* f : topologically_ordered_functions_) {
|
||||
for (const auto& bb : *f) {
|
||||
for (const auto& inst : bb) {
|
||||
switch (inst.opcode()) {
|
||||
// Single texel reads and writes
|
||||
|
||||
case SpvOpImageRead:
|
||||
handle_usage_[get_image(inst)].AddStorageReadTexture();
|
||||
break;
|
||||
case SpvOpImageWrite:
|
||||
handle_usage_[get_image(inst)].AddStorageWriteTexture();
|
||||
break;
|
||||
case SpvOpImageFetch:
|
||||
handle_usage_[get_image(inst)].AddSampledTexture();
|
||||
break;
|
||||
|
||||
// Sampling and gathering from a sampled image.
|
||||
|
||||
case SpvOpImageSampleImplicitLod:
|
||||
case SpvOpImageSampleExplicitLod:
|
||||
case SpvOpImageSampleProjImplicitLod:
|
||||
case SpvOpImageSampleProjExplicitLod:
|
||||
case SpvOpImageGather:
|
||||
handle_usage_[get_image(inst)].AddSampledTexture();
|
||||
handle_usage_[get_sampler(inst)].AddSampler();
|
||||
break;
|
||||
case SpvOpImageSampleDrefImplicitLod:
|
||||
case SpvOpImageSampleDrefExplicitLod:
|
||||
case SpvOpImageSampleProjDrefImplicitLod:
|
||||
case SpvOpImageSampleProjDrefExplicitLod:
|
||||
case SpvOpImageDrefGather:
|
||||
// Depth reference access implies usage as a depth texture, which
|
||||
// in turn is a sampled texture.
|
||||
handle_usage_[get_image(inst)].AddDepthTexture();
|
||||
handle_usage_[get_sampler(inst)].AddComparisonSampler();
|
||||
break;
|
||||
|
||||
// Image queries
|
||||
|
||||
case SpvOpImageQuerySizeLod:
|
||||
case SpvOpImageQuerySize:
|
||||
// Applies to NonReadable, and hence a write-only storage image
|
||||
handle_usage_[get_image(inst)].AddStorageWriteTexture();
|
||||
break;
|
||||
case SpvOpImageQueryLod:
|
||||
handle_usage_[get_image(inst)].AddSampledTexture();
|
||||
handle_usage_[get_sampler(inst)].AddSampler();
|
||||
break;
|
||||
case SpvOpImageQueryLevels:
|
||||
// We can't tell anything more than that it's an image.
|
||||
handle_usage_[get_image(inst)].AddTexture();
|
||||
break;
|
||||
case SpvOpImageQuerySamples:
|
||||
handle_usage_[get_image(inst)].AddMultisampledTexture();
|
||||
break;
|
||||
|
||||
// Function calls
|
||||
|
||||
case SpvOpFunctionCall: {
|
||||
// Propagate handle usages from callee function formal parameters to
|
||||
// the matching caller parameters. This is where we rely on the
|
||||
// fact that callees have been processed earlier in the flow.
|
||||
const auto num_in_operands = inst.NumInOperands();
|
||||
// The first operand of the call is the function ID.
|
||||
// The remaining operands are the operands to the function.
|
||||
if (num_in_operands < 1) {
|
||||
return Fail() << "Call instruction must have at least one operand"
|
||||
<< inst.PrettyPrint();
|
||||
}
|
||||
const auto function_id = inst.GetSingleWordInOperand(0);
|
||||
const auto& formal_params = function_params[function_id];
|
||||
if (formal_params.size() != (num_in_operands - 1)) {
|
||||
return Fail() << "Called function has " << formal_params.size()
|
||||
<< " parameters, but function call has "
|
||||
<< (num_in_operands - 1) << " parameters"
|
||||
<< inst.PrettyPrint();
|
||||
}
|
||||
for (uint32_t i = 1; i < num_in_operands; ++i) {
|
||||
auto where = handle_usage_.find(formal_params[i - 1]);
|
||||
if (where == handle_usage_.end()) {
|
||||
// We haven't recorded any handle usage on the formal parameter.
|
||||
continue;
|
||||
}
|
||||
const Usage& formal_param_usage = where->second;
|
||||
const auto operand_id = inst.GetSingleWordInOperand(i);
|
||||
const auto* operand_as_sampler =
|
||||
GetMemoryObjectDeclarationForHandle(operand_id, false);
|
||||
const auto* operand_as_image =
|
||||
GetMemoryObjectDeclarationForHandle(operand_id, true);
|
||||
if (operand_as_sampler) {
|
||||
handle_usage_[operand_as_sampler].Add(formal_param_usage);
|
||||
}
|
||||
if (operand_as_image &&
|
||||
(operand_as_image != operand_as_sampler)) {
|
||||
handle_usage_[operand_as_image].Add(formal_param_usage);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return success_;
|
||||
}
|
||||
|
||||
Usage ParserImpl::GetHandleUsage(uint32_t id) const {
|
||||
const auto where = handle_usage_.find(def_use_mgr_->GetDef(id));
|
||||
if (where != handle_usage_.end()) {
|
||||
return where->second;
|
||||
}
|
||||
return Usage();
|
||||
}
|
||||
|
||||
} // namespace spirv
|
||||
} // namespace reader
|
||||
} // namespace tint
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "src/reader/spirv/enum_converter.h"
|
||||
#include "src/reader/spirv/fail_stream.h"
|
||||
#include "src/reader/spirv/namer.h"
|
||||
#include "src/reader/spirv/usage.h"
|
||||
#include "src/source.h"
|
||||
|
||||
namespace tint {
|
||||
|
@ -189,7 +190,8 @@ class ParserImpl : Reader {
|
|||
/// Builds the internal representation of the SPIR-V module.
|
||||
/// Assumes the module is somewhat well-formed. Normally you
|
||||
/// would want to validate the SPIR-V module before attempting
|
||||
/// to build this internal representation.
|
||||
/// to build this internal representation. Also computes a topological
|
||||
/// ordering of the functions.
|
||||
/// This is a no-op if the parser has already failed.
|
||||
/// @returns true if the parser is still successful.
|
||||
bool BuildInternalModule();
|
||||
|
@ -234,6 +236,12 @@ class ParserImpl : Reader {
|
|||
/// @returns true if parser is still successful.
|
||||
bool RegisterTypes();
|
||||
|
||||
/// Register sampler and texture usage for memory object declarations.
|
||||
/// This must be called after we've registered line numbers for all
|
||||
/// instructions. This is a no-op if the parser has already failed.
|
||||
/// @returns true if parser is still successful.
|
||||
bool RegisterHandleUsage();
|
||||
|
||||
/// Emit const definitions for scalar specialization constants generated
|
||||
/// by one of OpConstantTrue, OpConstantFalse, or OpSpecConstant.
|
||||
/// This is a no-op if the parser has already failed.
|
||||
|
@ -375,8 +383,8 @@ class ParserImpl : Reader {
|
|||
return scalar_spec_constants_.find(id) != scalar_spec_constants_.end();
|
||||
}
|
||||
|
||||
/// For a SPIR-V ID that defines a sampler, image, or sampled image value,
|
||||
/// return the SPIR-V instruction that represents the memory object
|
||||
/// For a SPIR-V ID that might define a sampler, image, or sampled image
|
||||
/// value, return the SPIR-V instruction that represents the memory object
|
||||
/// declaration for the object. If we encounter an OpSampledImage along the
|
||||
/// way, follow the image operand when follow_image is true; otherwise follow
|
||||
/// the sampler operand. Returns nullptr if we can't trace back to a memory
|
||||
|
@ -391,6 +399,12 @@ class ParserImpl : Reader {
|
|||
uint32_t id,
|
||||
bool follow_image);
|
||||
|
||||
/// Returns the handle usage for a memory object declaration.
|
||||
/// @param id SPIR-V ID of a sampler or image OpVariable or
|
||||
/// OpFunctionParameter
|
||||
/// @returns the handle usage, or an empty usage object.
|
||||
Usage GetHandleUsage(uint32_t id) const;
|
||||
|
||||
private:
|
||||
/// Converts a specific SPIR-V type to a Tint type. Integer case
|
||||
ast::type::Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty);
|
||||
|
@ -468,6 +482,9 @@ class ParserImpl : Reader {
|
|||
spvtools::opt::analysis::TypeManager* type_mgr_ = nullptr;
|
||||
spvtools::opt::analysis::DecorationManager* deco_mgr_ = nullptr;
|
||||
|
||||
// The functions ordered so that callees precede their callers.
|
||||
std::vector<const spvtools::opt::Function*> topologically_ordered_functions_;
|
||||
|
||||
// Maps an instruction to its source location. If no OpLine information
|
||||
// is in effect for the instruction, map the instruction to its position
|
||||
// in the SPIR-V module, counting by instructions, where the first
|
||||
|
@ -525,6 +542,10 @@ class ParserImpl : Reader {
|
|||
// GetMemoryObjectDeclarationForHandle.
|
||||
std::unordered_map<uint32_t, const spvtools::opt::Instruction*>
|
||||
mem_obj_decl_sampler_;
|
||||
|
||||
// Maps a memory-object-declaration instruction to any sampler or texture
|
||||
// usages implied by usages of the memory-object-declaration.
|
||||
std::unordered_map<const spvtools::opt::Instruction*, Usage> handle_usage_;
|
||||
};
|
||||
|
||||
} // namespace spirv
|
||||
|
|
|
@ -52,10 +52,20 @@ std::string CommonTypes() {
|
|||
%uint_2 = OpConstant %uint 2
|
||||
%uint_100 = OpConstant %uint 100
|
||||
|
||||
%v2uint = OpTypeVector %uint 2
|
||||
%v4uint = OpTypeVector %uint 4
|
||||
%v4int = OpTypeVector %int 4
|
||||
%v2float = OpTypeVector %float 2
|
||||
%v3float = OpTypeVector %float 3
|
||||
%v4float = OpTypeVector %float 4
|
||||
|
||||
%float_null = OpConstantNull %float
|
||||
%v2float_null = OpConstantNull %v2float
|
||||
%v3float_null = OpConstantNull %v3float
|
||||
%v4float_null = OpConstantNull %v4float
|
||||
|
||||
%depth = OpConstant %float 0.2
|
||||
|
||||
; Define types for all sampler and texture types that can map to WGSL,
|
||||
; modulo texel formats for storage textures. For now, we limit
|
||||
; ourselves to 2-channel 32-bit texel formats.
|
||||
|
@ -759,6 +769,293 @@ TEST_F(SpvParserTest, GetMemoryObjectDeclarationForHandle_FuncParam_Image) {
|
|||
EXPECT_EQ(image->result_id(), 20u);
|
||||
}
|
||||
|
||||
// Test RegisterHandleUsage, sampled image cases
|
||||
|
||||
struct SampledImageCase {
|
||||
std::string inst;
|
||||
std::string expected_sampler_usage;
|
||||
std::string expected_image_usage;
|
||||
};
|
||||
inline std::ostream& operator<<(std::ostream& out, const SampledImageCase& c) {
|
||||
out << "SampledImageCase(" << c.inst << ", " << c.expected_sampler_usage
|
||||
<< ", " << c.expected_image_usage << ")";
|
||||
return out;
|
||||
}
|
||||
|
||||
using SpvParserTest_RegisterHandleUsage_SampledImage =
|
||||
SpvParserTestBase<::testing::TestWithParam<SampledImageCase>>;
|
||||
|
||||
TEST_P(SpvParserTest_RegisterHandleUsage_SampledImage, Variable) {
|
||||
const auto assembly = Preamble() + CommonTypes() + R"(
|
||||
%si_ty = OpTypeSampledImage %f_texture_2d
|
||||
%coords = OpConstantNull %v2float
|
||||
|
||||
%10 = OpVariable %ptr_sampler UniformConstant
|
||||
%20 = OpVariable %ptr_f_texture_2d UniformConstant
|
||||
|
||||
%main = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
|
||||
%sam = OpLoad %sampler %10
|
||||
%im = OpLoad %f_texture_2d %20
|
||||
%sampled_image = OpSampledImage %si_ty %im %sam
|
||||
)" + GetParam().inst + R"(
|
||||
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
auto* p = parser(test::Assemble(assembly));
|
||||
ASSERT_TRUE(p->BuildInternalModule());
|
||||
EXPECT_TRUE(p->RegisterHandleUsage());
|
||||
EXPECT_TRUE(p->error().empty());
|
||||
Usage su = p->GetHandleUsage(10);
|
||||
Usage iu = p->GetHandleUsage(20);
|
||||
|
||||
EXPECT_THAT(su.to_str(), Eq(GetParam().expected_sampler_usage));
|
||||
EXPECT_THAT(iu.to_str(), Eq(GetParam().expected_image_usage));
|
||||
}
|
||||
|
||||
TEST_P(SpvParserTest_RegisterHandleUsage_SampledImage, FunctionParam) {
|
||||
const auto assembly = Preamble() + CommonTypes() + R"(
|
||||
%f_ty = OpTypeFunction %void %ptr_sampler %ptr_f_texture_2d
|
||||
%si_ty = OpTypeSampledImage %f_texture_2d
|
||||
%coords = OpConstantNull %v2float
|
||||
%component = OpConstant %uint 1
|
||||
|
||||
%10 = OpVariable %ptr_sampler UniformConstant
|
||||
%20 = OpVariable %ptr_f_texture_2d UniformConstant
|
||||
|
||||
%func = OpFunction %void None %f_ty
|
||||
%110 = OpFunctionParameter %ptr_sampler
|
||||
%120 = OpFunctionParameter %ptr_f_texture_2d
|
||||
%func_entry = OpLabel
|
||||
%sam = OpLoad %sampler %110
|
||||
%im = OpLoad %f_texture_2d %120
|
||||
%sampled_image = OpSampledImage %si_ty %im %sam
|
||||
|
||||
)" + GetParam().inst + R"(
|
||||
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
%main = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
%foo = OpFunctionCall %void %func %10 %20
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
auto* p = parser(test::Assemble(assembly));
|
||||
ASSERT_TRUE(p->BuildInternalModule()) << p->error() << assembly << std::endl;
|
||||
EXPECT_TRUE(p->RegisterHandleUsage()) << p->error() << assembly << std::endl;
|
||||
EXPECT_TRUE(p->error().empty()) << p->error() << assembly << std::endl;
|
||||
Usage su = p->GetHandleUsage(10);
|
||||
Usage iu = p->GetHandleUsage(20);
|
||||
|
||||
std::cout << p->GetHandleUsage(10) << std::endl;
|
||||
std::cout << p->GetHandleUsage(20) << std::endl;
|
||||
std::cout << p->GetHandleUsage(110) << std::endl;
|
||||
std::cout << p->GetHandleUsage(120) << std::endl;
|
||||
|
||||
EXPECT_THAT(su.to_str(), Eq(GetParam().expected_sampler_usage));
|
||||
EXPECT_THAT(iu.to_str(), Eq(GetParam().expected_image_usage));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
Samples,
|
||||
SpvParserTest_RegisterHandleUsage_SampledImage,
|
||||
::testing::Values(
|
||||
|
||||
// OpImageGather
|
||||
SampledImageCase{"%result = OpImageGather "
|
||||
"%v4float %sampled_image %coords %uint_1",
|
||||
"Usage(Sampler( ))", "Usage(Texture( is_sampled ))"},
|
||||
// OpImageDrefGather
|
||||
SampledImageCase{"%result = OpImageDrefGather "
|
||||
"%v4float %sampled_image %coords %depth",
|
||||
"Usage(Sampler( comparison ))",
|
||||
"Usage(Texture( is_sampled depth ))"},
|
||||
|
||||
// Sample the texture.
|
||||
|
||||
// OpImageSampleImplicitLod
|
||||
SampledImageCase{"%result = OpImageSampleImplicitLod "
|
||||
"%v4float %sampled_image %coords",
|
||||
"Usage(Sampler( ))", "Usage(Texture( is_sampled ))"},
|
||||
// OpImageSampleExplicitLod
|
||||
SampledImageCase{"%result = OpImageSampleExplicitLod "
|
||||
"%v4float %sampled_image %coords Lod %float_null",
|
||||
"Usage(Sampler( ))", "Usage(Texture( is_sampled ))"},
|
||||
// OpImageSampleDrefImplicitLod
|
||||
SampledImageCase{"%result = OpImageSampleDrefImplicitLod "
|
||||
"%v4float %sampled_image %coords %depth",
|
||||
"Usage(Sampler( comparison ))",
|
||||
"Usage(Texture( is_sampled depth ))"},
|
||||
// OpImageSampleDrefExplicitLod
|
||||
SampledImageCase{
|
||||
"%result = OpImageSampleDrefExplicitLod "
|
||||
"%v4float %sampled_image %coords %depth Lod %float_null",
|
||||
"Usage(Sampler( comparison ))",
|
||||
"Usage(Texture( is_sampled depth ))"},
|
||||
|
||||
// Sample the texture, with *Proj* variants, even though WGSL doesn't
|
||||
// support them.
|
||||
|
||||
// OpImageSampleProjImplicitLod
|
||||
SampledImageCase{"%result = OpImageSampleProjImplicitLod "
|
||||
"%v4float %sampled_image %coords",
|
||||
"Usage(Sampler( ))", "Usage(Texture( is_sampled ))"},
|
||||
// OpImageSampleProjExplicitLod
|
||||
SampledImageCase{"%result = OpImageSampleProjExplicitLod "
|
||||
"%v4float %sampled_image %coords Lod %float_null",
|
||||
"Usage(Sampler( ))", "Usage(Texture( is_sampled ))"},
|
||||
// OpImageSampleProjDrefImplicitLod
|
||||
SampledImageCase{"%result = OpImageSampleProjDrefImplicitLod "
|
||||
"%v4float %sampled_image %coords %depth",
|
||||
"Usage(Sampler( comparison ))",
|
||||
"Usage(Texture( is_sampled depth ))"},
|
||||
// OpImageSampleProjDrefExplicitLod
|
||||
SampledImageCase{
|
||||
"%result = OpImageSampleProjDrefExplicitLod "
|
||||
"%v4float %sampled_image %coords %depth Lod %float_null",
|
||||
"Usage(Sampler( comparison ))",
|
||||
"Usage(Texture( is_sampled depth ))"},
|
||||
|
||||
// OpImageQueryLod
|
||||
SampledImageCase{
|
||||
"%result = OpImageQueryLod %v2float %sampled_image %coords",
|
||||
"Usage(Sampler( ))", "Usage(Texture( is_sampled ))"}
|
||||
|
||||
));
|
||||
|
||||
// Test RegisterHandleUsage, raw image cases.
|
||||
// For these we test the use of an image value directly, and not combined
|
||||
// with the sampler. The image still could be of sampled image type.
|
||||
|
||||
struct RawImageCase {
|
||||
std::string type; // Example: f_storage_1d or f_texture_1d
|
||||
std::string inst;
|
||||
std::string expected_image_usage;
|
||||
};
|
||||
inline std::ostream& operator<<(std::ostream& out, const RawImageCase& c) {
|
||||
out << "RawImageCase(" << c.type << ", " << c.inst << ", "
|
||||
<< c.expected_image_usage << ")";
|
||||
return out;
|
||||
}
|
||||
|
||||
using SpvParserTest_RegisterHandleUsage_RawImage =
|
||||
SpvParserTestBase<::testing::TestWithParam<RawImageCase>>;
|
||||
|
||||
TEST_P(SpvParserTest_RegisterHandleUsage_RawImage, Variable) {
|
||||
const auto assembly = Preamble() + CommonTypes() + R"(
|
||||
%20 = OpVariable %ptr_)" +
|
||||
GetParam().type + R"( UniformConstant
|
||||
|
||||
%main = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
|
||||
%im = OpLoad %)" + GetParam().type +
|
||||
R"( %20
|
||||
)" + GetParam().inst + R"(
|
||||
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
auto* p = parser(test::Assemble(assembly));
|
||||
ASSERT_TRUE(p->BuildInternalModule());
|
||||
EXPECT_TRUE(p->RegisterHandleUsage());
|
||||
EXPECT_TRUE(p->error().empty());
|
||||
|
||||
Usage iu = p->GetHandleUsage(20);
|
||||
EXPECT_THAT(iu.to_str(), Eq(GetParam().expected_image_usage));
|
||||
|
||||
Usage su = p->GetHandleUsage(20);
|
||||
}
|
||||
|
||||
TEST_P(SpvParserTest_RegisterHandleUsage_RawImage, FunctionParam) {
|
||||
const auto assembly = Preamble() + CommonTypes() + R"(
|
||||
%f_ty = OpTypeFunction %void %ptr_)" +
|
||||
GetParam().type + R"(
|
||||
|
||||
%20 = OpVariable %ptr_)" +
|
||||
GetParam().type + R"( UniformConstant
|
||||
|
||||
%func = OpFunction %void None %f_ty
|
||||
%i_param = OpFunctionParameter %ptr_)" +
|
||||
GetParam().type + R"(
|
||||
%func_entry = OpLabel
|
||||
%im = OpLoad %)" + GetParam().type +
|
||||
R"( %i_param
|
||||
|
||||
)" + GetParam().inst + R"(
|
||||
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
%main = OpFunction %void None %voidfn
|
||||
%entry = OpLabel
|
||||
%foo = OpFunctionCall %void %func %20
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
auto* p = parser(test::Assemble(assembly));
|
||||
ASSERT_TRUE(p->BuildInternalModule());
|
||||
EXPECT_TRUE(p->RegisterHandleUsage());
|
||||
EXPECT_TRUE(p->error().empty());
|
||||
Usage iu = p->GetHandleUsage(20);
|
||||
|
||||
EXPECT_THAT(iu.to_str(), Eq(GetParam().expected_image_usage));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
Samples,
|
||||
SpvParserTest_RegisterHandleUsage_RawImage,
|
||||
::testing::Values(
|
||||
|
||||
// OpImageRead
|
||||
RawImageCase{"f_storage_1d",
|
||||
"%result = OpImageRead %v4float %im %uint_1",
|
||||
"Usage(Texture( read ))"},
|
||||
|
||||
// OpImageWrite
|
||||
RawImageCase{"f_storage_1d", "OpImageWrite %im %uint_1 %v4float_null",
|
||||
"Usage(Texture( write ))"},
|
||||
|
||||
// OpImageFetch
|
||||
RawImageCase{"f_texture_1d",
|
||||
"%result = OpImageFetch "
|
||||
"%v4float %im %float_null",
|
||||
"Usage(Texture( is_sampled ))"},
|
||||
|
||||
// Image queries
|
||||
|
||||
// OpImageQuerySizeLod
|
||||
// Applies to NonReadable, hence write-only storage
|
||||
RawImageCase{"f_storage_2d",
|
||||
"%result = OpImageQuerySizeLod "
|
||||
"%v2uint %im %uint_1",
|
||||
"Usage(Texture( write ))"},
|
||||
|
||||
// OpImageQuerySize
|
||||
// Applies to NonReadable, hence write-only storage
|
||||
RawImageCase{"f_storage_2d",
|
||||
"%result = OpImageQuerySize "
|
||||
"%v2uint %im",
|
||||
"Usage(Texture( write ))"},
|
||||
|
||||
// OpImageQueryLevels
|
||||
RawImageCase{"f_texture_2d",
|
||||
"%result = OpImageQueryLevels "
|
||||
"%uint %im",
|
||||
"Usage(Texture( ))"},
|
||||
|
||||
// OpImageQuerySamples
|
||||
RawImageCase{"f_texture_2d_ms",
|
||||
"%result = OpImageQuerySamples "
|
||||
"%uint %im",
|
||||
"Usage(Texture( is_sampled ms ))"}
|
||||
|
||||
));
|
||||
|
||||
} // namespace
|
||||
} // namespace spirv
|
||||
} // namespace reader
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "src/reader/spirv/usage.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace tint {
|
||||
namespace reader {
|
||||
namespace spirv {
|
||||
|
@ -181,6 +183,12 @@ void Usage::AddDepthTexture() {
|
|||
is_depth_ = true;
|
||||
}
|
||||
|
||||
std::string Usage::to_str() const {
|
||||
std::ostringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // namespace spirv
|
||||
} // namespace reader
|
||||
} // namespace tint
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#define SRC_READER_SPIRV_USAGE_H_
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
namespace tint {
|
||||
namespace reader {
|
||||
|
@ -102,6 +103,9 @@ class Usage {
|
|||
/// Records usage as a depth texture.
|
||||
void AddDepthTexture();
|
||||
|
||||
/// Returns this usage object as a string.
|
||||
std::string to_str() const;
|
||||
|
||||
private:
|
||||
// Sampler properties.
|
||||
bool is_sampler_ = false;
|
||||
|
|
Loading…
Reference in New Issue