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>
This commit is contained in:
Stephen White 2023-01-07 04:09:25 +00:00 committed by Dawn LUCI CQ
parent 079617e3d0
commit fb8a6dbb5d
5 changed files with 514 additions and 0 deletions

View File

@ -535,6 +535,8 @@ libtint_source_set("libtint_core_all_src") {
"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 @@ if (tint_build_unittests) {
"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",

View File

@ -441,6 +441,8 @@ list(APPEND TINT_LIB_SRCS
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 @@ if(TINT_BUILD_TESTS)
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

View File

@ -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

View File

@ -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_

View File

@ -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