Implement a Texture1D -> Texture2D transform.
This is required for GLSL ES, which doesn't support Texture1D.
Bug: dawn:1301
Change-Id: Iba08d04a0bc23c278e65618550ea314ca0cbee1c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/114363
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Stephen White <senorblanco@chromium.org>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index f57a2d7..f3fd25b 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -535,6 +535,8 @@
"transform/std140.h",
"transform/substitute_override.cc",
"transform/substitute_override.h",
+ "transform/texture_1d_to_2d.cc",
+ "transform/texture_1d_to_2d.h",
"transform/transform.cc",
"transform/transform.h",
"transform/truncate_interstage_variables.cc",
@@ -1328,6 +1330,7 @@
"transform/std140_test.cc",
"transform/substitute_override_test.cc",
"transform/test_helper.h",
+ "transform/texture_1d_to_2d_test.cc",
"transform/transform_test.cc",
"transform/truncate_interstage_variables_test.cc",
"transform/unshadow_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index f35aa57..2f7bda7 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -441,6 +441,8 @@
transform/std140.h
transform/substitute_override.cc
transform/substitute_override.h
+ transform/texture_1d_to_2d.cc
+ transform/texture_1d_to_2d.h
transform/transform.cc
transform/transform.h
transform/truncate_interstage_variables.cc
@@ -1261,6 +1263,7 @@
transform/std140_test.cc
transform/substitute_override_test.cc
transform/test_helper.h
+ transform/texture_1d_to_2d_test.cc
transform/truncate_interstage_variables_test.cc
transform/unshadow_test.cc
transform/var_for_dynamic_index_test.cc
diff --git a/src/tint/transform/texture_1d_to_2d.cc b/src/tint/transform/texture_1d_to_2d.cc
new file mode 100644
index 0000000..0e19d02
--- /dev/null
+++ b/src/tint/transform/texture_1d_to_2d.cc
@@ -0,0 +1,186 @@
+// Copyright 2022 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/tint/transform/texture_1d_to_2d.h"
+
+#include <utility>
+
+#include "src/tint/program_builder.h"
+#include "src/tint/sem/function.h"
+#include "src/tint/sem/statement.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::transform::Texture1DTo2D);
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::transform {
+
+namespace {
+
+bool ShouldRun(const Program* program) {
+ for (auto* fn : program->AST().Functions()) {
+ if (auto* sem_fn = program->Sem().Get(fn)) {
+ for (auto* builtin : sem_fn->DirectlyCalledBuiltins()) {
+ const auto& signature = builtin->Signature();
+ auto texture = signature.Parameter(sem::ParameterUsage::kTexture);
+ if (texture) {
+ auto* tex = texture->Type()->As<type::Texture>();
+ if (tex->dim() == ast::TextureDimension::k1d) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ for (auto* var : program->AST().GlobalVariables()) {
+ if (Switch(
+ program->Sem().Get(var->type),
+ [&](const type::SampledTexture* tex) {
+ return tex->dim() == ast::TextureDimension::k1d;
+ },
+ [&](const type::StorageTexture* storage_tex) {
+ return storage_tex->dim() == ast::TextureDimension::k1d;
+ })) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+/// PIMPL state for the transform
+struct Texture1DTo2D::State {
+ /// The source program
+ const Program* const src;
+ /// The target program builder
+ ProgramBuilder b;
+ /// The clone context
+ CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
+
+ /// Constructor
+ /// @param program the source program
+ explicit State(const Program* program) : src(program) {}
+
+ /// Runs the transform
+ /// @returns the new program or SkipTransform if the transform is not required
+ ApplyResult Run() {
+ auto& sem = src->Sem();
+
+ if (!ShouldRun(ctx.src)) {
+ return SkipTransform;
+ }
+
+ auto create_var = [&](const ast::Variable* v, ast::Type* type) -> const ast::Variable* {
+ if (v->As<ast::Parameter>()) {
+ return ctx.dst->Param(ctx.Clone(v->symbol), type, ctx.Clone(v->attributes));
+ } else {
+ return ctx.dst->Var(ctx.Clone(v->symbol), type, ctx.Clone(v->attributes));
+ }
+ };
+
+ ctx.ReplaceAll([&](const ast::Variable* v) -> const ast::Variable* {
+ const ast::Variable* r = Switch(
+ sem.Get(v->type),
+ [&](const type::SampledTexture* tex) -> const ast::Variable* {
+ if (tex->dim() == ast::TextureDimension::k1d) {
+ auto* type = ctx.dst->create<ast::SampledTexture>(
+ ast::TextureDimension::k2d, CreateASTTypeFor(ctx, tex->type()));
+ return create_var(v, type);
+ } else {
+ return nullptr;
+ }
+ },
+ [&](const type::StorageTexture* storage_tex) -> const ast::Variable* {
+ if (storage_tex->dim() == ast::TextureDimension::k1d) {
+ auto* type = ctx.dst->create<ast::StorageTexture>(
+ ast::TextureDimension::k2d, storage_tex->texel_format(),
+ CreateASTTypeFor(ctx, storage_tex->type()), storage_tex->access());
+ return create_var(v, type);
+ } else {
+ return nullptr;
+ }
+ },
+ [](Default) { return nullptr; });
+ return r;
+ });
+
+ ctx.ReplaceAll([&](const ast::CallExpression* c) -> const ast::Expression* {
+ auto* call = sem.Get(c)->UnwrapMaterialize()->As<sem::Call>();
+ if (!call) {
+ return nullptr;
+ }
+ auto* builtin = call->Target()->As<sem::Builtin>();
+ if (!builtin) {
+ return nullptr;
+ }
+ const auto& signature = builtin->Signature();
+ auto texture = signature.Parameter(sem::ParameterUsage::kTexture);
+ auto* tex = texture->Type()->As<type::Texture>();
+ if (tex->dim() != ast::TextureDimension::k1d) {
+ return nullptr;
+ }
+
+ if (builtin->Type() == sem::BuiltinType::kTextureDimensions) {
+ // If this textureDimensions() call is in a CallStatement, we can leave it
+ // unmodified since the return value will be dropped on the floor anyway.
+ if (call->Stmt()->Declaration()->Is<ast::CallStatement>()) {
+ return nullptr;
+ }
+ auto* new_call = ctx.CloneWithoutTransform(c);
+ return ctx.dst->MemberAccessor(new_call, "x");
+ }
+
+ auto coords_index = signature.IndexOf(sem::ParameterUsage::kCoords);
+ if (coords_index == -1) {
+ return nullptr;
+ }
+
+ utils::Vector<const ast::Expression*, 8> args;
+ int index = 0;
+ for (auto* arg : c->args) {
+ if (index == coords_index) {
+ auto* ctype = call->Arguments()[static_cast<size_t>(coords_index)]->Type();
+ auto* coords = c->args[static_cast<size_t>(coords_index)];
+
+ const ast::LiteralExpression* half = nullptr;
+ if (ctype->is_integer_scalar()) {
+ half = ctx.dst->Expr(0_a);
+ } else {
+ half = ctx.dst->Expr(0.5_a);
+ }
+ args.Push(
+ ctx.dst->vec(CreateASTTypeFor(ctx, ctype), 2u, ctx.Clone(coords), half));
+ } else {
+ args.Push(ctx.Clone(arg));
+ }
+ index++;
+ }
+ return ctx.dst->Call(ctx.Clone(c->target.name), args);
+ });
+
+ ctx.Clone();
+ return Program(std::move(b));
+ }
+};
+
+Texture1DTo2D::Texture1DTo2D() = default;
+
+Texture1DTo2D::~Texture1DTo2D() = default;
+
+Transform::ApplyResult Texture1DTo2D::Apply(const Program* src, const DataMap&, DataMap&) const {
+ return State(src).Run();
+}
+
+} // namespace tint::transform
diff --git a/src/tint/transform/texture_1d_to_2d.h b/src/tint/transform/texture_1d_to_2d.h
new file mode 100644
index 0000000..9999821
--- /dev/null
+++ b/src/tint/transform/texture_1d_to_2d.h
@@ -0,0 +1,43 @@
+// Copyright 2022 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_TINT_TRANSFORM_TEXTURE_1D_TO_2D_H_
+#define SRC_TINT_TRANSFORM_TEXTURE_1D_TO_2D_H_
+
+#include "src/tint/transform/transform.h"
+
+namespace tint::transform {
+
+/// This transform converts all 1D texture types and accesses to 2D.
+/// This is required for GLSL ES, which does not support 1D textures.
+class Texture1DTo2D final : public Castable<Texture1DTo2D, Transform> {
+ public:
+ /// Constructor
+ Texture1DTo2D();
+
+ /// Destructor
+ ~Texture1DTo2D() override;
+
+ /// @copydoc Transform::Apply
+ ApplyResult Apply(const Program* program,
+ const DataMap& inputs,
+ DataMap& outputs) const override;
+
+ private:
+ struct State;
+};
+
+} // namespace tint::transform
+
+#endif // SRC_TINT_TRANSFORM_TEXTURE_1D_TO_2D_H_
diff --git a/src/tint/transform/texture_1d_to_2d_test.cc b/src/tint/transform/texture_1d_to_2d_test.cc
new file mode 100644
index 0000000..83a12bd
--- /dev/null
+++ b/src/tint/transform/texture_1d_to_2d_test.cc
@@ -0,0 +1,279 @@
+// Copyright 2022 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/tint/transform/texture_1d_to_2d.h"
+
+// #include <memory>
+// #include <utility>
+
+#include "src/tint/transform/test_helper.h"
+
+namespace tint::transform {
+namespace {
+
+using Texture1DTo2DTest = TransformTest;
+
+TEST_F(Texture1DTo2DTest, EmptyModule) {
+ auto* src = "";
+
+ DataMap data;
+ EXPECT_FALSE(ShouldRun<Texture1DTo2D>(src, data));
+}
+
+TEST_F(Texture1DTo2DTest, Global1DDecl) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_1d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, Global1DDeclAndSample) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_1d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+fn main() -> vec4<f32> {
+ return textureSample(t, s, 0.5);
+}
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+fn main() -> vec4<f32> {
+ return textureSample(t, s, vec2<f32>(0.5, 0.5));
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, Global1DDeclAndLoad) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_1d<f32>;
+
+fn main() -> vec4<f32> {
+ return textureLoad(t, 1, 0);
+}
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+fn main() -> vec4<f32> {
+ return textureLoad(t, vec2<i32>(1, 0), 0);
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, Global1DDeclAndTextureDimensions) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_1d<f32>;
+
+fn main() -> u32 {
+ return textureDimensions(t);
+}
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+fn main() -> u32 {
+ return textureDimensions(t).x;
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, Global1DDeclAndTextureNumLevels) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_1d<f32>;
+
+fn main() -> u32 {
+ return textureNumLevels(t);
+}
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+fn main() -> u32 {
+ return textureNumLevels(t);
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, Global1DDeclAndTextureDimensionsInCallStmt) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_1d<f32>;
+
+fn main() {
+ textureDimensions(t);
+}
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+fn main() {
+ textureDimensions(t);
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, GlobalStorage1DDecl) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_storage_1d<r32float, write>;
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_storage_2d<r32float, write>;
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, Global2DDeclAndSample) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+fn main() -> vec4<f32> {
+ return textureSample(t, s, vec2<f32>(0.5, 1.5));
+}
+)";
+
+ DataMap data;
+ EXPECT_FALSE(ShouldRun<Texture1DTo2D>(src, data));
+}
+
+TEST_F(Texture1DTo2DTest, PrivateIntNoop) {
+ auto* src = R"(
+var<private> i : i32;
+)";
+
+ DataMap data;
+ EXPECT_FALSE(ShouldRun<Texture1DTo2D>(src, data));
+}
+
+TEST_F(Texture1DTo2DTest, GlobalMatrixNoop) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> m : mat2x2<f32>;
+)";
+
+ DataMap data;
+ EXPECT_FALSE(ShouldRun<Texture1DTo2D>(src, data));
+}
+
+TEST_F(Texture1DTo2DTest, Texture1DFuncParam) {
+ auto* src = R"(
+@group(0) @binding(0) var tex : texture_1d<f32>;
+
+@group(0) @binding(1) var samp : sampler;
+
+fn f(t : texture_1d<f32>, s : sampler) -> vec4<f32> {
+ return textureSample(t, s, 0.7);
+}
+
+fn main() -> vec4<f32> {
+ return f(tex, samp);
+}
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var tex : texture_2d<f32>;
+
+@group(0) @binding(1) var samp : sampler;
+
+fn f(t : texture_2d<f32>, s : sampler) -> vec4<f32> {
+ return textureSample(t, s, vec2<f32>(0.7, 0.5));
+}
+
+fn main() -> vec4<f32> {
+ return f(tex, samp);
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, TextureStorage1DFuncParam) {
+ auto* src = R"(
+@group(0) @binding(0) var tex : texture_storage_1d<rgba8unorm, write>;
+
+fn f(t : texture_storage_1d<rgba8unorm, write>) {
+ textureStore(t, 3, vec4<f32>(42.0, 21.0, 84.0, 10.5));
+}
+
+fn main() {
+ f(tex);
+}
+)";
+
+ auto* expect = R"(
+@group(0) @binding(0) var tex : texture_storage_2d<rgba8unorm, write>;
+
+fn f(t : texture_storage_2d<rgba8unorm, write>) {
+ textureStore(t, vec2<i32>(3, 0), vec4<f32>(42.0, 21.0, 84.0, 10.5));
+}
+
+fn main() {
+ f(tex);
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+} // namespace
+} // namespace tint::transform