Add MultiplanarExternalTextureTransform and Tests
Implements MultiplanarExternalTextureTransform to allow transforming a texture_external binding into two texture_2d<f32> bindings and a uniform buffer binding. Transforms textureSampleLevel and textureLoad calls with a texture_external parameter into custom functions that can handle both single-plane RGBA or bi-planar YUV. Includes tests. Bug: dawn:1082 Change-Id: Icb6d8b0f3773feca01c833171f07230c3531f3aa Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/68620 Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
ae4487f3f9
commit
bf7ec8f70b
|
@ -453,6 +453,8 @@ libtint_source_set("libtint_core_all_src") {
|
|||
"transform/manager.h",
|
||||
"transform/module_scope_var_to_entry_point_param.cc",
|
||||
"transform/module_scope_var_to_entry_point_param.h",
|
||||
"transform/multiplanar_external_texture.cc",
|
||||
"transform/multiplanar_external_texture.h",
|
||||
"transform/num_workgroups_from_uniform.cc",
|
||||
"transform/num_workgroups_from_uniform.h",
|
||||
"transform/pad_array_elements.cc",
|
||||
|
|
|
@ -317,6 +317,8 @@ set(TINT_LIB_SRCS
|
|||
transform/manager.h
|
||||
transform/module_scope_var_to_entry_point_param.cc
|
||||
transform/module_scope_var_to_entry_point_param.h
|
||||
transform/multiplanar_external_texture.cc
|
||||
transform/multiplanar_external_texture.h
|
||||
transform/num_workgroups_from_uniform.cc
|
||||
transform/num_workgroups_from_uniform.h
|
||||
transform/pad_array_elements.cc
|
||||
|
@ -954,6 +956,7 @@ if(${TINT_BUILD_TESTS})
|
|||
transform/inline_pointer_lets_test.cc
|
||||
transform/loop_to_for_loop_test.cc
|
||||
transform/module_scope_var_to_entry_point_param_test.cc
|
||||
transform/multiplanar_external_texture_test.cc
|
||||
transform/num_workgroups_from_uniform_test.cc
|
||||
transform/pad_array_elements_test.cc
|
||||
transform/promote_initializers_to_const_var_test.cc
|
||||
|
|
|
@ -0,0 +1,356 @@
|
|||
// Copyright 2021 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/transform/multiplanar_external_texture.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "src/ast/function.h"
|
||||
#include "src/program_builder.h"
|
||||
#include "src/sem/call.h"
|
||||
#include "src/sem/variable.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::transform::MultiplanarExternalTexture);
|
||||
TINT_INSTANTIATE_TYPEINFO(
|
||||
tint::transform::MultiplanarExternalTexture::NewBindingPoints);
|
||||
|
||||
namespace tint {
|
||||
namespace transform {
|
||||
namespace {
|
||||
|
||||
/// This struct stores symbols for new bindings created as a result of
|
||||
/// transforming a texture_external instance.
|
||||
struct NewBindingSymbols {
|
||||
Symbol ext_tex_params_binding_sym;
|
||||
Symbol ext_tex_plane_1_binding_sym;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/// State holds the current transform state
|
||||
struct MultiplanarExternalTexture::State {
|
||||
/// Symbol for the ExternalTextureParams struct
|
||||
Symbol external_texture_params_struct_sym;
|
||||
|
||||
/// Symbol for the textureLoadExternal function
|
||||
Symbol texture_load_external_sym;
|
||||
|
||||
/// Symbol for the textureSampleExternal function
|
||||
Symbol texture_sample_external_sym;
|
||||
|
||||
/// Storage for new bindings that have been created corresponding to an
|
||||
/// original texture_external binding.
|
||||
std::unordered_map<Symbol, NewBindingSymbols> new_binding_symbols;
|
||||
};
|
||||
|
||||
MultiplanarExternalTexture::NewBindingPoints::NewBindingPoints(
|
||||
BindingsMap inputBindingsMap)
|
||||
: bindings_map(std::move(inputBindingsMap)) {}
|
||||
MultiplanarExternalTexture::NewBindingPoints::~NewBindingPoints() = default;
|
||||
|
||||
MultiplanarExternalTexture::MultiplanarExternalTexture() = default;
|
||||
MultiplanarExternalTexture::~MultiplanarExternalTexture() = default;
|
||||
|
||||
// Within this transform, an instance of a texture_external binding is unpacked
|
||||
// into two texture_2d<f32> bindings representing two possible planes of a
|
||||
// single texture and a uniform buffer binding representing a struct of
|
||||
// parameters. Calls to textureLoad or textureSampleLevel that contain a
|
||||
// texture_external parameter will be transformed into a newly generated version
|
||||
// of the function, which can perform the desired operation on a single RGBA
|
||||
// plane or on seperate Y and UV planes.
|
||||
void MultiplanarExternalTexture::Run(CloneContext& ctx,
|
||||
const DataMap& inputs,
|
||||
DataMap&) {
|
||||
State state;
|
||||
auto& b = *ctx.dst;
|
||||
|
||||
auto* new_binding_points = inputs.Get<NewBindingPoints>();
|
||||
|
||||
if (!new_binding_points) {
|
||||
b.Diagnostics().add_error(
|
||||
diag::System::Transform,
|
||||
"missing new binding point data for " + std::string(TypeInfo().name));
|
||||
return;
|
||||
}
|
||||
|
||||
auto& sem = ctx.src->Sem();
|
||||
|
||||
// For each texture_external binding, we replace it with a texture_2d<f32>
|
||||
// binding and create two additional bindings (one texture_2d<f32> to
|
||||
// represent the secondary plane and one uniform buffer for the
|
||||
// ExternalTextureParams struct).
|
||||
ctx.ReplaceAll([&](const ast::Variable* var) -> const ast::Variable* {
|
||||
if (!::tint::Is<ast::ExternalTexture>(var->type)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If the decorations are empty, then this must be a texture_external being
|
||||
// passed as a function parameter. We need to unpack this into multiple
|
||||
// parameters - but this hasn't been implemented so produce an error.
|
||||
if (var->decorations.empty()) {
|
||||
b.Diagnostics().add_error(
|
||||
diag::System::Transform,
|
||||
"transforming a texture_external passed as a user-defined function "
|
||||
"parameter has not been implemented.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If we find a texture_external binding, we know we must emit the
|
||||
// ExternalTextureParams struct.
|
||||
if (!state.external_texture_params_struct_sym.IsValid()) {
|
||||
ast::StructMemberList member_list = {
|
||||
b.Member("numPlanes", b.ty.u32()), b.Member("vr", b.ty.f32()),
|
||||
b.Member("ug", b.ty.f32()), b.Member("vg", b.ty.f32()),
|
||||
b.Member("ub", b.ty.f32())};
|
||||
|
||||
state.external_texture_params_struct_sym =
|
||||
b.Symbols().New("ExternalTextureParams");
|
||||
|
||||
b.Structure(state.external_texture_params_struct_sym, member_list,
|
||||
ast::DecorationList{b.StructBlock()});
|
||||
}
|
||||
|
||||
// The binding points for the newly introduced bindings must have been
|
||||
// provided to this transform. We fetch the new binding points by
|
||||
// providing the original texture_external binding points into the
|
||||
// passed map.
|
||||
BindingPoint bp = {var->BindingPoint().group->value,
|
||||
var->BindingPoint().binding->value};
|
||||
BindingPoints bps;
|
||||
BindingsMap::const_iterator it = new_binding_points->bindings_map.find(bp);
|
||||
if (it == new_binding_points->bindings_map.end()) {
|
||||
b.Diagnostics().add_error(
|
||||
diag::System::Transform,
|
||||
"missing new binding points for texture_external at binding {" +
|
||||
std::to_string(bp.group) + "," + std::to_string(bp.binding) +
|
||||
"}");
|
||||
return nullptr;
|
||||
} else {
|
||||
bps = it->second;
|
||||
}
|
||||
|
||||
// Symbols for the newly created bindings must be saved so they can be
|
||||
// passed as parameters later. These are placed in a map and keyed by
|
||||
// the symbol associated with the texture_external binding that
|
||||
// corresponds with the new bindings.
|
||||
NewBindingSymbols new_binding_syms;
|
||||
new_binding_syms.ext_tex_plane_1_binding_sym =
|
||||
b.Symbols().New("ext_tex_plane_1");
|
||||
b.Global(new_binding_syms.ext_tex_plane_1_binding_sym,
|
||||
b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32()),
|
||||
b.GroupAndBinding(bps.plane_1.group, bps.plane_1.binding));
|
||||
|
||||
new_binding_syms.ext_tex_params_binding_sym =
|
||||
b.Symbols().New("ext_tex_params");
|
||||
b.Global(new_binding_syms.ext_tex_params_binding_sym,
|
||||
b.ty.type_name("ExternalTextureParams"),
|
||||
ast::StorageClass::kUniform,
|
||||
b.GroupAndBinding(bps.params.group, bps.params.binding));
|
||||
|
||||
// Replace the original texture_external binding with a texture_2d<f32>
|
||||
// binding.
|
||||
auto cloned_sym = ctx.Clone(var->symbol);
|
||||
ast::DecorationList cloned_decorations = ctx.Clone(var->decorations);
|
||||
const ast::Expression* cloned_constructor = ctx.Clone(var->constructor);
|
||||
state.new_binding_symbols[cloned_sym] = new_binding_syms;
|
||||
|
||||
return b.Var(cloned_sym,
|
||||
b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32()),
|
||||
cloned_constructor, cloned_decorations);
|
||||
});
|
||||
|
||||
// Transform the original textureLoad and textureSampleLevel calls into
|
||||
// textureLoadExternal and textureSampleExternal calls.
|
||||
ctx.ReplaceAll([&](const ast::CallExpression* expr)
|
||||
-> const ast::CallExpression* {
|
||||
auto* intrinsic = sem.Get(expr)->Target()->As<sem::Intrinsic>();
|
||||
|
||||
if (!intrinsic ||
|
||||
!intrinsic->Parameters()[0]->Type()->Is<sem::ExternalTexture>() ||
|
||||
intrinsic->Parameters().empty() ||
|
||||
intrinsic->Type() == sem::IntrinsicType::kTextureDimensions) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ast::Expression* ext_tex_plane_0_binding_param =
|
||||
ctx.Clone(expr->args[0]);
|
||||
|
||||
// Lookup the symbols for the new bindings using the symbol from the
|
||||
// original texture_external.
|
||||
Symbol ext_tex_plane_1_binding_sym =
|
||||
state
|
||||
.new_binding_symbols[ext_tex_plane_0_binding_param
|
||||
->As<ast::IdentifierExpression>()
|
||||
->symbol]
|
||||
.ext_tex_plane_1_binding_sym;
|
||||
Symbol ext_tex_params_binding_sym =
|
||||
state
|
||||
.new_binding_symbols[ext_tex_plane_0_binding_param
|
||||
->As<ast::IdentifierExpression>()
|
||||
->symbol]
|
||||
.ext_tex_params_binding_sym;
|
||||
|
||||
// If valid new binding locations were not provided earlier, we would
|
||||
// have been unable to create these symbols. An error message was
|
||||
// emitted earlier, so just return early to avoid internal compiler
|
||||
// errors and retain a clean error message.
|
||||
if (!ext_tex_plane_1_binding_sym.IsValid() ||
|
||||
!ext_tex_params_binding_sym.IsValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ast::IdentifierExpression* exp;
|
||||
ast::ExpressionList params;
|
||||
|
||||
if (intrinsic->Type() == sem::IntrinsicType::kTextureLoad) {
|
||||
if (expr->args.size() != 2) {
|
||||
TINT_ICE(Transform, b.Diagnostics())
|
||||
<< "expected textureLoad call with a texture_external "
|
||||
"to "
|
||||
"have 2 parameters, found "
|
||||
<< expr->args.size() << " parameters";
|
||||
}
|
||||
|
||||
if (!state.texture_load_external_sym.IsValid()) {
|
||||
state.texture_load_external_sym =
|
||||
b.Symbols().New("textureLoadExternal");
|
||||
|
||||
// Emit the textureLoadExternal function.
|
||||
ast::VariableList var_list = {
|
||||
b.Param("plane0", b.ty.sampled_texture(ast::TextureDimension::k2d,
|
||||
b.ty.f32())),
|
||||
b.Param("plane1", b.ty.sampled_texture(ast::TextureDimension::k2d,
|
||||
b.ty.f32())),
|
||||
b.Param("coord", b.ty.vec2(b.ty.i32())),
|
||||
b.Param("params",
|
||||
b.ty.type_name(state.external_texture_params_struct_sym))};
|
||||
|
||||
ast::StatementList statement_list =
|
||||
createTexFnExtStatementList(b, sem::IntrinsicType::kTextureLoad);
|
||||
|
||||
b.Func(state.texture_load_external_sym, var_list, b.ty.vec4(b.ty.f32()),
|
||||
statement_list, {});
|
||||
}
|
||||
|
||||
exp =
|
||||
b.create<ast::IdentifierExpression>(state.texture_load_external_sym);
|
||||
params = {ext_tex_plane_0_binding_param,
|
||||
b.Expr(ext_tex_plane_1_binding_sym), ctx.Clone(expr->args[1]),
|
||||
b.Expr(ext_tex_params_binding_sym)};
|
||||
} else if (intrinsic->Type() == sem::IntrinsicType::kTextureSampleLevel) {
|
||||
if (expr->args.size() != 3) {
|
||||
TINT_ICE(Transform, b.Diagnostics())
|
||||
<< "expected textureSampleLevel call with a "
|
||||
"texture_external to have 3 parameters, found "
|
||||
<< expr->args.size() << " parameters";
|
||||
}
|
||||
|
||||
if (!state.texture_sample_external_sym.IsValid()) {
|
||||
state.texture_sample_external_sym =
|
||||
b.Symbols().New("textureSampleExternal");
|
||||
|
||||
// Emit the textureSampleExternal function.
|
||||
ast::VariableList varList = {
|
||||
b.Param("plane0", b.ty.sampled_texture(ast::TextureDimension::k2d,
|
||||
b.ty.f32())),
|
||||
b.Param("plane1", b.ty.sampled_texture(ast::TextureDimension::k2d,
|
||||
b.ty.f32())),
|
||||
b.Param("smp", b.ty.sampler(ast::SamplerKind::kSampler)),
|
||||
b.Param("coord", b.ty.vec2(b.ty.f32())),
|
||||
b.Param("params",
|
||||
b.ty.type_name(state.external_texture_params_struct_sym))};
|
||||
|
||||
ast::StatementList statementList = createTexFnExtStatementList(
|
||||
b, sem::IntrinsicType::kTextureSampleLevel);
|
||||
|
||||
b.Func(state.texture_sample_external_sym, varList,
|
||||
b.ty.vec4(b.ty.f32()), statementList, {});
|
||||
}
|
||||
exp = b.create<ast::IdentifierExpression>(
|
||||
state.texture_sample_external_sym);
|
||||
params = {ext_tex_plane_0_binding_param,
|
||||
b.Expr(ext_tex_plane_1_binding_sym), ctx.Clone(expr->args[1]),
|
||||
ctx.Clone(expr->args[2]), b.Expr(ext_tex_params_binding_sym)};
|
||||
}
|
||||
|
||||
return b.Call(exp, params);
|
||||
});
|
||||
|
||||
ctx.Clone();
|
||||
}
|
||||
|
||||
// Constructs a StatementList containing all the statements making up the bodies
|
||||
// of the textureSampleExternal and textureLoadExternal functions.
|
||||
ast::StatementList MultiplanarExternalTexture::createTexFnExtStatementList(
|
||||
ProgramBuilder& b,
|
||||
sem::IntrinsicType callType) {
|
||||
using f32 = ProgramBuilder::f32;
|
||||
const ast::CallExpression* single_plane_call;
|
||||
const ast::CallExpression* plane_0_call;
|
||||
const ast::CallExpression* plane_1_call;
|
||||
if (callType == sem::IntrinsicType::kTextureSampleLevel) {
|
||||
// textureSampleLevel(plane0, smp, coord.xy, 0.0);
|
||||
single_plane_call =
|
||||
b.Call("textureSampleLevel", "plane0", "smp", "coord", 0.0f);
|
||||
// textureSampleLevel(plane0, smp, coord.xy, 0.0);
|
||||
plane_0_call = b.Call("textureSampleLevel", "plane0", "smp", "coord", 0.0f);
|
||||
// textureSampleLevel(plane1, smp, coord.xy, 0.0);
|
||||
plane_1_call = b.Call("textureSampleLevel", "plane1", "smp", "coord", 0.0f);
|
||||
} else if (callType == sem::IntrinsicType::kTextureLoad) {
|
||||
// textureLoad(plane0, coords.xy, 0);
|
||||
single_plane_call = b.Call("textureLoad", "plane0", "coord", 0);
|
||||
// textureLoad(plane0, coords.xy, 0);
|
||||
plane_0_call = b.Call("textureLoad", "plane0", "coord", 0);
|
||||
// textureLoad(plane1, coords.xy, 0);
|
||||
plane_1_call = b.Call("textureLoad", "plane1", "coord", 0);
|
||||
}
|
||||
|
||||
return {
|
||||
// if (params.numPlanes == 1u) {
|
||||
// return singlePlaneCall
|
||||
// }
|
||||
b.If(b.create<ast::BinaryExpression>(
|
||||
ast::BinaryOp::kEqual, b.MemberAccessor("params", "numPlanes"),
|
||||
b.Expr(1u)),
|
||||
b.Block(b.Return(single_plane_call))),
|
||||
// let y = plane0Call.r - 0.0625;
|
||||
b.Decl(b.Const("y", nullptr,
|
||||
b.Sub(b.MemberAccessor(plane_0_call, "r"), 0.0625f))),
|
||||
// let uv = plane1Call.rg - 0.5;
|
||||
b.Decl(b.Const("uv", nullptr,
|
||||
b.Sub(b.MemberAccessor(plane_1_call, "rg"), 0.5f))),
|
||||
// let u = uv.x;
|
||||
b.Decl(b.Const("u", nullptr, b.MemberAccessor("uv", "x"))),
|
||||
// let v = uv.y;
|
||||
b.Decl(b.Const("v", nullptr, b.MemberAccessor("uv", "y"))),
|
||||
// let r = 1.164 * y + params.vr * v;
|
||||
b.Decl(b.Const("r", nullptr,
|
||||
b.Add(b.Mul(1.164f, "y"),
|
||||
b.Mul(b.MemberAccessor("params", "vr"), "v")))),
|
||||
// let g = 1.164 * y - params.ug * u - params.vg * v;
|
||||
b.Decl(b.Const("g", nullptr,
|
||||
b.Sub(b.Sub(b.Mul(1.164f, "y"),
|
||||
b.Mul(b.MemberAccessor("params", "ug"), "u")),
|
||||
b.Mul(b.MemberAccessor("params", "vg"), "v")))),
|
||||
// let b = 1.164 * y + params.ub * u;
|
||||
b.Decl(b.Const("b", nullptr,
|
||||
b.Add(b.Mul(1.164f, "y"),
|
||||
b.Mul(b.MemberAccessor("params", "ub"), "u")))),
|
||||
// return vec4<f32>(r, g, b, 1.0);
|
||||
b.Return(b.vec4<f32>("r", "g", "b", 1.0f)),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace transform
|
||||
} // namespace tint
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright 2021 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_TRANSFORM_MULTIPLANAR_EXTERNAL_TEXTURE_H_
|
||||
#define SRC_TRANSFORM_MULTIPLANAR_EXTERNAL_TEXTURE_H_
|
||||
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "src/ast/struct_member.h"
|
||||
#include "src/sem/binding_point.h"
|
||||
#include "src/sem/intrinsic_type.h"
|
||||
#include "src/transform/transform.h"
|
||||
|
||||
namespace tint {
|
||||
namespace transform {
|
||||
|
||||
/// BindingPoint is an alias to sem::BindingPoint
|
||||
using BindingPoint = sem::BindingPoint;
|
||||
|
||||
/// This struct identifies the binding groups and locations for new bindings to
|
||||
/// use when transforming a texture_external instance.
|
||||
struct BindingPoints {
|
||||
/// The desired binding location of the texture_2d representing plane #1 when
|
||||
/// a texture_external binding is expanded.
|
||||
BindingPoint plane_1;
|
||||
/// The desired binding location of the ExternalTextureParams uniform when a
|
||||
/// texture_external binding is expanded.
|
||||
BindingPoint params;
|
||||
};
|
||||
|
||||
/// Within the MultiplanarExternalTexture transform, each instance of a
|
||||
/// texture_external binding is unpacked into two texture_2d<f32> bindings
|
||||
/// representing two possible planes of a texture and a uniform buffer binding
|
||||
/// representing a struct of parameters. Calls to textureLoad or
|
||||
/// textureSampleLevel that contain a texture_external parameter will be
|
||||
/// transformed into a newly generated version of the function, which can
|
||||
/// perform the desired operation on a single RGBA plane or on seperate Y and UV
|
||||
/// planes.
|
||||
class MultiplanarExternalTexture
|
||||
: public Castable<MultiplanarExternalTexture, Transform> {
|
||||
public:
|
||||
/// BindingsMap is a map where the key is the binding location of a
|
||||
/// texture_external and the value is a struct containing the desired
|
||||
/// locations for new bindings expanded from the texture_external instance.
|
||||
using BindingsMap = std::unordered_map<BindingPoint, BindingPoints>;
|
||||
|
||||
/// NewBindingPoints is consumed by the MultiplanarExternalTexture transform.
|
||||
/// Data holds information about location of each texture_external binding and
|
||||
/// which binding slots it should expand into.
|
||||
struct NewBindingPoints : public Castable<Data, transform::Data> {
|
||||
/// Constructor
|
||||
/// @param bm a map to the new binding slots to use.
|
||||
explicit NewBindingPoints(BindingsMap bm);
|
||||
|
||||
/// Destructor
|
||||
~NewBindingPoints() override;
|
||||
|
||||
/// A map of new binding points to use.
|
||||
const BindingsMap bindings_map;
|
||||
};
|
||||
|
||||
/// Constructor
|
||||
MultiplanarExternalTexture();
|
||||
/// Destructor
|
||||
~MultiplanarExternalTexture() override;
|
||||
|
||||
protected:
|
||||
struct State;
|
||||
|
||||
/// Runs the transform using the CloneContext built for transforming a
|
||||
/// program. Run() is responsible for calling Clone() on the CloneContext.
|
||||
/// @param ctx the CloneContext primed with the input program and
|
||||
/// ProgramBuilder
|
||||
/// @param inputs optional extra transform-specific input data
|
||||
/// @param outputs optional extra transform-specific output data
|
||||
void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) override;
|
||||
|
||||
/// Creates the statement list for the TextureSampleExternal and
|
||||
/// TextureLoadExternal functions.
|
||||
/// @param b a reference to the ProgramBuilder associated with the destination
|
||||
/// context
|
||||
/// @param callType determines the kind of param list to emit (either
|
||||
/// textureLoad or textureSampleLevel)
|
||||
/// @returns a statement list that is used to create the TextureSampleExternal
|
||||
/// and TextureLoadExternal functions.
|
||||
ast::StatementList createTexFnExtStatementList(ProgramBuilder& b,
|
||||
sem::IntrinsicType callType);
|
||||
};
|
||||
|
||||
} // namespace transform
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_TRANSFORM_MULTIPLANAR_EXTERNAL_TEXTURE_H_
|
|
@ -0,0 +1,408 @@
|
|||
// Copyright 2021 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/transform/multiplanar_external_texture.h"
|
||||
#include "src/transform/test_helper.h"
|
||||
|
||||
namespace tint {
|
||||
namespace transform {
|
||||
namespace {
|
||||
|
||||
using MultiplanarExternalTextureTest = TransformTest;
|
||||
|
||||
// Running the transform without passing in data for the new bindings should
|
||||
// result in an error.
|
||||
TEST_F(MultiplanarExternalTextureTest, ErrorNoPassedData) {
|
||||
auto* src = R"(
|
||||
[[group(0), binding(0)]] var s : sampler;
|
||||
[[group(0), binding(1)]] var ext_tex : texture_external;
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main([[builtin(position)]] coord : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
||||
return textureSampleLevel(ext_tex, s, coord.xy);
|
||||
}
|
||||
)";
|
||||
auto* expect =
|
||||
R"(error: missing new binding point data for tint::transform::MultiplanarExternalTexture)";
|
||||
|
||||
auto got = Run<MultiplanarExternalTexture>(src);
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
// Running the transform with incorrect binding data should result in an error.
|
||||
TEST_F(MultiplanarExternalTextureTest, ErrorIncorrectBindingPont) {
|
||||
auto* src = R"(
|
||||
[[group(0), binding(0)]] var s : sampler;
|
||||
[[group(0), binding(1)]] var ext_tex : texture_external;
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main([[builtin(position)]] coord : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
||||
return textureSampleLevel(ext_tex, s, coord.xy);
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect =
|
||||
R"(error: missing new binding points for texture_external at binding {0,1})";
|
||||
|
||||
DataMap data;
|
||||
// This bindings map specifies 0,0 as the location of the texture_external,
|
||||
// which is incorrect.
|
||||
data.Add<MultiplanarExternalTexture::NewBindingPoints>(
|
||||
MultiplanarExternalTexture::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
|
||||
auto got = Run<MultiplanarExternalTexture>(src, data);
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
// Tests that the transform works with a textureDimensions call.
|
||||
TEST_F(MultiplanarExternalTextureTest, Dimensions) {
|
||||
auto* src = R"(
|
||||
[[group(0), binding(0)]] var ext_tex : texture_external;
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main([[builtin(position)]] coord : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
||||
var dim : vec2<i32>;
|
||||
dim = textureDimensions(ext_tex);
|
||||
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
[[block]]
|
||||
struct ExternalTextureParams {
|
||||
numPlanes : u32;
|
||||
vr : f32;
|
||||
ug : f32;
|
||||
vg : f32;
|
||||
ub : f32;
|
||||
};
|
||||
|
||||
[[group(0), binding(1)]] var ext_tex_plane_1 : texture_2d<f32>;
|
||||
|
||||
[[group(0), binding(2)]] var<uniform> ext_tex_params : ExternalTextureParams;
|
||||
|
||||
[[group(0), binding(0)]] var ext_tex : texture_2d<f32>;
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main([[builtin(position)]] coord : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
||||
var dim : vec2<i32>;
|
||||
dim = textureDimensions(ext_tex);
|
||||
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
data.Add<MultiplanarExternalTexture::NewBindingPoints>(
|
||||
MultiplanarExternalTexture::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
|
||||
auto got = Run<MultiplanarExternalTexture>(src, data);
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
// Test that the transform works with a textureSampleLevel call.
|
||||
TEST_F(MultiplanarExternalTextureTest, BasicTextureSampleLevel) {
|
||||
auto* src = R"(
|
||||
[[group(0), binding(0)]] var s : sampler;
|
||||
[[group(0), binding(1)]] var ext_tex : texture_external;
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main([[builtin(position)]] coord : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
||||
return textureSampleLevel(ext_tex, s, coord.xy);
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
[[group(0), binding(0)]] var s : sampler;
|
||||
|
||||
[[block]]
|
||||
struct ExternalTextureParams {
|
||||
numPlanes : u32;
|
||||
vr : f32;
|
||||
ug : f32;
|
||||
vg : f32;
|
||||
ub : f32;
|
||||
};
|
||||
|
||||
[[group(0), binding(2)]] var ext_tex_plane_1 : texture_2d<f32>;
|
||||
|
||||
[[group(0), binding(3)]] var<uniform> ext_tex_params : ExternalTextureParams;
|
||||
|
||||
[[group(0), binding(1)]] var ext_tex : texture_2d<f32>;
|
||||
|
||||
fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
|
||||
if ((params.numPlanes == 1u)) {
|
||||
return textureSampleLevel(plane0, smp, coord, 0.0);
|
||||
}
|
||||
let y = (textureSampleLevel(plane0, smp, coord, 0.0).r - 0.0625);
|
||||
let uv = (textureSampleLevel(plane1, smp, coord, 0.0).rg - 0.5);
|
||||
let u = uv.x;
|
||||
let v = uv.y;
|
||||
let r = ((1.164000034 * y) + (params.vr * v));
|
||||
let g = (((1.164000034 * y) - (params.ug * u)) - (params.vg * v));
|
||||
let b = ((1.164000034 * y) + (params.ub * u));
|
||||
return vec4<f32>(r, g, b, 1.0);
|
||||
}
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main([[builtin(position)]] coord : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
||||
return textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params);
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
data.Add<MultiplanarExternalTexture::NewBindingPoints>(
|
||||
MultiplanarExternalTexture::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
|
||||
auto got = Run<MultiplanarExternalTexture>(src, data);
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
// Tests that the transform works with a textureLoad call.
|
||||
TEST_F(MultiplanarExternalTextureTest, BasicTextureLoad) {
|
||||
auto* src = R"(
|
||||
[[group(0), binding(0)]] var ext_tex : texture_external;
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main([[builtin(position)]] coord : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
||||
return textureLoad(ext_tex, vec2<i32>(1, 1));
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
[[block]]
|
||||
struct ExternalTextureParams {
|
||||
numPlanes : u32;
|
||||
vr : f32;
|
||||
ug : f32;
|
||||
vg : f32;
|
||||
ub : f32;
|
||||
};
|
||||
|
||||
[[group(0), binding(1)]] var ext_tex_plane_1 : texture_2d<f32>;
|
||||
|
||||
[[group(0), binding(2)]] var<uniform> ext_tex_params : ExternalTextureParams;
|
||||
|
||||
[[group(0), binding(0)]] var ext_tex : texture_2d<f32>;
|
||||
|
||||
fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
|
||||
if ((params.numPlanes == 1u)) {
|
||||
return textureLoad(plane0, coord, 0);
|
||||
}
|
||||
let y = (textureLoad(plane0, coord, 0).r - 0.0625);
|
||||
let uv = (textureLoad(plane1, coord, 0).rg - 0.5);
|
||||
let u = uv.x;
|
||||
let v = uv.y;
|
||||
let r = ((1.164000034 * y) + (params.vr * v));
|
||||
let g = (((1.164000034 * y) - (params.ug * u)) - (params.vg * v));
|
||||
let b = ((1.164000034 * y) + (params.ub * u));
|
||||
return vec4<f32>(r, g, b, 1.0);
|
||||
}
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main([[builtin(position)]] coord : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
||||
return textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1, 1), ext_tex_params);
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
data.Add<MultiplanarExternalTexture::NewBindingPoints>(
|
||||
MultiplanarExternalTexture::BindingsMap{{{0, 0}, {{0, 1}, {0, 2}}}});
|
||||
auto got = Run<MultiplanarExternalTexture>(src, data);
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
// Tests that the transform works with both a textureSampleLevel and textureLoad
|
||||
// call.
|
||||
TEST_F(MultiplanarExternalTextureTest, TextureSampleAndTextureLoad) {
|
||||
auto* src = R"(
|
||||
[[group(0), binding(0)]] var s : sampler;
|
||||
[[group(0), binding(1)]] var ext_tex : texture_external;
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main([[builtin(position)]] coord : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
||||
return textureSampleLevel(ext_tex, s, coord.xy) + textureLoad(ext_tex, vec2<i32>(1, 1));
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
[[group(0), binding(0)]] var s : sampler;
|
||||
|
||||
[[block]]
|
||||
struct ExternalTextureParams {
|
||||
numPlanes : u32;
|
||||
vr : f32;
|
||||
ug : f32;
|
||||
vg : f32;
|
||||
ub : f32;
|
||||
};
|
||||
|
||||
[[group(0), binding(2)]] var ext_tex_plane_1 : texture_2d<f32>;
|
||||
|
||||
[[group(0), binding(3)]] var<uniform> ext_tex_params : ExternalTextureParams;
|
||||
|
||||
[[group(0), binding(1)]] var ext_tex : texture_2d<f32>;
|
||||
|
||||
fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
|
||||
if ((params.numPlanes == 1u)) {
|
||||
return textureSampleLevel(plane0, smp, coord, 0.0);
|
||||
}
|
||||
let y = (textureSampleLevel(plane0, smp, coord, 0.0).r - 0.0625);
|
||||
let uv = (textureSampleLevel(plane1, smp, coord, 0.0).rg - 0.5);
|
||||
let u = uv.x;
|
||||
let v = uv.y;
|
||||
let r = ((1.164000034 * y) + (params.vr * v));
|
||||
let g = (((1.164000034 * y) - (params.ug * u)) - (params.vg * v));
|
||||
let b = ((1.164000034 * y) + (params.ub * u));
|
||||
return vec4<f32>(r, g, b, 1.0);
|
||||
}
|
||||
|
||||
fn textureLoadExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, coord : vec2<i32>, params : ExternalTextureParams) -> vec4<f32> {
|
||||
if ((params.numPlanes == 1u)) {
|
||||
return textureLoad(plane0, coord, 0);
|
||||
}
|
||||
let y = (textureLoad(plane0, coord, 0).r - 0.0625);
|
||||
let uv = (textureLoad(plane1, coord, 0).rg - 0.5);
|
||||
let u = uv.x;
|
||||
let v = uv.y;
|
||||
let r = ((1.164000034 * y) + (params.vr * v));
|
||||
let g = (((1.164000034 * y) - (params.ug * u)) - (params.vg * v));
|
||||
let b = ((1.164000034 * y) + (params.ub * u));
|
||||
return vec4<f32>(r, g, b, 1.0);
|
||||
}
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main([[builtin(position)]] coord : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
||||
return (textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params) + textureLoadExternal(ext_tex, ext_tex_plane_1, vec2<i32>(1, 1), ext_tex_params));
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
data.Add<MultiplanarExternalTexture::NewBindingPoints>(
|
||||
MultiplanarExternalTexture::BindingsMap{{{0, 1}, {{0, 2}, {0, 3}}}});
|
||||
auto got = Run<MultiplanarExternalTexture>(src, data);
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
// Tests that the transform works with many instances of texture_external.
|
||||
TEST_F(MultiplanarExternalTextureTest, ManyTextureSampleLevel) {
|
||||
auto* src = R"(
|
||||
[[group(0), binding(0)]] var s : sampler;
|
||||
[[group(0), binding(1)]] var ext_tex : texture_external;
|
||||
[[group(0), binding(2)]] var ext_tex_1 : texture_external;
|
||||
[[group(0), binding(3)]] var ext_tex_2 : texture_external;
|
||||
[[group(1), binding(0)]] var ext_tex_3 : texture_external;
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main([[builtin(position)]] coord : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
||||
return textureSampleLevel(ext_tex, s, coord.xy) + textureSampleLevel(ext_tex_1, s, coord.xy) + textureSampleLevel(ext_tex_2, s, coord.xy) + textureSampleLevel(ext_tex_3, s, coord.xy);
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
[[group(0), binding(0)]] var s : sampler;
|
||||
|
||||
[[block]]
|
||||
struct ExternalTextureParams {
|
||||
numPlanes : u32;
|
||||
vr : f32;
|
||||
ug : f32;
|
||||
vg : f32;
|
||||
ub : f32;
|
||||
};
|
||||
|
||||
[[group(0), binding(4)]] var ext_tex_plane_1 : texture_2d<f32>;
|
||||
|
||||
[[group(0), binding(5)]] var<uniform> ext_tex_params : ExternalTextureParams;
|
||||
|
||||
[[group(0), binding(1)]] var ext_tex : texture_2d<f32>;
|
||||
|
||||
[[group(0), binding(6)]] var ext_tex_plane_1_1 : texture_2d<f32>;
|
||||
|
||||
[[group(0), binding(7)]] var<uniform> ext_tex_params_1 : ExternalTextureParams;
|
||||
|
||||
[[group(0), binding(2)]] var ext_tex_1 : texture_2d<f32>;
|
||||
|
||||
[[group(0), binding(8)]] var ext_tex_plane_1_2 : texture_2d<f32>;
|
||||
|
||||
[[group(0), binding(9)]] var<uniform> ext_tex_params_2 : ExternalTextureParams;
|
||||
|
||||
[[group(0), binding(3)]] var ext_tex_2 : texture_2d<f32>;
|
||||
|
||||
[[group(1), binding(1)]] var ext_tex_plane_1_3 : texture_2d<f32>;
|
||||
|
||||
[[group(1), binding(2)]] var<uniform> ext_tex_params_3 : ExternalTextureParams;
|
||||
|
||||
[[group(1), binding(0)]] var ext_tex_3 : texture_2d<f32>;
|
||||
|
||||
fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
|
||||
if ((params.numPlanes == 1u)) {
|
||||
return textureSampleLevel(plane0, smp, coord, 0.0);
|
||||
}
|
||||
let y = (textureSampleLevel(plane0, smp, coord, 0.0).r - 0.0625);
|
||||
let uv = (textureSampleLevel(plane1, smp, coord, 0.0).rg - 0.5);
|
||||
let u = uv.x;
|
||||
let v = uv.y;
|
||||
let r = ((1.164000034 * y) + (params.vr * v));
|
||||
let g = (((1.164000034 * y) - (params.ug * u)) - (params.vg * v));
|
||||
let b = ((1.164000034 * y) + (params.ub * u));
|
||||
return vec4<f32>(r, g, b, 1.0);
|
||||
}
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main([[builtin(position)]] coord : vec4<f32>) -> [[location(0)]] vec4<f32> {
|
||||
return (((textureSampleExternal(ext_tex, ext_tex_plane_1, s, coord.xy, ext_tex_params) + textureSampleExternal(ext_tex_1, ext_tex_plane_1_1, s, coord.xy, ext_tex_params_1)) + textureSampleExternal(ext_tex_2, ext_tex_plane_1_2, s, coord.xy, ext_tex_params_2)) + textureSampleExternal(ext_tex_3, ext_tex_plane_1_3, s, coord.xy, ext_tex_params_3));
|
||||
}
|
||||
)";
|
||||
|
||||
DataMap data;
|
||||
data.Add<MultiplanarExternalTexture::NewBindingPoints>(
|
||||
MultiplanarExternalTexture::BindingsMap{
|
||||
{{0, 1}, {{0, 4}, {0, 5}}},
|
||||
{{0, 2}, {{0, 6}, {0, 7}}},
|
||||
{{0, 3}, {{0, 8}, {0, 9}}},
|
||||
{{1, 0}, {{1, 1}, {1, 2}}},
|
||||
});
|
||||
auto got = Run<MultiplanarExternalTexture>(src, data);
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
// Tests that the texture_external passed as a function parameter produces the
|
||||
// correct output.
|
||||
TEST_F(MultiplanarExternalTextureTest, ExternalTexturePassedAsParam) {
|
||||
auto* src = R"(
|
||||
fn f(t : texture_external, s : sampler) {
|
||||
textureSampleLevel(t, s, vec2<f32>(1.0, 2.0));
|
||||
}
|
||||
|
||||
[[group(0), binding(0)]] var ext_tex : texture_external;
|
||||
[[group(0), binding(1)]] var smp : sampler;
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn main() {
|
||||
f(ext_tex, smp);
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect =
|
||||
"error: transforming a texture_external passed as a user-defined "
|
||||
"function parameter has not been implemented.";
|
||||
DataMap data;
|
||||
data.Add<MultiplanarExternalTexture::NewBindingPoints>(
|
||||
MultiplanarExternalTexture::BindingsMap{
|
||||
{{0, 0}, {{0, 2}, {0, 3}}},
|
||||
});
|
||||
auto got = Run<MultiplanarExternalTexture>(src, data);
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace transform
|
||||
} // namespace tint
|
|
@ -316,6 +316,7 @@ tint_unittests_source_set("tint_unittests_transform_src") {
|
|||
"../src/transform/inline_pointer_lets_test.cc",
|
||||
"../src/transform/loop_to_for_loop_test.cc",
|
||||
"../src/transform/module_scope_var_to_entry_point_param_test.cc",
|
||||
"../src/transform/multiplanar_external_texture_test.cc",
|
||||
"../src/transform/num_workgroups_from_uniform_test.cc",
|
||||
"../src/transform/pad_array_elements_test.cc",
|
||||
"../src/transform/promote_initializers_to_const_var_test.cc",
|
||||
|
|
Loading…
Reference in New Issue