Resolver: collect unique texture/samplers pairs.

GLSL does not support separate textures and samplers, so they must be
replaced with combined samplers. This is the first stage of that change,
where we collect the unique texture/sampler pairs. Within a function,
texture and sampler must be either a global variable or a function
parameter. At the entry point level, all references must resolve to global
variables, so by recursing the call graph we can determine all of the
global pairs required.

This information will be used by an upcoming transform to modify the AST
to be GLSL-compliant: modifying function signatures, call sites, removing
separate globals and adding combined globals. It will also eventually
replace the pair-gathering currently performed by
Inspector::GetSamplerTextureUses().

Bug: tint:1366
Change-Id: I89451b195649da26e45641ea2f6955683ae9fc66
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/75960
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Stephen White <senorblanco@chromium.org>
This commit is contained in:
Stephen White
2022-01-10 20:46:35 +00:00
committed by Tint LUCI CQ
parent d1efb5d48c
commit 3e354fd524
5 changed files with 219 additions and 3 deletions

View File

@@ -1400,9 +1400,25 @@ sem::Call* Resolver::IntrinsicCall(
current_function_->AddDirectlyCalledIntrinsic(intrinsic);
if (IsTextureIntrinsic(intrinsic_type) &&
!ValidateTextureIntrinsicFunction(call)) {
return nullptr;
if (IsTextureIntrinsic(intrinsic_type)) {
if (!ValidateTextureIntrinsicFunction(call)) {
return nullptr;
}
// Collect a texture/sampler pair for this intrinsic.
const auto& signature = intrinsic->Signature();
int texture_index = signature.IndexOf(sem::ParameterUsage::kTexture);
if (texture_index == -1) {
TINT_ICE(Resolver, diagnostics_)
<< "texture intrinsic without texture parameter";
}
auto* texture = args[texture_index]->As<sem::VariableUser>()->Variable();
int sampler_index = signature.IndexOf(sem::ParameterUsage::kSampler);
const sem::Variable* sampler =
sampler_index != -1
? args[sampler_index]->As<sem::VariableUser>()->Variable()
: nullptr;
current_function_->AddTextureSamplerPair(texture, sampler);
}
if (!ValidateIntrinsicCall(call)) {
@@ -1439,6 +1455,26 @@ sem::Call* Resolver::FunctionCall(
for (auto* var : target->TransitivelyReferencedGlobals()) {
current_function_->AddTransitivelyReferencedGlobal(var);
}
// Map all texture/sampler pairs from the target function to the
// current function. These can only be global or parameter
// variables. Resolve any parameter variables to the corresponding
// argument passed to the current function. Leave global variables
// as-is. Then add the mapped pair to the current function's list of
// texture/sampler pairs.
for (sem::VariablePair pair : target->TextureSamplerPairs()) {
const sem::Variable* texture = pair.first;
const sem::Variable* sampler = pair.second;
if (auto* param = texture->As<sem::Parameter>()) {
texture = args[param->Index()]->As<sem::VariableUser>()->Variable();
}
if (sampler) {
if (auto* param = sampler->As<sem::Parameter>()) {
sampler = args[param->Index()]->As<sem::VariableUser>()->Variable();
}
}
current_function_->AddTextureSamplerPair(texture, sampler);
}
}
target->AddCallSite(call);

View File

@@ -2021,6 +2021,146 @@ TEST_F(ResolverTest, UnaryOp_Negation) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: cannot negate expression of type 'u32");
}
TEST_F(ResolverTest, TextureSampler_TextureSample) {
Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
GroupAndBinding(1, 1));
Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
auto* call = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1.0f, 2.0f)));
const ast::Function* f = Func("test_function", {}, ty.void_(), {call},
{Stage(ast::PipelineStage::kFragment)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
const sem::Function* sf = Sem().Get(f);
auto pairs = sf->TextureSamplerPairs();
ASSERT_EQ(pairs.size(), 1u);
EXPECT_TRUE(pairs[0].first != nullptr);
EXPECT_TRUE(pairs[0].second != nullptr);
}
TEST_F(ResolverTest, TextureSampler_TextureSampleInFunction) {
Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
GroupAndBinding(1, 1));
Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
auto* inner_call =
CallStmt(Call("textureSample", "t", "s", vec2<f32>(1.0f, 2.0f)));
const ast::Function* inner_func =
Func("inner_func", {}, ty.void_(), {inner_call});
auto* outer_call = CallStmt(Call("inner_func"));
const ast::Function* outer_func =
Func("outer_func", {}, ty.void_(), {outer_call},
{Stage(ast::PipelineStage::kFragment)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto inner_pairs = Sem().Get(inner_func)->TextureSamplerPairs();
ASSERT_EQ(inner_pairs.size(), 1u);
EXPECT_TRUE(inner_pairs[0].first != nullptr);
EXPECT_TRUE(inner_pairs[0].second != nullptr);
auto outer_pairs = Sem().Get(outer_func)->TextureSamplerPairs();
ASSERT_EQ(outer_pairs.size(), 1u);
EXPECT_TRUE(outer_pairs[0].first != nullptr);
EXPECT_TRUE(outer_pairs[0].second != nullptr);
}
TEST_F(ResolverTest, TextureSampler_TextureSampleFunctionDiamondSameVariables) {
Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
GroupAndBinding(1, 1));
Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
auto* inner_call_1 =
CallStmt(Call("textureSample", "t", "s", vec2<f32>(1.0f, 2.0f)));
const ast::Function* inner_func_1 =
Func("inner_func_1", {}, ty.void_(), {inner_call_1});
auto* inner_call_2 =
CallStmt(Call("textureSample", "t", "s", vec2<f32>(3.0f, 4.0f)));
const ast::Function* inner_func_2 =
Func("inner_func_2", {}, ty.void_(), {inner_call_2});
auto* outer_call_1 = CallStmt(Call("inner_func_1"));
auto* outer_call_2 = CallStmt(Call("inner_func_2"));
const ast::Function* outer_func =
Func("outer_func", {}, ty.void_(), {outer_call_1, outer_call_2},
{Stage(ast::PipelineStage::kFragment)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto inner_pairs_1 = Sem().Get(inner_func_1)->TextureSamplerPairs();
ASSERT_EQ(inner_pairs_1.size(), 1u);
EXPECT_TRUE(inner_pairs_1[0].first != nullptr);
EXPECT_TRUE(inner_pairs_1[0].second != nullptr);
auto inner_pairs_2 = Sem().Get(inner_func_2)->TextureSamplerPairs();
ASSERT_EQ(inner_pairs_1.size(), 1u);
EXPECT_TRUE(inner_pairs_2[0].first != nullptr);
EXPECT_TRUE(inner_pairs_2[0].second != nullptr);
auto outer_pairs = Sem().Get(outer_func)->TextureSamplerPairs();
ASSERT_EQ(outer_pairs.size(), 1u);
EXPECT_TRUE(outer_pairs[0].first != nullptr);
EXPECT_TRUE(outer_pairs[0].second != nullptr);
}
TEST_F(ResolverTest,
TextureSampler_TextureSampleFunctionDiamondDifferentVariables) {
Global("t1", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
GroupAndBinding(1, 1));
Global("t2", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
GroupAndBinding(1, 2));
Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 3));
auto* inner_call_1 =
CallStmt(Call("textureSample", "t1", "s", vec2<f32>(1.0f, 2.0f)));
const ast::Function* inner_func_1 =
Func("inner_func_1", {}, ty.void_(), {inner_call_1});
auto* inner_call_2 =
CallStmt(Call("textureSample", "t2", "s", vec2<f32>(3.0f, 4.0f)));
const ast::Function* inner_func_2 =
Func("inner_func_2", {}, ty.void_(), {inner_call_2});
auto* outer_call_1 = CallStmt(Call("inner_func_1"));
auto* outer_call_2 = CallStmt(Call("inner_func_2"));
const ast::Function* outer_func =
Func("outer_func", {}, ty.void_(), {outer_call_1, outer_call_2},
{Stage(ast::PipelineStage::kFragment)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto inner_pairs_1 = Sem().Get(inner_func_1)->TextureSamplerPairs();
ASSERT_EQ(inner_pairs_1.size(), 1u);
EXPECT_TRUE(inner_pairs_1[0].first != nullptr);
EXPECT_TRUE(inner_pairs_1[0].second != nullptr);
auto inner_pairs_2 = Sem().Get(inner_func_2)->TextureSamplerPairs();
ASSERT_EQ(inner_pairs_2.size(), 1u);
EXPECT_TRUE(inner_pairs_2[0].first != nullptr);
EXPECT_TRUE(inner_pairs_2[0].second != nullptr);
auto outer_pairs = Sem().Get(outer_func)->TextureSamplerPairs();
ASSERT_EQ(outer_pairs.size(), 2u);
EXPECT_TRUE(outer_pairs[0].first == inner_pairs_1[0].first);
EXPECT_TRUE(outer_pairs[0].second == inner_pairs_1[0].second);
EXPECT_TRUE(outer_pairs[1].first == inner_pairs_2[0].first);
EXPECT_TRUE(outer_pairs[1].second == inner_pairs_2[0].second);
}
TEST_F(ResolverTest, TextureSampler_TextureDimensions) {
Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
GroupAndBinding(1, 2));
auto* call = Call("textureDimensions", "t");
const ast::Function* f = WrapInFunction(call);
EXPECT_TRUE(r()->Resolve()) << r()->error();
const sem::Function* sf = Sem().Get(f);
auto pairs = sf->TextureSamplerPairs();
ASSERT_EQ(pairs.size(), 1u);
EXPECT_TRUE(pairs[0].first != nullptr);
EXPECT_TRUE(pairs[0].second == nullptr);
}
} // namespace
} // namespace resolver
} // namespace tint