Handle complex cases in Inspector::GenerateSamplerTargets

This code was implicitly assuming that all resources it was looking
for would be directly referenced at the intrinsic callsite, and not
passed via function parameters.

This was causing a crash in more complex cases.

The inspector code has been updated to handle cases where the
resources are not being directly referenced.

Unneeded calls to GenerateSamplerTargets() are removed.

Utility function GetOriginatingResources() is added to handle walking up
call sites to resolve resources.

Text shader based test runner is added to the Inspector tests to make
expressing complex tests easier.

BUG=tint:967

Change-Id: I2ecb6d57c518003da59f38b261bae4d62ce7e6ac
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/59340
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ryan Harrison 2021-07-27 15:42:51 +00:00 committed by Tint LUCI CQ
parent dffa60ca98
commit dee93c6b9a
7 changed files with 491 additions and 71 deletions

View File

@ -632,6 +632,8 @@ if(${TINT_BUILD_TESTS})
inspector/inspector_test.cc inspector/inspector_test.cc
inspector/test_inspector_builder.cc inspector/test_inspector_builder.cc
inspector/test_inspector_builder.h inspector/test_inspector_builder.h
inspector/test_inspector_runner.cc
inspector/test_inspector_runner.h
intrinsic_table_test.cc intrinsic_table_test.cc
program_test.cc program_test.cc
resolver/assignment_validation_test.cc resolver/assignment_validation_test.cc

View File

