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/alias_type.h"
|
||||||
#include "src/ast/type/array_type.h"
|
#include "src/ast/type/array_type.h"
|
||||||
#include "src/ast/type/bool_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/f32_type.h"
|
||||||
#include "src/ast/type/i32_type.h"
|
#include "src/ast/type/i32_type.h"
|
||||||
#include "src/ast/type/matrix_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/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/struct_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"
|
||||||
|
@ -74,6 +79,7 @@
|
||||||
#include "src/ast/variable_decoration.h"
|
#include "src/ast/variable_decoration.h"
|
||||||
#include "src/reader/spirv/enum_converter.h"
|
#include "src/reader/spirv/enum_converter.h"
|
||||||
#include "src/reader/spirv/function.h"
|
#include "src/reader/spirv/function.h"
|
||||||
|
#include "src/reader/spirv/usage.h"
|
||||||
#include "src/type_manager.h"
|
#include "src/type_manager.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
|
@ -438,6 +444,9 @@ bool ParserImpl::BuildInternalModule() {
|
||||||
type_mgr_ = ir_context_->get_type_mgr();
|
type_mgr_ = ir_context_->get_type_mgr();
|
||||||
deco_mgr_ = ir_context_->get_decoration_mgr();
|
deco_mgr_ = ir_context_->get_decoration_mgr();
|
||||||
|
|
||||||
|
topologically_ordered_functions_ =
|
||||||
|
FunctionTraverser(*module_).TopologicallyOrderedFunctions();
|
||||||
|
|
||||||
return success_;
|
return success_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,6 +533,9 @@ bool ParserImpl::ParseInternalModuleExceptFunctions() {
|
||||||
if (!RegisterEntryPoints()) {
|
if (!RegisterEntryPoints()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!RegisterHandleUsage()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!RegisterTypes()) {
|
if (!RegisterTypes()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1479,8 +1491,7 @@ bool ParserImpl::EmitFunctions() {
|
||||||
if (!success_) {
|
if (!success_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (const auto* f :
|
for (const auto* f : topologically_ordered_functions_) {
|
||||||
FunctionTraverser(*module_).TopologicallyOrderedFunctions()) {
|
|
||||||
if (!success_) {
|
if (!success_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1506,11 +1517,13 @@ bool ParserImpl::EmitFunctions() {
|
||||||
const spvtools::opt::Instruction*
|
const spvtools::opt::Instruction*
|
||||||
ParserImpl::GetMemoryObjectDeclarationForHandle(uint32_t id,
|
ParserImpl::GetMemoryObjectDeclarationForHandle(uint32_t id,
|
||||||
bool follow_image) {
|
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* {
|
follow_image]() -> const spvtools::opt::Instruction* {
|
||||||
const auto* inst = def_use_mgr_->GetDef(id);
|
const auto* inst = def_use_mgr_->GetDef(id);
|
||||||
Fail() << "Could not find memory object declaration for the "
|
Fail() << "Could not find memory object declaration for the "
|
||||||
<< (follow_image ? "image" : "sampler") << " underlying id " << id
|
<< (follow_image ? "image" : "sampler") << " underlying id " << id
|
||||||
|
<< " (from original id " << saved_id << ") "
|
||||||
<< (inst ? inst->PrettyPrint() : std::string());
|
<< (inst ? inst->PrettyPrint() : std::string());
|
||||||
return nullptr;
|
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 spirv
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "src/reader/spirv/enum_converter.h"
|
#include "src/reader/spirv/enum_converter.h"
|
||||||
#include "src/reader/spirv/fail_stream.h"
|
#include "src/reader/spirv/fail_stream.h"
|
||||||
#include "src/reader/spirv/namer.h"
|
#include "src/reader/spirv/namer.h"
|
||||||
|
#include "src/reader/spirv/usage.h"
|
||||||
#include "src/source.h"
|
#include "src/source.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
|
@ -189,7 +190,8 @@ class ParserImpl : Reader {
|
||||||
/// Builds the internal representation of the SPIR-V module.
|
/// Builds the internal representation of the SPIR-V module.
|
||||||
/// Assumes the module is somewhat well-formed. Normally you
|
/// Assumes the module is somewhat well-formed. Normally you
|
||||||
/// would want to validate the SPIR-V module before attempting
|
/// 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.
|
/// This is a no-op if the parser has already failed.
|
||||||
/// @returns true if the parser is still successful.
|
/// @returns true if the parser is still successful.
|
||||||
bool BuildInternalModule();
|
bool BuildInternalModule();
|
||||||
|
@ -234,6 +236,12 @@ class ParserImpl : Reader {
|
||||||
/// @returns true if parser is still successful.
|
/// @returns true if parser is still successful.
|
||||||
bool RegisterTypes();
|
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
|
/// Emit const definitions for scalar specialization constants generated
|
||||||
/// by one of OpConstantTrue, OpConstantFalse, or OpSpecConstant.
|
/// by one of OpConstantTrue, OpConstantFalse, or OpSpecConstant.
|
||||||
/// This is a no-op if the parser has already failed.
|
/// 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();
|
return scalar_spec_constants_.find(id) != scalar_spec_constants_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For a SPIR-V ID that defines a sampler, image, or sampled image value,
|
/// For a SPIR-V ID that might define a sampler, image, or sampled image
|
||||||
/// return the SPIR-V instruction that represents the memory object
|
/// value, return the SPIR-V instruction that represents the memory object
|
||||||
/// declaration for the object. If we encounter an OpSampledImage along the
|
/// declaration for the object. If we encounter an OpSampledImage along the
|
||||||
/// way, follow the image operand when follow_image is true; otherwise follow
|
/// 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
|
/// the sampler operand. Returns nullptr if we can't trace back to a memory
|
||||||
|
@ -391,6 +399,12 @@ class ParserImpl : Reader {
|
||||||
uint32_t id,
|
uint32_t id,
|
||||||
bool follow_image);
|
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:
|
private:
|
||||||
/// Converts a specific SPIR-V type to a Tint type. Integer case
|
/// Converts a specific SPIR-V type to a Tint type. Integer case
|
||||||
ast::type::Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty);
|
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::TypeManager* type_mgr_ = nullptr;
|
||||||
spvtools::opt::analysis::DecorationManager* deco_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
|
// Maps an instruction to its source location. If no OpLine information
|
||||||
// is in effect for the instruction, map the instruction to its position
|
// is in effect for the instruction, map the instruction to its position
|
||||||
// in the SPIR-V module, counting by instructions, where the first
|
// in the SPIR-V module, counting by instructions, where the first
|
||||||
|
@ -525,6 +542,10 @@ class ParserImpl : Reader {
|
||||||
// GetMemoryObjectDeclarationForHandle.
|
// GetMemoryObjectDeclarationForHandle.
|
||||||
std::unordered_map<uint32_t, const spvtools::opt::Instruction*>
|
std::unordered_map<uint32_t, const spvtools::opt::Instruction*>
|
||||||
mem_obj_decl_sampler_;
|
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
|
} // namespace spirv
|
||||||
|
|
|
@ -52,10 +52,20 @@ std::string CommonTypes() {
|
||||||
%uint_2 = OpConstant %uint 2
|
%uint_2 = OpConstant %uint 2
|
||||||
%uint_100 = OpConstant %uint 100
|
%uint_100 = OpConstant %uint 100
|
||||||
|
|
||||||
|
%v2uint = OpTypeVector %uint 2
|
||||||
%v4uint = OpTypeVector %uint 4
|
%v4uint = OpTypeVector %uint 4
|
||||||
%v4int = OpTypeVector %int 4
|
%v4int = OpTypeVector %int 4
|
||||||
|
%v2float = OpTypeVector %float 2
|
||||||
|
%v3float = OpTypeVector %float 3
|
||||||
%v4float = OpTypeVector %float 4
|
%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,
|
; Define types for all sampler and texture types that can map to WGSL,
|
||||||
; modulo texel formats for storage textures. For now, we limit
|
; modulo texel formats for storage textures. For now, we limit
|
||||||
; ourselves to 2-channel 32-bit texel formats.
|
; ourselves to 2-channel 32-bit texel formats.
|
||||||
|
@ -759,6 +769,293 @@ TEST_F(SpvParserTest, GetMemoryObjectDeclarationForHandle_FuncParam_Image) {
|
||||||
EXPECT_EQ(image->result_id(), 20u);
|
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
|
||||||
} // namespace spirv
|
} // namespace spirv
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include "src/reader/spirv/usage.h"
|
#include "src/reader/spirv/usage.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace reader {
|
namespace reader {
|
||||||
namespace spirv {
|
namespace spirv {
|
||||||
|
@ -181,6 +183,12 @@ void Usage::AddDepthTexture() {
|
||||||
is_depth_ = true;
|
is_depth_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Usage::to_str() const {
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << *this;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace spirv
|
} // namespace spirv
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#define SRC_READER_SPIRV_USAGE_H_
|
#define SRC_READER_SPIRV_USAGE_H_
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace reader {
|
namespace reader {
|
||||||
|
@ -102,6 +103,9 @@ class Usage {
|
||||||
/// Records usage as a depth texture.
|
/// Records usage as a depth texture.
|
||||||
void AddDepthTexture();
|
void AddDepthTexture();
|
||||||
|
|
||||||
|
/// Returns this usage object as a string.
|
||||||
|
std::string to_str() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Sampler properties.
|
// Sampler properties.
|
||||||
bool is_sampler_ = false;
|
bool is_sampler_ = false;
|
||||||
|
|
Loading…
Reference in New Issue