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:
parent
079617e3d0
commit
fb8a6dbb5d
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
Loading…
Reference in New Issue