@ -44,6 +44,7 @@
#include "src/sem/vector_type.h" #include "src/sem/vector_type.h"
#include "src/sem/void_type.h" #include "src/sem/void_type.h"
#include "src/utils/math.h" #include "src/utils/math.h"
#include "src/utils/unique_vector.h"
namespace tint { namespace tint {
namespace inspector { namespace inspector {
@ -394,8 +395,6 @@ std::vector<ResourceBinding> Inspector::GetSamplerResourceBindings(
return {}; return {};
} }
GenerateSamplerTargets();
std::vector<ResourceBinding> result; std::vector<ResourceBinding> result;
auto* func_sem = program_->Sem().Get(func); auto* func_sem = program_->Sem().Get(func);
@ -420,8 +419,6 @@ std::vector<ResourceBinding> Inspector::GetComparisonSamplerResourceBindings(
return {}; return {};
} }
GenerateSamplerTargets();
std::vector<ResourceBinding> result; std::vector<ResourceBinding> result;
auto* func_sem = program_->Sem().Get(func); auto* func_sem = program_->Sem().Get(func);
@ -809,23 +806,132 @@ void Inspector::GenerateSamplerTargets() {
continue; continue;
} }
auto* s = c->params()[sampler_index];
auto* sampler = sem.Get<sem::VariableUser>(s)->Variable();
sem::BindingPoint sampler_binding_point = {
sampler->Declaration()->binding_point().group->value(),
sampler->Declaration()->binding_point().binding->value()};
auto* t = c->params()[texture_index]; auto* t = c->params()[texture_index];
auto* texture = sem.Get<sem::VariableUser>(t)->Variable(); auto* s = c->params()[sampler_index];
GetOriginatingResources(
std::array<const ast::Expression*, 2>{t, s},
[&](std::array<const sem::GlobalVariable*, 2> globals) {
auto* texture = globals[0];
sem::BindingPoint texture_binding_point = { sem::BindingPoint texture_binding_point = {
texture->Declaration()->binding_point().group->value(), texture->Declaration()->binding_point().group->value(),
texture->Declaration()->binding_point().binding->value()}; texture->Declaration()->binding_point().binding->value()};
auto* sampler = globals[1];
sem::BindingPoint sampler_binding_point = {
sampler->Declaration()->binding_point().group->value(),
sampler->Declaration()->binding_point().binding->value()};
for (auto entry_point : entry_points) { for (auto entry_point : entry_points) {
const auto& ep_name = program_->Symbols().NameFor(entry_point); const auto& ep_name = program_->Symbols().NameFor(entry_point);
(*sampler_targets_)[ep_name].add( (*sampler_targets_)[ep_name].add(
{sampler_binding_point, texture_binding_point}); {sampler_binding_point, texture_binding_point});
} }
});
}
}
template <size_t N, typename F>
void Inspector::GetOriginatingResources(
std::array<const ast::Expression*, N> exprs,
F&& callback) {
if (!program_->IsValid()) {
TINT_ICE(Inspector, diagnostics_)
<< "attempting to get originating resources in invalid program";
return;
}
auto& sem = program_->Sem();
std::array<const sem::GlobalVariable*, N> globals{};
std::array<const sem::Parameter*, N> parameters{};
UniqueVector<const ast::CallExpression*> callsites;
for (size_t i = 0; i < N; i++) {
auto*& expr = exprs[i];
// Resolve each of the expressions
while (true) {
if (auto* user = sem.Get<sem::VariableUser>(expr)) {
auto* var = user->Variable();
if (auto* global = tint::As<sem::GlobalVariable>(var)) {
// Found the global resource declaration.
globals[i] = global;
break; // Done with this expression.
}
if (auto* local = tint::As<sem::LocalVariable>(var)) {
// Chase the variable
expr = local->Declaration()->constructor();
if (!expr) {
TINT_ICE(Inspector, diagnostics_)
<< "resource variable had no initializer";
return;
}
continue; // Continue chasing the expression in this function
}
if (auto* param = tint::As<sem::Parameter>(var)) {
// Gather each of the callers of this function
auto* func = tint::As<sem::Function>(param->Owner());
if (func->CallSites().empty()) {
// One or more of the expressions is a parameter, but this function
// is not called. Ignore.
return;
}
for (auto* call_expr : func->CallSites()) {
callsites.add(call_expr);
}
// Need to evaluate each function call with the group of
// expressions, so move on to the next expression.
parameters[i] = param;
break;
}
TINT_ICE(Inspector, diagnostics_)
<< "unexpected variable type " << var->TypeInfo().name;
}
if (auto* unary = tint::As<ast::UnaryOpExpression>(expr)) {
switch (unary->op()) {
case ast::UnaryOp::kAddressOf:
case ast::UnaryOp::kIndirection:
// `*` and `&` are the only valid unary ops for a resource type,
// and must be balanced in order for the program to have passed
// validation. Just skip past these.
expr = unary->expr();
continue;
default: {
TINT_ICE(Inspector, diagnostics_)
<< "unexpected unary op on resource: " << unary->op();
return;
}
}
}
TINT_ICE(Inspector, diagnostics_)
<< "cannot resolve originating resource with expression type "
<< expr->TypeInfo().name;
return;
}
}
if (callsites.size()) {
for (auto* call_expr : callsites) {
// Make a copy of the expressions for this callsite
std::array<const ast::Expression*, N> call_exprs = exprs;
// Patch all the parameter expressions with their argument
for (size_t i = 0; i < N; i++) {
if (auto* param = parameters[i]) {
call_exprs[i] = call_expr->params()[param->Index()];
}
}
// Now call GetOriginatingResources() with from the callsite
GetOriginatingResources(call_exprs, callback);
}
} else {
// All the expressions resolved to globals
callback(globals);
} }
} }

View File

@ -210,6 +210,21 @@ class Inspector {
/// Constructes |sampler_targets_| if it hasn't already been instantiated. /// Constructes |sampler_targets_| if it hasn't already been instantiated.
void GenerateSamplerTargets(); void GenerateSamplerTargets();
/// For a N-uple of expressions, resolve to the appropriate global resources
/// and call 'cb'.
/// 'cb' may be called multiple times.
/// Assumes that not being able to resolve the resources is an error, so will
/// invoke TINT_ICE when that occurs.
/// @tparam N number of expressions in the n-uple
/// @tparam F type of the callback provided.
/// @param exprs N-uple of expressions to resolve.
/// @param cb is a callback function with the signature:
/// `void(std::array<const sem::GlobalVariable*, N>)`, which is invoked
/// whenever a set of expressions are resolved to globals.
template <size_t N, typename F>
void GetOriginatingResources(std::array<const ast::Expression*, N> exprs,
F&& cb);
}; };
} // namespace inspector } // namespace inspector

View File

