mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-14 11:21:40 +00:00
Transforms are supposed to be immutable, operating on the DataMaps provided for input and output, so make the methods const. Add a ShouldRun() method which the Manager can use to skip over transforms that do not need to be run. Change-Id: I320ac964577e94ac988748d8aca85bd43ee8d3b5 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/77120 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Antonio Maiorano <amaiorano@google.com> Commit-Queue: Ben Clayton <bclayton@google.com>
323 lines
13 KiB
C++
323 lines
13 KiB
C++
// 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/transform/combine_samplers.h"
|
|
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "src/sem/function.h"
|
|
#include "src/sem/statement.h"
|
|
|
|
#include "src/utils/map.h"
|
|
|
|
TINT_INSTANTIATE_TYPEINFO(tint::transform::CombineSamplers);
|
|
TINT_INSTANTIATE_TYPEINFO(tint::transform::CombineSamplers::BindingInfo);
|
|
|
|
namespace {
|
|
|
|
bool IsGlobal(const tint::sem::VariablePair& pair) {
|
|
return pair.first->Is<tint::sem::GlobalVariable>() &&
|
|
(!pair.second || pair.second->Is<tint::sem::GlobalVariable>());
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace tint {
|
|
namespace transform {
|
|
|
|
CombineSamplers::BindingInfo::BindingInfo(const BindingMap& map)
|
|
: binding_map(map) {}
|
|
CombineSamplers::BindingInfo::BindingInfo(const BindingInfo& other) = default;
|
|
CombineSamplers::BindingInfo::~BindingInfo() = default;
|
|
|
|
/// The PIMPL state for the CombineSamplers transform
|
|
struct CombineSamplers::State {
|
|
/// The clone context
|
|
CloneContext& ctx;
|
|
|
|
/// The binding info
|
|
const BindingInfo* binding_info;
|
|
|
|
/// Map from a texture/sampler pair to the corresponding combined sampler
|
|
/// variable
|
|
using CombinedTextureSamplerMap =
|
|
std::unordered_map<sem::VariablePair, const ast::Variable*>;
|
|
|
|
/// Use sem::BindingPoint without scope.
|
|
using BindingPoint = sem::BindingPoint;
|
|
|
|
/// A map of all global texture/sampler variable pairs to the global
|
|
/// combined sampler variable that will replace it.
|
|
CombinedTextureSamplerMap global_combined_texture_samplers_;
|
|
|
|
/// A map of all texture/sampler variable pairs that contain a function
|
|
/// parameter to the combined sampler function paramter that will replace it.
|
|
std::unordered_map<const sem::Function*, CombinedTextureSamplerMap>
|
|
function_combined_texture_samplers_;
|
|
|
|
/// Placeholder global samplers used when a function contains texture-only
|
|
/// references (one comparison sampler, one regular). These are also used as
|
|
/// temporary sampler parameters to the texture intrinsics to satsify the WGSL
|
|
/// resolver, but are then ignored and removed by the GLSL writer.
|
|
const ast::Variable* placeholder_samplers_[2] = {};
|
|
|
|
/// Group and binding decorations used by all combined sampler globals.
|
|
/// Group 0 and binding 0 are used, with collisions disabled.
|
|
/// @returns the newly-created decoration list
|
|
ast::DecorationList Decorations() const {
|
|
auto decorations = ctx.dst->GroupAndBinding(0, 0);
|
|
decorations.push_back(
|
|
ctx.dst->Disable(ast::DisabledValidation::kBindingPointCollision));
|
|
return decorations;
|
|
}
|
|
|
|
/// Constructor
|
|
/// @param context the clone context
|
|
/// @param info the binding map information
|
|
State(CloneContext& context, const BindingInfo* info)
|
|
: ctx(context), binding_info(info) {}
|
|
|
|
/// Creates a combined sampler global variables.
|
|
/// (Note this is actually a Texture node at the AST level, but it will be
|
|
/// written as the corresponding sampler (eg., sampler2D) on GLSL output.)
|
|
/// @param texture_var the texture (global) variable
|
|
/// @param sampler_var the sampler (global) variable
|
|
/// @param name the default name to use (may be overridden by map lookup)
|
|
/// @returns the newly-created global variable
|
|
const ast::Variable* CreateCombinedGlobal(const sem::Variable* texture_var,
|
|
const sem::Variable* sampler_var,
|
|
std::string name) {
|
|
SamplerTexturePair bp_pair;
|
|
bp_pair.texture_binding_point =
|
|
texture_var->As<sem::GlobalVariable>()->BindingPoint();
|
|
bp_pair.sampler_binding_point =
|
|
sampler_var ? sampler_var->As<sem::GlobalVariable>()->BindingPoint()
|
|
: BindingPoint();
|
|
auto it = binding_info->binding_map.find(bp_pair);
|
|
if (it != binding_info->binding_map.end()) {
|
|
name = it->second;
|
|
}
|
|
const ast::Type* type = CreateASTTypeFor(ctx, texture_var->Type());
|
|
Symbol symbol = ctx.dst->Symbols().New(name);
|
|
return ctx.dst->Global(symbol, type, Decorations());
|
|
}
|
|
|
|
/// Creates placeholder global sampler variables.
|
|
/// @param kind the sampler kind to create for
|
|
/// @returns the newly-created global variable
|
|
const ast::Variable* CreatePlaceholder(ast::SamplerKind kind) {
|
|
const ast::Type* type = ctx.dst->ty.sampler(kind);
|
|
const char* name = kind == ast::SamplerKind::kComparisonSampler
|
|
? "placeholder_comparison_sampler"
|
|
: "placeholder_sampler";
|
|
Symbol symbol = ctx.dst->Symbols().New(name);
|
|
return ctx.dst->Global(symbol, type, Decorations());
|
|
}
|
|
|
|
/// Performs the transformation
|
|
void Run() {
|
|
auto& sem = ctx.src->Sem();
|
|
|
|
// Remove all texture and sampler global variables. These will be replaced
|
|
// by combined samplers.
|
|
for (auto* var : ctx.src->AST().GlobalVariables()) {
|
|
auto* type = sem.Get(var->type);
|
|
if (type && type->IsAnyOf<sem::Texture, sem::Sampler>()) {
|
|
ctx.Remove(ctx.src->AST().GlobalDeclarations(), var);
|
|
} else if (auto binding_point = var->BindingPoint()) {
|
|
if (binding_point.group->value == 0 &&
|
|
binding_point.binding->value == 0) {
|
|
auto* decoration =
|
|
ctx.dst->Disable(ast::DisabledValidation::kBindingPointCollision);
|
|
ctx.InsertFront(var->decorations, decoration);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Rewrite all function signatures to use combined samplers, and remove
|
|
// separate textures & samplers. Create new combined globals where found.
|
|
ctx.ReplaceAll([&](const ast::Function* src) -> const ast::Function* {
|
|
if (auto* func = sem.Get(src)) {
|
|
auto pairs = func->TextureSamplerPairs();
|
|
if (pairs.empty()) {
|
|
return nullptr;
|
|
}
|
|
ast::VariableList params;
|
|
for (auto pair : func->TextureSamplerPairs()) {
|
|
const sem::Variable* texture_var = pair.first;
|
|
const sem::Variable* sampler_var = pair.second;
|
|
std::string name =
|
|
ctx.src->Symbols().NameFor(texture_var->Declaration()->symbol);
|
|
if (sampler_var) {
|
|
name += "_" + ctx.src->Symbols().NameFor(
|
|
sampler_var->Declaration()->symbol);
|
|
}
|
|
if (IsGlobal(pair)) {
|
|
// Both texture and sampler are global; add a new global variable
|
|
// to represent the combined sampler (if not already created).
|
|
utils::GetOrCreate(global_combined_texture_samplers_, pair, [&] {
|
|
return CreateCombinedGlobal(texture_var, sampler_var, name);
|
|
});
|
|
} else {
|
|
// Either texture or sampler (or both) is a function parameter;
|
|
// add a new function parameter to represent the combined sampler.
|
|
const ast::Type* type = CreateASTTypeFor(ctx, texture_var->Type());
|
|
const ast::Variable* var =
|
|
ctx.dst->Param(ctx.dst->Symbols().New(name), type);
|
|
params.push_back(var);
|
|
function_combined_texture_samplers_[func][pair] = var;
|
|
}
|
|
}
|
|
// Filter out separate textures and samplers from the original
|
|
// function signature.
|
|
for (auto* var : src->params) {
|
|
if (!sem.Get(var->type)->IsAnyOf<sem::Texture, sem::Sampler>()) {
|
|
params.push_back(ctx.Clone(var));
|
|
}
|
|
}
|
|
// Create a new function signature that differs only in the parameter
|
|
// list.
|
|
auto symbol = ctx.Clone(src->symbol);
|
|
auto* return_type = ctx.Clone(src->return_type);
|
|
auto* body = ctx.Clone(src->body);
|
|
auto decorations = ctx.Clone(src->decorations);
|
|
auto return_type_decorations = ctx.Clone(src->return_type_decorations);
|
|
return ctx.dst->create<ast::Function>(
|
|
symbol, params, return_type, body, std::move(decorations),
|
|
std::move(return_type_decorations));
|
|
}
|
|
return nullptr;
|
|
});
|
|
|
|
// Replace all function call expressions containing texture or
|
|
// sampler parameters to use the current function's combined samplers or
|
|
// the combined global samplers, as appropriate.
|
|
ctx.ReplaceAll([&](const ast::CallExpression* expr)
|
|
-> const ast::Expression* {
|
|
if (auto* call = sem.Get(expr)) {
|
|
ast::ExpressionList args;
|
|
// Replace all texture intrinsic calls.
|
|
if (auto* intrinsic = call->Target()->As<sem::Intrinsic>()) {
|
|
const auto& signature = intrinsic->Signature();
|
|
int sampler_index = signature.IndexOf(sem::ParameterUsage::kSampler);
|
|
int texture_index = signature.IndexOf(sem::ParameterUsage::kTexture);
|
|
if (texture_index == -1) {
|
|
return nullptr;
|
|
}
|
|
const sem::Expression* texture = call->Arguments()[texture_index];
|
|
const sem::Expression* sampler =
|
|
sampler_index != -1 ? call->Arguments()[sampler_index] : nullptr;
|
|
auto* texture_var = texture->As<sem::VariableUser>()->Variable();
|
|
auto* sampler_var =
|
|
sampler ? sampler->As<sem::VariableUser>()->Variable() : nullptr;
|
|
sem::VariablePair new_pair(texture_var, sampler_var);
|
|
for (auto* arg : expr->args) {
|
|
auto* type = ctx.src->TypeOf(arg)->UnwrapRef();
|
|
if (type->Is<sem::Texture>()) {
|
|
const ast::Variable* var =
|
|
IsGlobal(new_pair)
|
|
? global_combined_texture_samplers_[new_pair]
|
|
: function_combined_texture_samplers_
|
|
[call->Stmt()->Function()][new_pair];
|
|
args.push_back(ctx.dst->Expr(var->symbol));
|
|
} else if (auto* sampler_type = type->As<sem::Sampler>()) {
|
|
ast::SamplerKind kind = sampler_type->kind();
|
|
int index = (kind == ast::SamplerKind::kSampler) ? 0 : 1;
|
|
const ast::Variable*& p = placeholder_samplers_[index];
|
|
if (!p) {
|
|
p = CreatePlaceholder(kind);
|
|
}
|
|
args.push_back(ctx.dst->Expr(p->symbol));
|
|
} else {
|
|
args.push_back(ctx.Clone(arg));
|
|
}
|
|
}
|
|
return ctx.dst->Call(ctx.Clone(expr->target.name), args);
|
|
}
|
|
// Replace all function calls.
|
|
if (auto* callee = call->Target()->As<sem::Function>()) {
|
|
for (auto pair : callee->TextureSamplerPairs()) {
|
|
// Global pairs used by the callee do not require a function
|
|
// parameter at the call site.
|
|
if (IsGlobal(pair)) {
|
|
continue;
|
|
}
|
|
const sem::Variable* texture_var = pair.first;
|
|
const sem::Variable* sampler_var = pair.second;
|
|
if (auto* param = texture_var->As<sem::Parameter>()) {
|
|
const sem::Expression* texture =
|
|
call->Arguments()[param->Index()];
|
|
texture_var = texture->As<sem::VariableUser>()->Variable();
|
|
}
|
|
if (sampler_var) {
|
|
if (auto* param = sampler_var->As<sem::Parameter>()) {
|
|
const sem::Expression* sampler =
|
|
call->Arguments()[param->Index()];
|
|
sampler_var = sampler->As<sem::VariableUser>()->Variable();
|
|
}
|
|
}
|
|
sem::VariablePair new_pair(texture_var, sampler_var);
|
|
// If both texture and sampler are (now) global, pass that
|
|
// global variable to the callee. Otherwise use the caller's
|
|
// function parameter for this pair.
|
|
const ast::Variable* var =
|
|
IsGlobal(new_pair) ? global_combined_texture_samplers_[new_pair]
|
|
: function_combined_texture_samplers_
|
|
[call->Stmt()->Function()][new_pair];
|
|
auto* arg = ctx.dst->Expr(var->symbol);
|
|
args.push_back(arg);
|
|
}
|
|
// Append all of the remaining non-texture and non-sampler
|
|
// parameters.
|
|
for (auto* arg : expr->args) {
|
|
if (!ctx.src->TypeOf(arg)
|
|
->UnwrapRef()
|
|
->IsAnyOf<sem::Texture, sem::Sampler>()) {
|
|
args.push_back(ctx.Clone(arg));
|
|
}
|
|
}
|
|
return ctx.dst->Call(ctx.Clone(expr->target.name), args);
|
|
}
|
|
}
|
|
return nullptr;
|
|
});
|
|
|
|
ctx.Clone();
|
|
}
|
|
};
|
|
|
|
CombineSamplers::CombineSamplers() = default;
|
|
|
|
CombineSamplers::~CombineSamplers() = default;
|
|
|
|
void CombineSamplers::Run(CloneContext& ctx,
|
|
const DataMap& inputs,
|
|
DataMap&) const {
|
|
auto* binding_info = inputs.Get<BindingInfo>();
|
|
if (!binding_info) {
|
|
ctx.dst->Diagnostics().add_error(
|
|
diag::System::Transform,
|
|
"missing transform data for " + std::string(TypeInfo().name));
|
|
return;
|
|
}
|
|
|
|
State(ctx, binding_info).Run();
|
|
}
|
|
|
|
} // namespace transform
|
|
} // namespace tint
|