[tint][glsl] Directly create all replacements in texture_polyfill
glsl.TexturePolyfill does several things one of which is introducing
GLSL's CombinedTextureSamplerVar for all combinations of texture+sampler
pairs statically used together in the shader (as well as for sampled
textures used without a sampler).
The logic used to create the CombinedTextureSamplerVar for
texture+sampler pairs as a pre-processing step but then would create the
ones for texture usages without samplers on demand, so that it could
reuse one of the texture+sampler replacements as an optimization.
Instead do the computation of the replacements for texture usages
without samplers in a second step of the preprocess, simplifying when
the various pieces of logic in that transform run. This will make it
easier to introduce support for binding_array in the transform in a
follow-up CL.
Bug: 411573957
Change-Id: I5074d8407e3bfd2649ebe963c18650d660843051
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/247395
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/glsl/writer/raise/texture_polyfill.cc b/src/tint/lang/glsl/writer/raise/texture_polyfill.cc
index 7adbbaa..38331cb 100644
--- a/src/tint/lang/glsl/writer/raise/texture_polyfill.cc
+++ b/src/tint/lang/glsl/writer/raise/texture_polyfill.cc
@@ -62,15 +62,14 @@
/// The type manager.
core::type::Manager& ty{ir.Types()};
- // A map of single texture to the replacement var. Note, this doesn't just re-use the
- // `texture_sampler_to_replacement` with the placeholder sampler because we can share an
- // individual texture with a texture,sampler pair. So, if we create the texture as `t1,s1` we
- // want to use that `t1` individually but if we look it up with `t1,sp` then we won't find it.
- // This secondary map exists to allow us access to textures which may have been created
- // with a sampler.
- Hashmap<core::ir::Var*, std::optional<core::ir::Var*>, 2> texture_to_replacement_{};
+ // A map of single texture to the replacement var for when the texture is used without a
+ // sampler. Textures not used with a sampler may not have any entry in
+ // `texture_sampler_to_replacement`. However we try as best we can to reuse a replacement from
+ // the texture+sampler replacement to minimize the number of new binding points created. (this
+ // is why we don't just add texture+placeholder_sampler in `texture_sampler_to_replacement`).
+ Hashmap<core::ir::Var*, core::ir::Var*, 2> texture_to_replacement_{};
- // A map of the <texture,sampler> binding pair to the replacement var.
+ // A map of the <texture,sampler> binding pair to the replacement CombinedTextureSamplerVar.
Hashmap<binding::CombinedTextureSamplerPair, core::ir::Var*, 2>
texture_sampler_to_replacement_{};
@@ -86,7 +85,7 @@
UpgradeTexture1DVars();
UpgradeTexture1DParams();
- PopulateTextureInformation();
+ PopulateTextureReplacements();
Vector<core::ir::CoreBuiltinCall*, 4> call_worklist;
for (auto* inst : ir.Instructions()) {
@@ -167,51 +166,11 @@
}
}
- core::ir::Var* GetReplacement(core::ir::Var* tex,
- core::ir::Var* sampler,
- const core::type::Pointer* tex_ty) {
- // Don't change storage textures
- if (tex->Result()->Type()->UnwrapPtr()->Is<core::type::StorageTexture>()) {
- return tex;
- }
-
- if (!sampler) {
- auto existing_var = texture_to_replacement_.Get(tex);
- if (existing_var) {
- return existing_var->value();
- }
-
- replaced_textures_and_samplers_.Add(tex);
-
- // If the texture wasn't already in the map this means it was an individual texture we
- // hadn't seen yet. Create it and insert into the map for future use.
- binding::CombinedTextureSamplerPair key{tex->BindingPoint().value(),
- cfg.placeholder_sampler_bind_point};
- auto* replacement = MakeVar(key, tex, nullptr, tex_ty);
- texture_to_replacement_.Add(tex, replacement);
- return replacement;
- }
-
- auto tex_bp = tex->BindingPoint();
- auto samp_bp = sampler->BindingPoint();
- TINT_ASSERT(tex_bp.has_value() && samp_bp.has_value());
-
- replaced_textures_and_samplers_.Add(tex);
- replaced_textures_and_samplers_.Add(sampler);
-
- binding::CombinedTextureSamplerPair key{tex_bp.value(), samp_bp.value()};
- auto var = texture_sampler_to_replacement_.Get(key);
- TINT_ASSERT(var);
- return *(var.value);
- }
-
- // Get the `var` for a texture/sampler value. This means the value must be the result of a load.
- core::ir::Var* VarForValue(core::ir::Value* val) {
- if (!val) {
- return nullptr;
- }
-
- auto* load = LoadForValue(val);
+ // Get the module root var a texture/sampler value comes from.
+ core::ir::Var* VarForValue(core::ir::Value* val) const {
+ auto* res = val->As<core::ir::InstructionResult>();
+ TINT_ASSERT(res);
+ auto* load = res->Instruction()->As<core::ir::Load>();
TINT_ASSERT(load);
auto* from = load->From()->As<core::ir::InstructionResult>();
TINT_ASSERT(from);
@@ -220,23 +179,13 @@
return var;
}
- core::ir::Load* LoadForValue(core::ir::Value* val) {
- if (!val) {
- return nullptr;
- }
-
- auto* res = val->As<core::ir::InstructionResult>();
- TINT_ASSERT(res);
- auto* load = res->Instruction()->As<core::ir::Load>();
- TINT_ASSERT(load);
- return load;
- }
-
+ // Returns the module root var that the texture and sampler come from (or nullptr for the
+ // sampler if it isn't an argument).
struct SamplerTextureVars {
core::ir::Var* texture;
core::ir::Var* sampler;
};
- SamplerTextureVars GetTextureSamplerFor(core::ir::CoreBuiltinCall* call) {
+ SamplerTextureVars GetTextureSamplerFor(core::ir::CoreBuiltinCall* call) const {
auto args = call->Args();
switch (call->Func()) {
case core::BuiltinFn::kTextureDimensions:
@@ -264,16 +213,24 @@
}
}
- core::ir::Var* MakeVar(binding::CombinedTextureSamplerPair& key,
- core::ir::Var* tex,
- core::ir::Var* sampler,
- const core::type::Pointer* tex_ty) {
+ // Creates the new root module var that will be used as the replacement for a texture+(sampler?)
+ // pair.
+ glsl::ir::CombinedTextureSamplerVar* MakeReplacement(binding::CombinedTextureSamplerPair& key,
+ core::ir::Var* tex,
+ core::ir::Var* sampler,
+ const core::type::Pointer* tex_ty) {
// Create a combined texture sampler variable and insert it into the root block.
auto* result = b.InstructionResult(tex_ty);
auto* var = ir.CreateInstruction<glsl::ir::CombinedTextureSamplerVar>(result, key.texture,
key.sampler);
ir.root_block->Append(var);
+ // Remember that we will need to remove the replaced variables.
+ replaced_textures_and_samplers_.Add(tex);
+ if (sampler) {
+ replaced_textures_and_samplers_.Add(sampler);
+ }
+
// Set the variable name based on the original texture and sampler names if provided.
StringStream name;
if (auto texture_name = ir.NameOf(tex)) {
@@ -297,8 +254,11 @@
// This function builds up replacement combined textures. It creates a global mapping, one for
// all the texture,sampler pairs and one with individual textures. The individual textures will
// attempt to populate with the first texture from a texture,sampler pair but if we didn't see
- // the texture in any pair we'll create it on the fly when getting the replacemnt later.
- void PopulateTextureInformation() {
+ // the texture in any pair we'll create a new one.
+ void PopulateTextureReplacements() {
+ // The textures used on their own that we'll make sure have a replacement in the later
+ // loop.
+ Hashset<core::ir::Var*, 4> textures_used_without_samplers{};
for (auto* inst : ir.Instructions()) {
auto* call = inst->As<core::ir::CoreBuiltinCall>();
if (!call || (!core::IsTexture(call->Func()) && !core::IsImageQuery(call->Func()))) {
@@ -309,25 +269,61 @@
auto* tex = tex_sampler.texture;
auto* sampler = tex_sampler.sampler;
- // No sampler, then we aren't going to be creating a combined sampler.
+ // No sampler, remember that we need to find at least one replacement for the texture.
if (!sampler) {
+ textures_used_without_samplers.Add(tex);
continue;
}
+ // Make a replacement for the texture+sampler.
BindingPoint tex_bp = tex->BindingPoint().value();
BindingPoint samp_bp = sampler->BindingPoint().value();
binding::CombinedTextureSamplerPair key{tex_bp, samp_bp};
auto* replacement = texture_sampler_to_replacement_.GetOrAdd(key, [&] {
- return MakeVar(key, tex, sampler, tex->Result()->Type()->As<core::type::Pointer>());
+ return MakeReplacement(key, tex, sampler,
+ tex->Result()->Type()->As<core::type::Pointer>());
});
+ // Mark the texture+sampler replacement as a replacement for the texture on its own.
// Don't add depth textures here because the unsampled depth texture will need to be
// created as a sampled texture, instead of a depth texture.
if (!tex->Result()->Type()->UnwrapPtr()->Is<core::type::DepthTexture>()) {
texture_to_replacement_.Add(tex, replacement);
}
}
+
+ // Create replacements for all textures used without samplers and that can't reuse an
+ // existing one.
+ auto sorted_textures_used_without_samplers = textures_used_without_samplers.Vector();
+ sorted_textures_used_without_samplers.Sort(
+ [](const core::ir::Var* v1, const core::ir::Var* v2) {
+ return v1->BindingPoint().value() < v2->BindingPoint().value();
+ });
+
+ for (auto tex : sorted_textures_used_without_samplers) {
+ if (texture_to_replacement_.Contains(tex)) {
+ continue;
+ }
+
+ auto* tex_ty = tex->Result()->Type()->UnwrapPtr()->As<core::type::Texture>();
+
+ // Storage textures don't need replacements so they are replaced with themselves.
+ if (tex_ty->Is<core::type::StorageTexture>()) {
+ texture_to_replacement_.Add(tex, tex);
+ continue;
+ }
+
+ // Depth textures used without a sampler need to be replaced with f32 sampled textures.
+ auto* replacement_ty = ty.ptr<handle>(tex_ty);
+ if (tex_ty->Is<core::type::DepthTexture>()) {
+ replacement_ty = ty.ptr<handle>(ty.sampled_texture(tex_ty->Dim(), ty.f32()));
+ }
+
+ binding::CombinedTextureSamplerPair key{tex->BindingPoint().value(),
+ cfg.placeholder_sampler_bind_point};
+ texture_to_replacement_.Add(tex, MakeReplacement(key, tex, nullptr, replacement_ty));
+ }
}
std::optional<const core::type::Type*> UpgradeTexture1D(core::ir::Value* value) {
@@ -444,28 +440,23 @@
// Must be called inside an insertion block
core::ir::Value* GetNewTexture(core::ir::Value* tex, core::ir::Value* sampler = nullptr) {
auto* t = VarForValue(tex);
- auto* s = VarForValue(sampler);
- auto* tex_ty = t->Result()->Type()->As<core::type::Pointer>();
- TINT_ASSERT(tex_ty);
+ core::ir::Var* replacement;
+ if (sampler) {
+ auto* s = VarForValue(sampler);
- // A depth texture gets turned into a SampledTexture of type `f32` when there is no
- // sampler.
- if (!sampler) {
- if (tex_ty->StoreType()->Is<core::type::DepthTexture>()) {
- tex_ty =
- ty.ptr(tex_ty->AddressSpace(),
- ty.sampled_texture(tex_ty->UnwrapPtr()->As<core::type::Texture>()->Dim(),
- ty.f32()),
- tex_ty->Access());
- }
+ auto t_bp = t->BindingPoint();
+ auto s_bp = s->BindingPoint();
+ TINT_ASSERT(t_bp.has_value() && s_bp.has_value());
+ binding::CombinedTextureSamplerPair key{t_bp.value(), s_bp.value()};
+
+ replacement = *texture_sampler_to_replacement_.Get(key).value;
+ } else {
+ replacement = *texture_to_replacement_.Get(t).value;
}
-
- auto* replacement = GetReplacement(t, s, tex_ty);
TINT_ASSERT(replacement);
- // In the storage case, we'll return the original texture. Nothing else to do in that
- // case.
+ // In the storage case, we'll return the original texture. Nothing else to do in that case.
if (replacement == t) {
return tex;
}