@ -20,6 +20,7 @@
#include "src/ast/struct_block_decoration.h" #include "src/ast/struct_block_decoration.h"
#include "src/ast/workgroup_decoration.h" #include "src/ast/workgroup_decoration.h"
#include "src/inspector/test_inspector_builder.h" #include "src/inspector/test_inspector_builder.h"
#include "src/inspector/test_inspector_runner.h"
#include "src/program_builder.h" #include "src/program_builder.h"
#include "src/sem/depth_texture_type.h" #include "src/sem/depth_texture_type.h"
#include "src/sem/external_texture_type.h" #include "src/sem/external_texture_type.h"
@ -34,8 +35,15 @@ namespace {
// All the tests that descend from InspectorBuilder are expected to define their // All the tests that descend from InspectorBuilder are expected to define their
// test state via building up the AST through InspectorBuilder and then generate // test state via building up the AST through InspectorBuilder and then generate
// the program with ::Build(). // the program with ::Build.
// The returned Inspector from ::Build() can then be used to test expecations. // The returned Inspector from ::Build can then be used to test expecations.
//
// All the tests that descend from InspectorRunner are expected to define their
// test state via a WGSL shader, which will be parsed to generate a Program and
// Inspector in ::Initialize.
// The returned Inspector from ::Initialize can then be used to test
// expecations.
class InspectorGetEntryPointTest : public InspectorBuilder, class InspectorGetEntryPointTest : public InspectorBuilder,
public testing::Test {}; public testing::Test {};
@ -135,12 +143,16 @@ class InspectorGetStorageTextureResourceBindingsTestWithParam
class InspectorGetExternalTextureResourceBindingsTest : public InspectorBuilder, class InspectorGetExternalTextureResourceBindingsTest : public InspectorBuilder,
public testing::Test {}; public testing::Test {};
class InspectorGetSamplerTextureUsesTest : public InspectorBuilder, class InspectorGetSamplerTextureUsesTest : public InspectorRunner,
public testing::Test {}; public testing::Test {};
class InspectorGetWorkgroupStorageSizeTest : public InspectorBuilder, class InspectorGetWorkgroupStorageSizeTest : public InspectorBuilder,
public testing::Test {}; public testing::Test {};
// This is a catch all for shaders that have demonstrated regressions/crashes in
// the wild.
class InspectorRegressionTest : public InspectorRunner, public testing::Test {};
TEST_F(InspectorGetEntryPointTest, NoFunctions) { TEST_F(InspectorGetEntryPointTest, NoFunctions) {
Inspector& inspector = Build(); Inspector& inspector = Build();
@ -2353,34 +2365,31 @@ TEST_F(InspectorGetExternalTextureResourceBindingsTest, Simple) {
} }
TEST_F(InspectorGetSamplerTextureUsesTest, None) { TEST_F(InspectorGetSamplerTextureUsesTest, None) {
MakeEmptyBodyFunction("ep_func", ast::DecorationList{ std::string shader = R"(
Stage(ast::PipelineStage::kFragment), [[stage(fragment)]]
}); fn main() {
})";
Inspector& inspector = Build(); Inspector& inspector = Initialize(shader);
auto result = inspector.GetSamplerTextureUses("main");
auto result = inspector.GetSamplerTextureUses("ep_func");
ASSERT_FALSE(inspector.has_error()) << inspector.error(); ASSERT_FALSE(inspector.has_error()) << inspector.error();
ASSERT_EQ(0u, result.size()); ASSERT_EQ(0u, result.size());
} }
TEST_F(InspectorGetSamplerTextureUsesTest, Simple) { TEST_F(InspectorGetSamplerTextureUsesTest, Simple) {
auto* sampled_texture_type = std::string shader = R"(
ty.sampled_texture(ast::TextureDimension::k1d, ty.f32()); [[group(0), binding(1)]] var mySampler: sampler;
AddResource("foo_texture", sampled_texture_type, 0, 10); [[group(0), binding(2)]] var myTexture: texture_2d<f32>;
AddSampler("foo_sampler", 0, 1);
AddGlobalVariable("foo_coords", ty.f32());
MakeSamplerReferenceBodyFunction("ep_func", "foo_texture", "foo_sampler", [[stage(fragment)]]
"foo_coords", ty.f32(), fn main([[location(0)]] fragUV: vec2<f32>,
ast::DecorationList{ [[location(1)]] fragPosition: vec4<f32>) -> [[location(0)]] vec4<f32> {
Stage(ast::PipelineStage::kFragment), return textureSample(myTexture, mySampler, fragUV) * fragPosition;
}); })";
Inspector& inspector = Build(); Inspector& inspector = Initialize(shader);
auto result = inspector.GetSamplerTextureUses("main");
auto result = inspector.GetSamplerTextureUses("ep_func");
ASSERT_FALSE(inspector.has_error()) << inspector.error(); ASSERT_FALSE(inspector.has_error()) << inspector.error();
ASSERT_EQ(1u, result.size()); ASSERT_EQ(1u, result.size());
@ -2388,58 +2397,233 @@ TEST_F(InspectorGetSamplerTextureUsesTest, Simple) {
EXPECT_EQ(0u, result[0].sampler_binding_point.group); EXPECT_EQ(0u, result[0].sampler_binding_point.group);
EXPECT_EQ(1u, result[0].sampler_binding_point.binding); EXPECT_EQ(1u, result[0].sampler_binding_point.binding);
EXPECT_EQ(0u, result[0].texture_binding_point.group); EXPECT_EQ(0u, result[0].texture_binding_point.group);
EXPECT_EQ(10u, result[0].texture_binding_point.binding); EXPECT_EQ(2u, result[0].texture_binding_point.binding);
}
TEST_F(InspectorGetSamplerTextureUsesTest, UnknownEntryPoint) {
std::string shader = R"(
[[group(0), binding(1)]] var mySampler: sampler;
[[group(0), binding(2)]] var myTexture: texture_2d<f32>;
[[stage(fragment)]]
fn main([[location(0)]] fragUV: vec2<f32>,
[[location(1)]] fragPosition: vec4<f32>) -> [[location(0)]] vec4<f32> {
return textureSample(myTexture, mySampler, fragUV) * fragPosition;
})";
Inspector& inspector = Initialize(shader);
auto result = inspector.GetSamplerTextureUses("foo");
ASSERT_TRUE(inspector.has_error()) << inspector.error();
} }
TEST_F(InspectorGetSamplerTextureUsesTest, MultipleCalls) { TEST_F(InspectorGetSamplerTextureUsesTest, MultipleCalls) {
auto* sampled_texture_type = std::string shader = R"(
ty.sampled_texture(ast::TextureDimension::k1d, ty.f32()); [[group(0), binding(1)]] var mySampler: sampler;
AddResource("foo_texture", sampled_texture_type, 0, 10); [[group(0), binding(2)]] var myTexture: texture_2d<f32>;
AddSampler("foo_sampler", 0, 1);
AddGlobalVariable("foo_coords", ty.f32());
MakeSamplerReferenceBodyFunction("ep_func", "foo_texture", "foo_sampler", [[stage(fragment)]]
"foo_coords", ty.f32(), fn main([[location(0)]] fragUV: vec2<f32>,
ast::DecorationList{ [[location(1)]] fragPosition: vec4<f32>) -> [[location(0)]] vec4<f32> {
Stage(ast::PipelineStage::kFragment), return textureSample(myTexture, mySampler, fragUV) * fragPosition;
}); })";
Inspector& inspector = Build(); Inspector& inspector = Initialize(shader);
auto result_0 = inspector.GetSamplerTextureUses("main");
auto result_0 = inspector.GetSamplerTextureUses("ep_func");
ASSERT_FALSE(inspector.has_error()) << inspector.error(); ASSERT_FALSE(inspector.has_error()) << inspector.error();
auto result_1 = inspector.GetSamplerTextureUses("ep_func"); auto result_1 = inspector.GetSamplerTextureUses("main");
ASSERT_FALSE(inspector.has_error()) << inspector.error(); ASSERT_FALSE(inspector.has_error()) << inspector.error();
EXPECT_EQ(result_0, result_1); EXPECT_EQ(result_0, result_1);
} }
TEST_F(InspectorGetSamplerTextureUsesTest, InFunction) { TEST_F(InspectorGetSamplerTextureUsesTest, BothIndirect) {
auto* sampled_texture_type = std::string shader = R"(
ty.sampled_texture(ast::TextureDimension::k1d, ty.f32()); [[group(0), binding(1)]] var mySampler: sampler;
AddResource("foo_texture", sampled_texture_type, 0, 0); [[group(0), binding(2)]] var myTexture: texture_2d<f32>;
AddSampler("foo_sampler", 0, 1);
AddGlobalVariable("foo_coords", ty.f32());
MakeSamplerReferenceBodyFunction("foo_func", "foo_texture", "foo_sampler", fn doSample(t: texture_2d<f32>, s: sampler, uv: vec2<f32>) -> vec4<f32> {
"foo_coords", ty.f32(), {}); return textureSample(t, s, uv);
}
MakeCallerBodyFunction("ep_func", {"foo_func"}, [[stage(fragment)]]
ast::DecorationList{ fn main([[location(0)]] fragUV: vec2<f32>,
Stage(ast::PipelineStage::kFragment), [[location(1)]] fragPosition: vec4<f32>) -> [[location(0)]] vec4<f32> {
}); return doSample(myTexture, mySampler, fragUV) * fragPosition;
})";
Inspector& inspector = Build(); Inspector& inspector = Initialize(shader);
auto result = inspector.GetSamplerTextureUses("main");
auto result = inspector.GetSamplerTextureUses("ep_func");
ASSERT_FALSE(inspector.has_error()) << inspector.error(); ASSERT_FALSE(inspector.has_error()) << inspector.error();
ASSERT_EQ(1u, result.size()); ASSERT_EQ(1u, result.size());
EXPECT_EQ(0u, result[0].sampler_binding_point.group); EXPECT_EQ(0u, result[0].sampler_binding_point.group);
EXPECT_EQ(1u, result[0].sampler_binding_point.binding); EXPECT_EQ(1u, result[0].sampler_binding_point.binding);
EXPECT_EQ(0u, result[0].texture_binding_point.group); EXPECT_EQ(0u, result[0].texture_binding_point.group);
EXPECT_EQ(0u, result[0].texture_binding_point.binding); EXPECT_EQ(2u, result[0].texture_binding_point.binding);
}
TEST_F(InspectorGetSamplerTextureUsesTest, SamplerIndirect) {
std::string shader = R"(
[[group(0), binding(1)]] var mySampler: sampler;
[[group(0), binding(2)]] var myTexture: texture_2d<f32>;
fn doSample(s: sampler, uv: vec2<f32>) -> vec4<f32> {
return textureSample(myTexture, s, uv);
}
[[stage(fragment)]]
fn main([[location(0)]] fragUV: vec2<f32>,
[[location(1)]] fragPosition: vec4<f32>) -> [[location(0)]] vec4<f32> {
return doSample(mySampler, fragUV) * fragPosition;
})";
Inspector& inspector = Initialize(shader);
auto result = inspector.GetSamplerTextureUses("main");
ASSERT_FALSE(inspector.has_error()) << inspector.error();
ASSERT_EQ(1u, result.size());
EXPECT_EQ(0u, result[0].sampler_binding_point.group);
EXPECT_EQ(1u, result[0].sampler_binding_point.binding);
EXPECT_EQ(0u, result[0].texture_binding_point.group);
EXPECT_EQ(2u, result[0].texture_binding_point.binding);
}
TEST_F(InspectorGetSamplerTextureUsesTest, TextureIndirect) {
std::string shader = R"(
[[group(0), binding(1)]] var mySampler: sampler;
[[group(0), binding(2)]] var myTexture: texture_2d<f32>;
fn doSample(t: texture_2d<f32>, uv: vec2<f32>) -> vec4<f32> {
return textureSample(t, mySampler, uv);
}
[[stage(fragment)]]
fn main([[location(0)]] fragUV: vec2<f32>,
[[location(1)]] fragPosition: vec4<f32>) -> [[location(0)]] vec4<f32> {
return doSample(myTexture, fragUV) * fragPosition;
})";
Inspector& inspector = Initialize(shader);
auto result = inspector.GetSamplerTextureUses("main");
ASSERT_FALSE(inspector.has_error()) << inspector.error();
ASSERT_EQ(1u, result.size());
EXPECT_EQ(0u, result[0].sampler_binding_point.group);
EXPECT_EQ(1u, result[0].sampler_binding_point.binding);
EXPECT_EQ(0u, result[0].texture_binding_point.group);
EXPECT_EQ(2u, result[0].texture_binding_point.binding);
}
TEST_F(InspectorGetSamplerTextureUsesTest, NeitherIndirect) {
std::string shader = R"(
[[group(0), binding(1)]] var mySampler: sampler;
[[group(0), binding(2)]] var myTexture: texture_2d<f32>;
fn doSample(uv: vec2<f32>) -> vec4<f32> {
return textureSample(myTexture, mySampler, uv);
}
[[stage(fragment)]]
fn main([[location(0)]] fragUV: vec2<f32>,
[[location(1)]] fragPosition: vec4<f32>) -> [[location(0)]] vec4<f32> {
return doSample(fragUV) * fragPosition;
})";
Inspector& inspector = Initialize(shader);
auto result = inspector.GetSamplerTextureUses("main");
ASSERT_FALSE(inspector.has_error()) << inspector.error();
ASSERT_EQ(1u, result.size());
EXPECT_EQ(0u, result[0].sampler_binding_point.group);
EXPECT_EQ(1u, result[0].sampler_binding_point.binding);
EXPECT_EQ(0u, result[0].texture_binding_point.group);
EXPECT_EQ(2u, result[0].texture_binding_point.binding);
}
TEST_F(InspectorGetSamplerTextureUsesTest, Complex) {
std::string shader = R"(
[[group(0), binding(1)]] var mySampler: sampler;
[[group(0), binding(2)]] var myTexture: texture_2d<f32>;
fn doSample(t: texture_2d<f32>, s: sampler, uv: vec2<f32>) -> vec4<f32> {
return textureSample(t, s, uv);
}
fn X(t: texture_2d<f32>, s: sampler, uv: vec2<f32>) -> vec4<f32> {
return doSample(t, s, uv);
}
fn Y(t: texture_2d<f32>, s: sampler, uv: vec2<f32>) -> vec4<f32> {
return doSample(t, s, uv);
}
fn Z(t: texture_2d<f32>, s: sampler, uv: vec2<f32>) -> vec4<f32> {
return X(t, s, uv) + Y(t, s, uv);
}
[[stage(fragment)]]
fn via_call([[location(0)]] fragUV: vec2<f32>,
[[location(1)]] fragPosition: vec4<f32>) -> [[location(0)]] vec4<f32> {
return Z(myTexture, mySampler, fragUV) * fragPosition;
}
[[stage(fragment)]]
fn via_ptr([[location(0)]] fragUV: vec2<f32>,
[[location(1)]] fragPosition: vec4<f32>) -> [[location(0)]] vec4<f32> {
let t = &myTexture;
let s = &mySampler;
return textureSample(*t, *s, fragUV) + fragPosition;
}
[[stage(fragment)]]
fn direct([[location(0)]] fragUV: vec2<f32>,
[[location(1)]] fragPosition: vec4<f32>) -> [[location(0)]] vec4<f32> {
return textureSample(myTexture, mySampler, fragUV) + fragPosition;
})";
Inspector& inspector = Initialize(shader);
{
auto result = inspector.GetSamplerTextureUses("via_call");
ASSERT_FALSE(inspector.has_error()) << inspector.error();
ASSERT_EQ(1u, result.size());
EXPECT_EQ(0u, result[0].sampler_binding_point.group);
EXPECT_EQ(1u, result[0].sampler_binding_point.binding);
EXPECT_EQ(0u, result[0].texture_binding_point.group);
EXPECT_EQ(2u, result[0].texture_binding_point.binding);
}
{
auto result = inspector.GetSamplerTextureUses("via_ptr");
ASSERT_FALSE(inspector.has_error()) << inspector.error();
ASSERT_EQ(1u, result.size());
EXPECT_EQ(0u, result[0].sampler_binding_point.group);
EXPECT_EQ(1u, result[0].sampler_binding_point.binding);
EXPECT_EQ(0u, result[0].texture_binding_point.group);
EXPECT_EQ(2u, result[0].texture_binding_point.binding);
}
{
auto result = inspector.GetSamplerTextureUses("direct");
ASSERT_FALSE(inspector.has_error()) << inspector.error();
ASSERT_EQ(1u, result.size());
EXPECT_EQ(0u, result[0].sampler_binding_point.group);
EXPECT_EQ(1u, result[0].sampler_binding_point.binding);
EXPECT_EQ(0u, result[0].texture_binding_point.group);
EXPECT_EQ(2u, result[0].texture_binding_point.binding);
}
} }
TEST_F(InspectorGetWorkgroupStorageSizeTest, Empty) { TEST_F(InspectorGetWorkgroupStorageSizeTest, Empty) {
@ -2529,6 +2713,27 @@ TEST_F(InspectorGetWorkgroupStorageSizeTest, StructAlignment) {
EXPECT_EQ(1024u, inspector.GetWorkgroupStorageSize("ep_func")); EXPECT_EQ(1024u, inspector.GetWorkgroupStorageSize("ep_func"));
} }
// Crash was occuring in ::GenerateSamplerTargets, when
// ::GetSamplerTextureUses was called.
TEST_F(InspectorRegressionTest, tint967) {
std::string shader = R"(
[[group(0), binding(1)]] var mySampler: sampler;
[[group(0), binding(2)]] var myTexture: texture_2d<f32>;
fn doSample(t: texture_2d<f32>, s: sampler, uv: vec2<f32>) -> vec4<f32> {
return textureSample(t, s, uv);
}
[[stage(fragment)]]
fn main([[location(0)]] fragUV: vec2<f32>,
[[location(1)]] fragPosition: vec4<f32>) -> [[location(0)]] vec4<f32> {
return doSample(myTexture, mySampler, fragUV) * fragPosition;
})";
Inspector& inspector = Initialize(shader);
auto result = inspector.GetSamplerTextureUses("main");
}
} // namespace } // namespace
} // namespace inspector } // namespace inspector
} // namespace tint } // namespace tint

