Add inspector helper for texture information.
This CL adds a helper to the inspector get information on textures which
are used in `textureNumSamples` and `textureNumLevels` calls. It also
gathers `textureLoad` because they use the other methods if polyfilled
in GLSL.
Change-Id: If0b04864d46a1415393237451761c989c84431ad
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/169261
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/lang/wgsl/inspector/inspector.cc b/src/tint/lang/wgsl/inspector/inspector.cc
index 630ef0f..d7fe564 100644
--- a/src/tint/lang/wgsl/inspector/inspector.cc
+++ b/src/tint/lang/wgsl/inspector/inspector.cc
@@ -27,7 +27,7 @@
#include "src/tint/lang/wgsl/inspector/inspector.h"
-#include <limits>
+#include <unordered_set>
#include <utility>
#include "src/tint/lang/core/builtin_value.h"
@@ -1009,4 +1009,139 @@
}
}
+std::vector<Inspector::LevelSampleInfo> Inspector::GetTextureQueries(const std::string& ep_name) {
+ std::vector<LevelSampleInfo> res;
+
+ std::unordered_set<BindingPoint> seen = {};
+
+ auto sample_type_for_call_and_type = [](wgsl::BuiltinFn builtin, const core::type::Type* ty) {
+ if (builtin == wgsl::BuiltinFn::kTextureNumLevels) {
+ return TextureQueryType::kTextureNumLevels;
+ }
+ if (builtin == wgsl::BuiltinFn::kTextureLoad) {
+ if (!ty->UnwrapRef()
+ ->IsAnyOf<core::type::MultisampledTexture,
+ core::type::DepthMultisampledTexture>()) {
+ return TextureQueryType::kTextureNumLevels;
+ }
+ }
+
+ return TextureQueryType::kTextureNumSamples;
+ };
+
+ Hashmap<const sem::Function*, Hashmap<const ast::Parameter*, TextureQueryType, 4>, 8>
+ fn_to_data;
+
+ auto record_function_param = [&fn_to_data](const sem::Function* func,
+ const ast::Parameter* param, TextureQueryType type) {
+ auto& param_to_type = *fn_to_data.GetOrZero(func);
+
+ auto entry = param_to_type.Get(param);
+ if (entry.has_value()) {
+ return;
+ }
+
+ param_to_type.Add(param, type);
+ };
+
+ auto save_if_needed = [&res, &seen](const sem::GlobalVariable* global, TextureQueryType type) {
+ auto binding = global->Attributes().binding_point.value();
+ if (seen.insert(binding).second) {
+ res.emplace_back(LevelSampleInfo{type, binding.group, binding.binding});
+ }
+ };
+
+ auto& sem = program_.Sem();
+
+ const auto* ep = FindEntryPointByName(ep_name);
+ if (!ep) {
+ return {};
+ }
+
+ // This works in dependency order such that we'll see the texture call first and can record
+ // any function parameter information and then as we walk up the function chain we can look
+ // the call data.
+ for (auto* fn_decl : sem.Module()->DependencyOrderedDeclarations()) {
+ auto* fn = sem.Get<sem::Function>(fn_decl);
+ if (!fn) {
+ continue;
+ }
+
+ // This is an entrypoint, make sure it's the requested entry point
+ if (fn->Declaration()->IsEntryPoint()) {
+ if (fn->Declaration() != ep) {
+ continue;
+ }
+ } else {
+ // Not an entry point, make sure it was called from the requested entry point
+ if (!fn->HasAncestorEntryPoint(ep->name->symbol)) {
+ continue;
+ }
+ }
+
+ for (auto* call : fn->DirectCalls()) {
+ // Builtin function call, record the texture information. If the used texture maps
+ // back up to a function parameter just store the type of the call and we'll track the
+ // function callback up in the `sem::Function` branch.
+ tint::Switch(
+ call->Target(),
+ [&](const sem::BuiltinFn* builtin) {
+ if (builtin->Fn() != wgsl::BuiltinFn::kTextureNumLevels &&
+ builtin->Fn() != wgsl::BuiltinFn::kTextureNumSamples &&
+ builtin->Fn() != wgsl::BuiltinFn::kTextureLoad) {
+ return;
+ }
+
+ auto* texture_expr = call->Declaration()->args[0];
+ auto* texture_sem = sem.GetVal(texture_expr)->RootIdentifier();
+ TINT_ASSERT(texture_sem);
+
+ auto type = sample_type_for_call_and_type(builtin->Fn(), texture_sem->Type());
+
+ tint::Switch(
+ texture_sem, //
+ [&](const sem::GlobalVariable* global) { save_if_needed(global, type); },
+ [&](const sem::Parameter* param) {
+ record_function_param(fn, param->Declaration(), type);
+ },
+ TINT_ICE_ON_NO_MATCH);
+ },
+ [&](const sem::Function* func) {
+ // A function call, check to see if any params needed to be tracked back to a
+ // global texture.
+
+ auto param_to_type = fn_to_data.Find(func);
+ if (!param_to_type) {
+ return;
+ }
+ TINT_ASSERT(call->Arguments().Length() == func->Declaration()->params.Length());
+
+ for (size_t i = 0; i < call->Arguments().Length(); i++) {
+ auto param = func->Declaration()->params[i];
+
+ // Determine if this had a texture we cared about
+ auto type = param_to_type->Get(param);
+ if (!type.has_value()) {
+ continue;
+ }
+
+ auto* arg = call->Arguments()[i];
+ auto* texture_sem = arg->RootIdentifier();
+
+ tint::Switch(
+ texture_sem,
+ [&](const sem::GlobalVariable* global) {
+ save_if_needed(global, type.value());
+ },
+ [&](const sem::Parameter* p) {
+ record_function_param(fn, p->Declaration(), type.value());
+ },
+ TINT_ICE_ON_NO_MATCH);
+ }
+ });
+ }
+ }
+ return res;
+}
+
} // namespace tint::inspector
diff --git a/src/tint/lang/wgsl/inspector/inspector.h b/src/tint/lang/wgsl/inspector/inspector.h
index 14c5ef6..6604832 100644
--- a/src/tint/lang/wgsl/inspector/inspector.h
+++ b/src/tint/lang/wgsl/inspector/inspector.h
@@ -156,6 +156,31 @@
/// extension.
std::vector<std::pair<std::string, Source>> GetEnableDirectives();
+ /// The information needed to be supplied.
+ enum class TextureQueryType : uint8_t {
+ /// Texture Num Levels
+ kTextureNumLevels,
+ /// Texture Num Samples
+ kTextureNumSamples,
+ };
+ /// Information on level and sample calls by a given texture binding point
+ struct LevelSampleInfo {
+ /// The type of function
+ TextureQueryType type = TextureQueryType::kTextureNumLevels;
+ /// The group number
+ uint32_t group = 0;
+ /// The binding number
+ uint32_t binding = 0;
+ };
+
+ /// @param ep the entry point ot get the information for
+ /// @returns a vector of information for textures which call textureNumLevels and
+ /// textureNumSamples for backends which require additional support for those methods. Each
+ /// binding point will only be returned once regardless of the number of calls made. The
+ /// texture types for `textureNumSamples` is disjoint from the texture types in
+ /// `textureNumLevels` so the binding point will always be one or the other.
+ std::vector<LevelSampleInfo> GetTextureQueries(const std::string& ep);
+
private:
const Program& program_;
diag::List diagnostics_;
diff --git a/src/tint/lang/wgsl/inspector/inspector_test.cc b/src/tint/lang/wgsl/inspector/inspector_test.cc
index c33f950..76d514e 100644
--- a/src/tint/lang/wgsl/inspector/inspector_test.cc
+++ b/src/tint/lang/wgsl/inspector/inspector_test.cc
@@ -3701,5 +3701,286 @@
inspector.GetSamplerTextureUses("main");
}
+class InspectorTextureTest : public InspectorRunner, public testing::Test {};
+
+TEST_F(InspectorTextureTest, TextureLevelInEP) {
+ std::string shader = R"(
+@group(2) @binding(3) var myTexture: texture_2d<f32>;
+
+@compute @workgroup_size(1)
+fn main() {
+ let num = textureNumLevels(myTexture);
+})";
+
+ Inspector& inspector = Initialize(shader);
+ auto info = inspector.GetTextureQueries("main");
+
+ ASSERT_EQ(1u, info.size());
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info[0].type);
+ EXPECT_EQ(2u, info[0].group);
+ EXPECT_EQ(3u, info[0].binding);
+}
+
+TEST_F(InspectorTextureTest, TextureLevelInEPNoDups) {
+ std::string shader = R"(
+@group(0) @binding(0) var myTexture: texture_2d<f32>;
+
+@compute @workgroup_size(1)
+fn main() {
+ let num1 = textureNumLevels(myTexture);
+ let num2 = textureNumLevels(myTexture);
+})";
+
+ Inspector& inspector = Initialize(shader);
+ auto info = inspector.GetTextureQueries("main");
+
+ ASSERT_EQ(1u, info.size());
+}
+
+TEST_F(InspectorTextureTest, TextureLevelInEPMultiple) {
+ std::string shader = R"(
+@group(2) @binding(3) var tex1: texture_2d<f32>;
+@group(1) @binding(2) var tex2: texture_2d<f32>;
+
+@compute @workgroup_size(1)
+fn main() {
+ let num1 = textureNumLevels(tex1);
+ let num2 = textureNumLevels(tex2);
+})";
+
+ Inspector& inspector = Initialize(shader);
+ auto info = inspector.GetTextureQueries("main");
+
+ ASSERT_EQ(2u, info.size());
+
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info[0].type);
+ EXPECT_EQ(2u, info[0].group);
+ EXPECT_EQ(3u, info[0].binding);
+
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info[1].type);
+ EXPECT_EQ(1u, info[1].group);
+ EXPECT_EQ(2u, info[1].binding);
+}
+
+TEST_F(InspectorTextureTest, TextureSamplesInEP) {
+ std::string shader = R"(
+@group(2) @binding(3) var myTexture: texture_multisampled_2d<f32>;
+
+@compute @workgroup_size(1)
+fn main() {
+ let num = textureNumSamples(myTexture);
+})";
+
+ Inspector& inspector = Initialize(shader);
+ auto info = inspector.GetTextureQueries("main");
+
+ ASSERT_EQ(1u, info.size());
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumSamples, info[0].type);
+ EXPECT_EQ(2u, info[0].group);
+ EXPECT_EQ(3u, info[0].binding);
+}
+
+TEST_F(InspectorTextureTest, TextureSamplesInEPNoDups) {
+ std::string shader = R"(
+@group(0) @binding(0) var myTexture: texture_multisampled_2d<f32>;
+
+@compute @workgroup_size(1)
+fn main() {
+ let num1 = textureNumSamples(myTexture);
+ let num2 = textureNumSamples(myTexture);
+})";
+
+ Inspector& inspector = Initialize(shader);
+ auto info = inspector.GetTextureQueries("main");
+
+ ASSERT_EQ(1u, info.size());
+}
+
+TEST_F(InspectorTextureTest, TextureSamplesInEPMultiple) {
+ std::string shader = R"(
+@group(2) @binding(3) var tex1: texture_multisampled_2d<f32>;
+@group(1) @binding(2) var tex2: texture_multisampled_2d<f32>;
+
+@compute @workgroup_size(1)
+fn main() {
+ let num1 = textureNumSamples(tex1);
+ let num2 = textureNumSamples(tex2);
+})";
+
+ Inspector& inspector = Initialize(shader);
+ auto info = inspector.GetTextureQueries("main");
+
+ ASSERT_EQ(2u, info.size());
+
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumSamples, info[0].type);
+ EXPECT_EQ(2u, info[0].group);
+ EXPECT_EQ(3u, info[0].binding);
+
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumSamples, info[1].type);
+ EXPECT_EQ(1u, info[1].group);
+ EXPECT_EQ(2u, info[1].binding);
+}
+
+TEST_F(InspectorTextureTest, TextureLoadInEP) {
+ std::string shader = R"(
+@group(2) @binding(3) var tex1: texture_2d<f32>;
+
+@compute @workgroup_size(1)
+fn main() {
+ let num1 = textureLoad(tex1, vec2(0, 0), 0);
+})";
+
+ Inspector& inspector = Initialize(shader);
+ auto info = inspector.GetTextureQueries("main");
+
+ ASSERT_EQ(1u, info.size());
+
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info[0].type);
+ EXPECT_EQ(2u, info[0].group);
+ EXPECT_EQ(3u, info[0].binding);
+}
+
+TEST_F(InspectorTextureTest, TextureLoadMultisampledInEP) {
+ std::string shader = R"(
+@group(2) @binding(3) var tex1: texture_multisampled_2d<f32>;
+
+@compute @workgroup_size(1)
+fn main() {
+ let num1 = textureLoad(tex1, vec2(0, 0), 0);
+})";
+
+ Inspector& inspector = Initialize(shader);
+ auto info = inspector.GetTextureQueries("main");
+
+ ASSERT_EQ(1u, info.size());
+
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumSamples, info[0].type);
+ EXPECT_EQ(2u, info[0].group);
+ EXPECT_EQ(3u, info[0].binding);
+}
+
+TEST_F(InspectorTextureTest, TextureLoadMultipleInEP) {
+ std::string shader = R"(
+@group(2) @binding(3) var tex1: texture_2d<f32>;
+@group(1) @binding(4) var tex2: texture_multisampled_2d<f32>;
+
+@compute @workgroup_size(1)
+fn main() {
+ let num1 = textureLoad(tex1, vec2(0, 0), 0);
+ let num2 = textureLoad(tex2, vec2(0, 0), 0);
+})";
+
+ Inspector& inspector = Initialize(shader);
+ auto info = inspector.GetTextureQueries("main");
+
+ ASSERT_EQ(2u, info.size());
+
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info[0].type);
+ EXPECT_EQ(2u, info[0].group);
+ EXPECT_EQ(3u, info[0].binding);
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumSamples, info[1].type);
+ EXPECT_EQ(1u, info[1].group);
+ EXPECT_EQ(4u, info[1].binding);
+}
+
+TEST_F(InspectorTextureTest, TextureInSubfunction) {
+ std::string shader = R"(
+@group(2) @binding(3) var tex1: texture_2d<f32>;
+@group(1) @binding(4) var tex2: texture_multisampled_2d<f32>;
+@group(1) @binding(3) var tex3: texture_2d<f32>;
+
+fn b(tx1: texture_2d<f32>, tx2: texture_multisampled_2d<f32>, tx3: texture_2d<f32>, tx4: texture_2d<f32>) {
+ let v1 = textureNumLevels(tx1);
+ let v2 = textureNumSamples(tx2);
+ let v3 = textureLoad(tx3, vec2(0, 0), 0);
+ let v4 = textureNumLevels(tx4);
+}
+
+fn a(tx1: texture_2d<f32>, tx2: texture_multisampled_2d<f32>, tx3: texture_2d<f32>) {
+ b(tx1, tx2, tx3, tx1);
+}
+
+@compute @workgroup_size(1)
+fn main() {
+ a(tex1, tex2, tex3);
+})";
+
+ Inspector& inspector = Initialize(shader);
+ auto info = inspector.GetTextureQueries("main");
+
+ ASSERT_EQ(3u, info.size());
+
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info[0].type);
+ EXPECT_EQ(2u, info[0].group);
+ EXPECT_EQ(3u, info[0].binding);
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumSamples, info[1].type);
+ EXPECT_EQ(1u, info[1].group);
+ EXPECT_EQ(4u, info[1].binding);
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info[2].type);
+ EXPECT_EQ(1u, info[2].group);
+ EXPECT_EQ(3u, info[2].binding);
+}
+
+TEST_F(InspectorTextureTest, TextureMultipleEPs) {
+ std::string shader = R"(
+@group(0) @binding(0) var<storage, read_write> dstBuf : array<u32>;
+@group(0) @binding(1) var tex1 : texture_2d_array<f32>;
+@group(0) @binding(4) var tex2 : texture_multisampled_2d<f32>;
+@group(1) @binding(3) var tex3 : texture_2d_array<f32>;
+
+@compute @workgroup_size(1, 1, 1) fn main1() {
+ dstBuf[0] = textureNumLayers(tex1);
+ dstBuf[1] = textureNumLevels(tex1);
+ dstBuf[2] = textureNumSamples(tex2);
+ dstBuf[3] = textureNumLevels(tex3);
+}
+
+@compute @workgroup_size(1, 1, 1) fn main2() {
+ dstBuf[0] = textureNumLayers(tex1);
+ dstBuf[1] = textureNumLevels(tex1);
+ dstBuf[2] = textureNumSamples(tex2);
+}
+ )";
+ Inspector& inspector = Initialize(shader);
+ {
+ auto info1 = inspector.GetTextureQueries("main1");
+ ASSERT_EQ(3u, info1.size());
+
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info1[0].type);
+ EXPECT_EQ(0u, info1[0].group);
+ EXPECT_EQ(1u, info1[0].binding);
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumSamples, info1[1].type);
+ EXPECT_EQ(0u, info1[1].group);
+ EXPECT_EQ(4u, info1[1].binding);
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info1[2].type);
+ EXPECT_EQ(1u, info1[2].group);
+ EXPECT_EQ(3u, info1[2].binding);
+ }
+ {
+ auto info2 = inspector.GetTextureQueries("main2");
+ ASSERT_EQ(2u, info2.size());
+
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumLevels, info2[0].type);
+ EXPECT_EQ(0u, info2[0].group);
+ EXPECT_EQ(1u, info2[0].binding);
+ EXPECT_EQ(Inspector::TextureQueryType::kTextureNumSamples, info2[1].type);
+ EXPECT_EQ(0u, info2[1].group);
+ EXPECT_EQ(4u, info2[1].binding);
+ }
+}
+
} // namespace
+
+static std::ostream& operator<<(std::ostream& out, const Inspector::TextureQueryType& ty) {
+ switch (ty) {
+ case Inspector::TextureQueryType::kTextureNumLevels:
+ out << "textureNumLevels";
+ break;
+ case Inspector::TextureQueryType::kTextureNumSamples:
+ out << "textureNumSamples";
+ break;
+ }
+ return out;
+}
+
} // namespace tint::inspector