| // Copyright 2021 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "src/tint/lang/core/type/texture_dimension.h" |
| #include "src/tint/lang/wgsl/ast/function.h" |
| #include "src/tint/lang/wgsl/program/clone_context.h" |
| #include "src/tint/lang/wgsl/program/program_builder.h" |
| #include "src/tint/lang/wgsl/resolver/resolve.h" |
| #include "src/tint/lang/wgsl/sem/call.h" |
| #include "src/tint/lang/wgsl/sem/function.h" |
| #include "src/tint/lang/wgsl/sem/variable.h" |
| |
| TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::MultiplanarExternalTexture); |
| TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::MultiplanarExternalTexture::NewBindingPoints); |
| |
| namespace tint::ast::transform { |
| namespace { |
| |
| using namespace tint::core::fluent_types; // NOLINT |
| using namespace tint::core::number_suffixes; // NOLINT |
| |
| bool ShouldRun(const Program& program) { |
| auto ext = program.Types().Find<core::type::ExternalTexture>(); |
| return ext != nullptr; |
| } |
| |
| /// This struct stores symbols for new bindings created as a result of transforming a |
| /// texture_external instance. |
| struct NewBindingSymbols { |
| Symbol params; |
| Symbol plane_0; |
| Symbol plane_1; |
| }; |
| } // namespace |
| |
| /// PIMPL state for the transform |
| struct MultiplanarExternalTexture::State { |
| /// The clone context. |
| program::CloneContext& ctx; |
| |
| /// Alias to `*ctx.dst` |
| ast::Builder& b; |
| |
| /// Destination binding locations for the expanded texture_external provided |
| /// as input into the transform. |
| const NewBindingPoints* new_binding_points; |
| |
| /// Symbol for the GammaTransferParams |
| Symbol gamma_transfer_struct_sym; |
| |
| /// Symbol for the ExternalTextureParams struct |
| Symbol params_struct_sym; |
| |
| /// Symbol for the textureLoadExternal functions |
| Hashmap<const sem::CallTarget*, Symbol, 2> texture_load_external_fns; |
| |
| /// Symbol for the textureSampleExternal function |
| Symbol texture_sample_external_sym; |
| |
| /// Symbol for the textureSampleExternalDEPRECATED function |
| Symbol texture_sample_external_deprecated_sym; |
| |
| /// Symbol for the gammaCorrection function |
| Symbol gamma_correction_sym; |
| |
| /// Storage for new bindings that have been created corresponding to an original |
| /// texture_external binding. |
| std::unordered_map<const sem::Variable*, NewBindingSymbols> new_binding_symbols; |
| |
| /// Constructor |
| /// @param context the clone |
| /// @param newBindingPoints the input destination binding locations for the |
| /// expanded texture_external |
| State(program::CloneContext& context, const NewBindingPoints* newBindingPoints) |
| : ctx(context), b(*context.dst), new_binding_points(newBindingPoints) {} |
| |
| /// Processes the module |
| void Process() { |
| 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). |
| for (auto* global : ctx.src->AST().GlobalVariables()) { |
| auto* sem_var = sem.Get<sem::GlobalVariable>(global); |
| if (!sem_var->Type()->UnwrapRef()->Is<core::type::ExternalTexture>()) { |
| continue; |
| } |
| |
| // If the attributes are empty, then this must be a texture_external passed as a |
| // function parameter. These variables are transformed elsewhere. |
| if (global->attributes.IsEmpty()) { |
| continue; |
| } |
| |
| // If we find a texture_external binding, we know we must emit the ExternalTextureParams |
| // struct. |
| if (!params_struct_sym.IsValid()) { |
| createExtTexParamsStructs(); |
| } |
| |
| // 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 = *sem_var->Attributes().binding_point; |
| |
| const tint::transform::multiplanar::BindingsMap::const_iterator it = |
| new_binding_points->bindings_map.find(bp); |
| if (it == new_binding_points->bindings_map.end()) { |
| b.Diagnostics().AddError(Source{}) |
| << "missing new binding points for texture_external at binding {" << bp.group |
| << "," << bp.binding << "}"; |
| continue; |
| } |
| |
| const tint::transform::multiplanar::BindingPoints 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 source symbol associated |
| // with the texture_external binding that corresponds with the new destination bindings. |
| // NewBindingSymbols new_binding_syms; |
| auto& syms = new_binding_symbols[sem_var]; |
| syms.plane_0 = ctx.Clone(global->name->symbol); |
| syms.plane_1 = b.Symbols().New("ext_tex_plane_1"); |
| if (new_binding_points->allow_collisions) { |
| b.GlobalVar(syms.plane_1, |
| b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32()), |
| b.Disable(DisabledValidation::kBindingPointCollision), |
| b.Group(AInt(bps.plane_1.group)), b.Binding(AInt(bps.plane_1.binding))); |
| } else { |
| b.GlobalVar(syms.plane_1, |
| b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32()), |
| b.Group(AInt(bps.plane_1.group)), b.Binding(AInt(bps.plane_1.binding))); |
| } |
| syms.params = b.Symbols().New("ext_tex_params"); |
| if (new_binding_points->allow_collisions) { |
| b.GlobalVar(syms.params, b.ty("ExternalTextureParams"), |
| core::AddressSpace::kUniform, |
| b.Disable(DisabledValidation::kBindingPointCollision), |
| b.Group(AInt(bps.params.group)), b.Binding(AInt(bps.params.binding))); |
| } else { |
| b.GlobalVar(syms.params, b.ty("ExternalTextureParams"), |
| core::AddressSpace::kUniform, b.Group(AInt(bps.params.group)), |
| b.Binding(AInt(bps.params.binding))); |
| } |
| |
| // Replace the original texture_external binding with a texture_2d<f32> binding. |
| auto cloned_attributes = ctx.Clone(global->attributes); |
| |
| // Allow the originating binding to have collisions. |
| if (new_binding_points->allow_collisions) { |
| cloned_attributes.Push(b.Disable(DisabledValidation::kBindingPointCollision)); |
| } |
| |
| const Expression* cloned_initializer = ctx.Clone(global->initializer); |
| |
| auto* replacement = b.Var( |
| syms.plane_0, b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32()), |
| cloned_initializer, cloned_attributes); |
| ctx.Replace(global, replacement); |
| } |
| |
| // We must update all the texture_external parameters for user declared functions. |
| for (auto* fn : ctx.src->AST().Functions()) { |
| for (const Variable* param : fn->params) { |
| if (auto* sem_var = sem.Get(param)) { |
| if (!sem_var->Type()->UnwrapRef()->Is<core::type::ExternalTexture>()) { |
| continue; |
| } |
| // If we find a texture_external, we must ensure the ExternalTextureParams |
| // struct exists. |
| if (!params_struct_sym.IsValid()) { |
| createExtTexParamsStructs(); |
| } |
| // When a texture_external is found, we insert all components the |
| // texture_external into the parameter list. We must also place the new symbols |
| // into the transform state so they can be used when transforming function |
| // calls. |
| auto& syms = new_binding_symbols[sem_var]; |
| syms.plane_0 = ctx.Clone(param->name->symbol); |
| syms.plane_1 = b.Symbols().New("ext_tex_plane_1"); |
| syms.params = b.Symbols().New("ext_tex_params"); |
| auto tex2d_f32 = [&] { |
| return b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32()); |
| }; |
| ctx.Replace(param, b.Param(syms.plane_0, tex2d_f32())); |
| ctx.InsertAfter(fn->params, param, b.Param(syms.plane_1, tex2d_f32())); |
| ctx.InsertAfter(fn->params, param, |
| b.Param(syms.params, b.ty(params_struct_sym))); |
| } |
| } |
| } |
| |
| // Transform the external texture builtin calls into calls to the external texture |
| // functions. |
| ctx.ReplaceAll([&](const CallExpression* expr) -> const Expression* { |
| auto* call = sem.Get(expr)->UnwrapMaterialize()->As<sem::Call>(); |
| auto* builtin = call->Target()->As<sem::BuiltinFn>(); |
| |
| if (builtin && !builtin->Parameters().IsEmpty() && |
| builtin->Parameters()[0]->Type()->Is<core::type::ExternalTexture>()) { |
| if (auto* var_user = |
| sem.GetVal(expr->args[0])->UnwrapLoad()->As<sem::VariableUser>()) { |
| auto it = new_binding_symbols.find(var_user->Variable()); |
| if (it == new_binding_symbols.end()) { |
| // 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. |
| return nullptr; |
| } |
| auto& syms = it->second; |
| |
| switch (builtin->Fn()) { |
| case wgsl::BuiltinFn::kTextureLoad: |
| return createTextureLoad(call, syms); |
| case wgsl::BuiltinFn::kTextureSampleBaseClampToEdge: |
| return createTextureSampleBaseClampToEdge(expr, syms); |
| case wgsl::BuiltinFn::kTextureDimensions: |
| return createTextureDimensions(call, syms); |
| default: |
| break; |
| } |
| } |
| } else if (call->Target()->Is<sem::Function>()) { |
| // The call expression may be to a user-defined function that contains a |
| // texture_external parameter. These need to be expanded out to multiple plane |
| // textures and the texture parameters structure. |
| for (auto* arg : expr->args) { |
| if (auto* var_user = sem.GetVal(arg)->UnwrapLoad()->As<sem::VariableUser>()) { |
| // Check if a parameter is a texture_external by trying to find |
| // it in the transform state. |
| auto it = new_binding_symbols.find(var_user->Variable()); |
| if (it != new_binding_symbols.end()) { |
| auto& syms = it->second; |
| // When we find a texture_external, we must unpack it into its |
| // components. |
| ctx.Replace(arg, b.Expr(syms.plane_0)); |
| ctx.InsertAfter(expr->args, arg, b.Expr(syms.plane_1)); |
| ctx.InsertAfter(expr->args, arg, b.Expr(syms.params)); |
| } |
| } |
| } |
| } |
| |
| return nullptr; |
| }); |
| } |
| |
| /// Creates the parameter structs associated with the transform. |
| void createExtTexParamsStructs() { |
| // Create GammaTransferParams struct. |
| tint::Vector gamma_transfer_member_list{ |
| b.Member("G", b.ty.f32()), b.Member("A", b.ty.f32()), b.Member("B", b.ty.f32()), |
| b.Member("C", b.ty.f32()), b.Member("D", b.ty.f32()), b.Member("E", b.ty.f32()), |
| b.Member("F", b.ty.f32()), b.Member("padding", b.ty.u32())}; |
| |
| gamma_transfer_struct_sym = b.Symbols().New("GammaTransferParams"); |
| |
| b.Structure(gamma_transfer_struct_sym, gamma_transfer_member_list); |
| |
| // Create ExternalTextureParams struct. |
| tint::Vector ext_tex_params_member_list{ |
| b.Member("numPlanes", b.ty.u32()), |
| b.Member("doYuvToRgbConversionOnly", b.ty.u32()), |
| b.Member("yuvToRgbConversionMatrix", b.ty.mat3x4<f32>()), |
| b.Member("gammaDecodeParams", b.ty("GammaTransferParams")), |
| b.Member("gammaEncodeParams", b.ty("GammaTransferParams")), |
| b.Member("gamutConversionMatrix", b.ty.mat3x3<f32>()), |
| b.Member("sampleTransform", b.ty.mat3x2<f32>()), |
| b.Member("loadTransform", b.ty.mat3x2<f32>()), |
| b.Member("samplePlane0RectMin", b.ty.vec2<f32>()), |
| b.Member("samplePlane0RectMax", b.ty.vec2<f32>()), |
| b.Member("samplePlane1RectMin", b.ty.vec2<f32>()), |
| b.Member("samplePlane1RectMax", b.ty.vec2<f32>()), |
| b.Member("visibleSize", b.ty.vec2<u32>()), |
| b.Member("plane1CoordFactor", b.ty.vec2<f32>())}; |
| |
| params_struct_sym = b.Symbols().New("ExternalTextureParams"); |
| |
| b.Structure(params_struct_sym, ext_tex_params_member_list); |
| } |
| |
| /// Creates the gammaCorrection function if needed and returns a call |
| /// expression to it. |
| void createGammaCorrectionFn() { |
| gamma_correction_sym = b.Symbols().New("gammaCorrection"); |
| |
| b.Func(gamma_correction_sym, |
| tint::Vector{ |
| b.Param("v", b.ty.vec3<f32>()), |
| b.Param("params", b.ty(gamma_transfer_struct_sym)), |
| }, |
| b.ty.vec3<f32>(), |
| tint::Vector{ |
| // let cond = abs(v) < vec3(params.D); |
| b.Decl(b.Let("cond", |
| b.LessThan(b.Call("abs", "v"), |
| b.Call<vec3<f32>>(b.MemberAccessor("params", "D"))))), |
| // let t = sign(v) * ((params.C * abs(v)) + params.F); |
| b.Decl(b.Let( |
| "t", b.Mul(b.Call("sign", "v"), |
| b.Add(b.Mul(b.MemberAccessor("params", "C"), b.Call("abs", "v")), |
| b.MemberAccessor("params", "F"))))), |
| // let f = (sign(v) * pow(((params.A * abs(v)) + params.B), |
| // vec3(params.G))) + params.E; |
| b.Decl(b.Let( |
| "f", b.Mul(b.Call("sign", "v"), |
| b.Add(b.Call("pow", |
| b.Add(b.Mul(b.MemberAccessor("params", "A"), |
| b.Call("abs", "v")), |
| b.MemberAccessor("params", "B")), |
| b.Call<vec3<f32>>(b.MemberAccessor("params", "G"))), |
| b.MemberAccessor("params", "E"))))), |
| // return select(f, t, cond); |
| b.Return(b.Call("select", "f", "t", "cond")), |
| }); |
| } |
| |
| /// Constructs a StatementList containing all the statements making up the body of the texture |
| /// builtin function. |
| /// @param call_type determines which function body to generate |
| /// @returns a statement list that makes of the body of the chosen function |
| auto buildTextureBuiltinBody(wgsl::BuiltinFn call_type) { |
| tint::Vector<const Statement*, 16> stmts; |
| const BlockStatement* single_plane_block = nullptr; |
| const BlockStatement* multi_plane_block = nullptr; |
| switch (call_type) { |
| case wgsl::BuiltinFn::kTextureSampleBaseClampToEdge: |
| stmts.Push(b.Decl( |
| b.Let("modifiedCoords", b.Mul(b.MemberAccessor("params", "sampleTransform"), |
| b.Call<vec3<f32>>("coord", 1_a))))); |
| |
| stmts.Push(b.Decl(b.Let( |
| "plane0_clamped", b.Call("clamp", "modifiedCoords", |
| b.MemberAccessor("params", "samplePlane0RectMin"), |
| b.MemberAccessor("params", "samplePlane0RectMax"))))); |
| |
| // var color: vec4<f32>; |
| stmts.Push(b.Decl(b.Var("color", b.ty.vec4(b.ty.f32())))); |
| |
| single_plane_block = b.Block( |
| b.Assign("color", b.MemberAccessor(b.Call("textureSampleLevel", "plane0", "smp", |
| "plane0_clamped", 0_a), |
| "rgba"))); |
| |
| multi_plane_block = b.Block( |
| b.Decl(b.Let("plane1_clamped", |
| b.Call("clamp", "modifiedCoords", |
| b.MemberAccessor("params", "samplePlane1RectMin"), |
| b.MemberAccessor("params", "samplePlane1RectMax")))), |
| |
| b.Assign("color", |
| b.Call<vec4<f32>>( |
| b.Mul(b.Call<vec4<f32>>( |
| b.MemberAccessor(b.Call("textureSampleLevel", "plane0", |
| "smp", "plane0_clamped", 0_a), |
| "r"), |
| b.MemberAccessor(b.Call("textureSampleLevel", "plane1", |
| "smp", "plane1_clamped", 0_a), |
| "rg"), |
| 1_a), |
| b.MemberAccessor("params", "yuvToRgbConversionMatrix")), |
| 1_a))); |
| break; |
| case wgsl::BuiltinFn::kTextureLoad: |
| stmts.Push(b.Decl( |
| b.Let("clampedCoords", b.Call("min", b.Call<vec2<u32>>("coord"), |
| b.MemberAccessor("params", "visibleSize"))))); |
| stmts.Push(b.Decl(b.Let( |
| "plane0_clamped", |
| b.Call<vec2<u32>>(b.Call( |
| "round", |
| b.Mul(b.MemberAccessor("params", "loadTransform"), |
| b.Call<vec3<f32>>(b.Call<vec2<f32>>("clampedCoords"), 1_a))))))); |
| |
| // var color: vec4<f32>; |
| stmts.Push(b.Decl(b.Var("color", b.ty.vec4(b.ty.f32())))); |
| |
| single_plane_block = b.Block(b.Assign( |
| "color", b.MemberAccessor( |
| b.Call("textureLoad", "plane0", "plane0_clamped", 0_a), "rgba"))); |
| |
| multi_plane_block = b.Block( |
| b.Decl(b.Let( |
| "plane1_clamped", |
| b.Call<vec2<u32>>(b.Mul(b.Call<vec2<f32>>("plane0_clamped"), |
| b.MemberAccessor("params", "plane1CoordFactor"))))), |
| |
| b.Assign("color", |
| b.Call<vec4<f32>>( |
| b.Mul(b.Call<vec4<f32>>( |
| b.MemberAccessor(b.Call("textureLoad", "plane0", |
| "plane0_clamped", 0_a), |
| "r"), |
| b.MemberAccessor(b.Call("textureLoad", "plane1", |
| "plane1_clamped", 0_a), |
| "rg"), |
| 1_a), |
| b.MemberAccessor("params", "yuvToRgbConversionMatrix")), |
| 1_a))); |
| break; |
| default: |
| TINT_ICE() << "unhandled builtin: " << call_type; |
| } |
| |
| // if ((params.numPlanes == 1u)) |
| stmts.Push(b.If(b.Equal(b.MemberAccessor("params", "numPlanes"), b.Expr(1_a)), |
| single_plane_block, b.Else(multi_plane_block))); |
| |
| // if (params.doYuvToRgbConversionOnly == 0u) |
| stmts.Push(b.If( |
| b.Equal(b.MemberAccessor("params", "doYuvToRgbConversionOnly"), b.Expr(0_a)), |
| b.Block( |
| // color = vec4<f32>(gammaConversion(color.rgb, gammaDecodeParams), color.a); |
| b.Assign("color", b.Call<vec4<f32>>( |
| b.Call("gammaCorrection", b.MemberAccessor("color", "rgb"), |
| b.MemberAccessor("params", "gammaDecodeParams")), |
| b.MemberAccessor("color", "a"))), |
| // color = vec4<f32>(params.gamutConversionMatrix * color.rgb), color.a); |
| b.Assign("color", b.Call<vec4<f32>>( |
| b.Mul(b.MemberAccessor("params", "gamutConversionMatrix"), |
| b.MemberAccessor("color", "rgb")), |
| b.MemberAccessor("color", "a"))), |
| // color = vec4<f32>(gammaConversion(color.rgb, gammaEncodeParams), color.a); |
| b.Assign("color", b.Call<vec4<f32>>( |
| b.Call("gammaCorrection", b.MemberAccessor("color", "rgb"), |
| b.MemberAccessor("params", "gammaEncodeParams")), |
| b.MemberAccessor("color", "a")))))); |
| |
| // return color; |
| stmts.Push(b.Return("color")); |
| |
| return stmts; |
| } |
| |
| /// Creates the textureSampleExternal function if needed and returns a call expression to it. |
| /// @param expr the call expression being transformed |
| /// @param syms the expanded symbols to be used in the new call |
| /// @returns a call expression to textureSampleExternal |
| const CallExpression* createTextureSampleBaseClampToEdge(const CallExpression* expr, |
| NewBindingSymbols syms) { |
| const Expression* plane_0_binding_param = ctx.Clone(expr->args[0]); |
| |
| if (TINT_UNLIKELY(expr->args.Length() != 3)) { |
| TINT_ICE() << "expected textureSampleBaseClampToEdge call with a " |
| "texture_external to have 3 parameters, found " |
| << expr->args.Length() << " parameters"; |
| } |
| |
| // TextureSampleExternal calls the gammaCorrection function, so ensure it |
| // exists. |
| if (!gamma_correction_sym.IsValid()) { |
| createGammaCorrectionFn(); |
| } |
| |
| if (!texture_sample_external_sym.IsValid()) { |
| texture_sample_external_sym = b.Symbols().New("textureSampleExternal"); |
| |
| // Emit the textureSampleExternal function. |
| b.Func(texture_sample_external_sym, |
| tint::Vector{ |
| b.Param("plane0", |
| b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())), |
| b.Param("plane1", |
| b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())), |
| b.Param("smp", b.ty.sampler(core::type::SamplerKind::kSampler)), |
| b.Param("coord", b.ty.vec2(b.ty.f32())), |
| b.Param("params", b.ty(params_struct_sym)), |
| }, |
| b.ty.vec4(b.ty.f32()), |
| buildTextureBuiltinBody(wgsl::BuiltinFn::kTextureSampleBaseClampToEdge)); |
| } |
| |
| return b.Call(texture_sample_external_sym, tint::Vector{ |
| plane_0_binding_param, |
| b.Expr(syms.plane_1), |
| ctx.Clone(expr->args[1]), |
| ctx.Clone(expr->args[2]), |
| b.Expr(syms.params), |
| }); |
| } |
| |
| /// Creates the textureLoadExternal function if needed and returns a call expression to it. |
| /// @param call the call expression being transformed |
| /// @param syms the expanded symbols to be used in the new call |
| /// @returns a call expression to textureLoadExternal |
| const CallExpression* createTextureLoad(const sem::Call* call, NewBindingSymbols syms) { |
| if (TINT_UNLIKELY(call->Arguments().Length() != 2)) { |
| TINT_ICE() |
| << "expected textureLoad call with a texture_external to have 2 arguments, found " |
| << call->Arguments().Length() << " arguments"; |
| } |
| |
| auto& args = call->Arguments(); |
| |
| // TextureLoadExternal calls the gammaCorrection function, so ensure it exists. |
| if (!gamma_correction_sym.IsValid()) { |
| createGammaCorrectionFn(); |
| } |
| |
| auto texture_load_external_sym = texture_load_external_fns.GetOrAdd(call->Target(), [&] { |
| auto& sig = call->Target()->Signature(); |
| auto* coord_ty = sig.Parameter(core::ParameterUsage::kCoords)->Type(); |
| |
| auto name = b.Symbols().New("textureLoadExternal"); |
| |
| // Emit the textureLoadExternal() function. |
| b.Func(name, |
| tint::Vector{ |
| b.Param("plane0", |
| b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())), |
| b.Param("plane1", |
| b.ty.sampled_texture(core::type::TextureDimension::k2d, b.ty.f32())), |
| b.Param("coord", CreateASTTypeFor(ctx, coord_ty)), |
| b.Param("params", b.ty(params_struct_sym)), |
| }, |
| b.ty.vec4(b.ty.f32()), // |
| buildTextureBuiltinBody(wgsl::BuiltinFn::kTextureLoad)); |
| |
| return name; |
| }); |
| |
| auto plane_0_binding_arg = ctx.Clone(args[0]->Declaration()); |
| |
| return b.Call(texture_load_external_sym, plane_0_binding_arg, syms.plane_1, |
| ctx.Clone(args[1]->Declaration()), syms.params); |
| } |
| |
| /// Returns the expression used to replace a textureDimensions call. |
| /// @param call the call expression being transformed |
| /// @param syms the expanded symbols to be used in the new call |
| /// @returns a load of params.visibleSize |
| const Expression* createTextureDimensions(const sem::Call* call, NewBindingSymbols syms) { |
| if (TINT_UNLIKELY(call->Arguments().Length() != 1)) { |
| TINT_ICE() << "expected textureDimensions call with a texture_external to have 1 " |
| "arguments, found " |
| << call->Arguments().Length() << " arguments"; |
| } |
| return b.Add(b.MemberAccessor(syms.params, "visibleSize"), b.Call<vec2<u32>>(1_a)); |
| } |
| }; |
| |
| MultiplanarExternalTexture::NewBindingPoints::NewBindingPoints() = default; |
| MultiplanarExternalTexture::NewBindingPoints::NewBindingPoints( |
| tint::transform::multiplanar::BindingsMap inputBindingsMap, |
| bool may_collide) |
| : bindings_map(std::move(inputBindingsMap)), allow_collisions(may_collide) {} |
| |
| 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 texture builtins 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 separate Y and UV planes. |
| Transform::ApplyResult MultiplanarExternalTexture::Apply(const Program& src, |
| const DataMap& inputs, |
| DataMap&) const { |
| auto* new_binding_points = inputs.Get<NewBindingPoints>(); |
| |
| if (!ShouldRun(src)) { |
| return SkipTransform; |
| } |
| |
| ProgramBuilder b; |
| program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true}; |
| if (!new_binding_points) { |
| b.Diagnostics().AddError(Source{}) |
| << "missing new binding point data for " << TypeInfo().name; |
| return resolver::Resolve(b); |
| } |
| |
| State state(ctx, new_binding_points); |
| |
| state.Process(); |
| |
| ctx.Clone(); |
| return resolver::Resolve(b); |
| } |
| |
| } // namespace tint::ast::transform |