View File

@ -0,0 +1,39 @@
// Copyright 2021 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/inspector/test_inspector_runner.h"
namespace tint {
namespace inspector {
InspectorRunner::InspectorRunner() = default;
InspectorRunner::~InspectorRunner() = default;
Inspector& InspectorRunner::Initialize(std::string shader) {
if (inspector_) {
return *inspector_;
}
file_ = std::make_unique<Source::File>("test", shader);
program_ = std::make_unique<Program>(reader::wgsl::Parse(file_.get()));
[&]() {
ASSERT_TRUE(program_->IsValid())
<< diag::Formatter().format(program_->Diagnostics());
}();
inspector_ = std::make_unique<Inspector>(program_.get());
return *inspector_;
}
} // namespace inspector
} // namespace tint

View File

@ -0,0 +1,51 @@
// Copyright 2021 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_INSPECTOR_TEST_INSPECTOR_RUNNER_H_
#define SRC_INSPECTOR_TEST_INSPECTOR_RUNNER_H_
#include <memory>
#include <string>
#include "gtest/gtest.h"
#include "tint/tint.h"
namespace tint {
namespace inspector {
/// Utility class for running shaders in inspector tests
class InspectorRunner {
public:
InspectorRunner();
virtual ~InspectorRunner();
/// Create a Program with Inspector from the provided WGSL shader.
/// Should only be called once per test.
/// @param shader a WGSL shader
/// @returns a reference to the Inspector for the built Program.
Inspector& Initialize(std::string shader);
protected:
/// File created from input shader and used to create Program.
std::unique_ptr<Source::File> file_;
/// Program created by this runner.
std::unique_ptr<Program> program_;
/// Inspector for |program_|
std::unique_ptr<Inspector> inspector_;
};
} // namespace inspector
} // namespace tint
#endif // SRC_INSPECTOR_TEST_INSPECTOR_RUNNER_H_

View File

@ -224,6 +224,8 @@ tint_unittests_source_set("tint_unittests_core_src") {
"../src/inspector/inspector_test.cc", "../src/inspector/inspector_test.cc",
"../src/inspector/test_inspector_builder.cc", "../src/inspector/test_inspector_builder.cc",
"../src/inspector/test_inspector_builder.h", "../src/inspector/test_inspector_builder.h",
"../src/inspector/test_inspector_runner.cc",
"../src/inspector/test_inspector_runner.h",
"../src/intrinsic_table_test.cc", "../src/intrinsic_table_test.cc",
"../src/program_builder_test.cc", "../src/program_builder_test.cc",
"../src/program_test.cc", "../src/program_test.cc",