Remove data returned from `TextureBuiltinFromUniform` transform.

This CL updates the tint/dawn API to remove the data returned from the
`TextureBuiltinFromUniform` transform. A new API is added to the
inspector which Dawn can use to query the same information. Dawn then
provides tint with a list of binding points in the order to place them
in the generated UBO.

Change-Id: I7049ff07f0d55766206f73f7c07ad2df383f5c2d
Bug: dawn:1299
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/168900
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
Reviewed-by: Shrek Shao <shrekshao@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/dawn/native/opengl/BindingPoint.h b/src/dawn/native/opengl/BindingPoint.h
new file mode 100644
index 0000000..86bf647
--- /dev/null
+++ b/src/dawn/native/opengl/BindingPoint.h
@@ -0,0 +1,53 @@
+// Copyright 2024 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.
+
+#ifndef SRC_DAWN_NATIVE_OPENGL_BINDPOINT_H_
+#define SRC_DAWN_NATIVE_OPENGL_BINDPOINT_H_
+
+#include <unordered_map>
+#include <utility>
+
+#include "src/tint/api/common/binding_point.h"
+
+namespace dawn::native::opengl {
+
+/// Indicate the type of field for each entry to push.
+enum class BindPointFunction : uint8_t {
+    /// The number of MIP levels of the bound texture view.
+    kTextureNumLevels,
+    /// The number of samples per texel of the bound multi-sampled texture.
+    kTextureNumSamples,
+};
+
+/// Records the field and the byte offset of the data to push in the internal uniform buffer.
+using FunctionAndOffset = std::pair<BindPointFunction, uint32_t>;
+/// Maps from binding point to data entry with the information to populate the data.
+using BindingPointToFunctionAndOffset = std::unordered_map<tint::BindingPoint, FunctionAndOffset>;
+
+}  // namespace dawn::native::opengl
+
+#endif  // SRC_DAWN_NATIVE_OPENGL_BINDPOINT_H_
diff --git a/src/dawn/native/opengl/CommandBufferGL.cpp b/src/dawn/native/opengl/CommandBufferGL.cpp
index 9607141..745e5c2 100644
--- a/src/dawn/native/opengl/CommandBufferGL.cpp
+++ b/src/dawn/native/opengl/CommandBufferGL.cpp
@@ -38,6 +38,7 @@
 #include "dawn/native/Commands.h"
 #include "dawn/native/ExternalTexture.h"
 #include "dawn/native/RenderBundle.h"
+#include "dawn/native/opengl/BindingPoint.h"
 #include "dawn/native/opengl/BufferGL.h"
 #include "dawn/native/opengl/ComputePipelineGL.h"
 #include "dawn/native/opengl/DeviceGL.h"
@@ -430,15 +431,15 @@
         }
 
         // Update data by retrieving information from texture view object.
-        const tint::TextureBuiltinsFromUniformOptions::Field field = iter->second.first;
+        const BindPointFunction field = iter->second.first;
         const size_t byteOffset = static_cast<size_t>(iter->second.second);
 
         uint32_t data;
         switch (field) {
-            case tint::TextureBuiltinsFromUniformOptions::Field::TextureNumLevels:
+            case BindPointFunction::kTextureNumLevels:
                 data = view->GetLevelCount();
                 break;
-            case tint::TextureBuiltinsFromUniformOptions::Field::TextureNumSamples:
+            case BindPointFunction::kTextureNumSamples:
                 data = view->GetTexture()->GetSampleCount();
                 break;
         }
diff --git a/src/dawn/native/opengl/PipelineGL.cpp b/src/dawn/native/opengl/PipelineGL.cpp
index e63e6e4..f59c41e 100644
--- a/src/dawn/native/opengl/PipelineGL.cpp
+++ b/src/dawn/native/opengl/PipelineGL.cpp
@@ -212,8 +212,7 @@
     return mTextureBuiltinsBuffer.Get();
 }
 
-const tint::TextureBuiltinsFromUniformOptions::BindingPointToFieldAndOffset&
-PipelineGL::GetBindingPointBuiltinDataInfo() const {
+const BindingPointToFunctionAndOffset& PipelineGL::GetBindingPointBuiltinDataInfo() const {
     return mBindingPointEmulatedBuiltins;
 }
 
diff --git a/src/dawn/native/opengl/PipelineGL.h b/src/dawn/native/opengl/PipelineGL.h
index a2e1a42..b2a6591 100644
--- a/src/dawn/native/opengl/PipelineGL.h
+++ b/src/dawn/native/opengl/PipelineGL.h
@@ -36,6 +36,7 @@
 #include "include/tint/tint.h"
 
 #include "dawn/native/PerStage.h"
+#include "dawn/native/opengl/BindingPoint.h"
 #include "dawn/native/opengl/opengl_platform.h"
 
 namespace dawn::native {
@@ -66,8 +67,7 @@
     GLuint GetProgramHandle() const;
 
     const Buffer* GetInternalUniformBuffer() const;
-    const tint::TextureBuiltinsFromUniformOptions::BindingPointToFieldAndOffset&
-    GetBindingPointBuiltinDataInfo() const;
+    const BindingPointToFunctionAndOffset& GetBindingPointBuiltinDataInfo() const;
 
   protected:
     void ApplyNow(const OpenGLFunctions& gl);
@@ -92,8 +92,7 @@
 
     // Reflect info from tint: a map from texture binding point to extra data need to push into the
     // internal uniform buffer.
-    tint::TextureBuiltinsFromUniformOptions::BindingPointToFieldAndOffset
-        mBindingPointEmulatedBuiltins;
+    BindingPointToFunctionAndOffset mBindingPointEmulatedBuiltins;
 };
 
 }  // namespace dawn::native::opengl
diff --git a/src/dawn/native/opengl/ShaderModuleGL.cpp b/src/dawn/native/opengl/ShaderModuleGL.cpp
index f0cbfb8..0ce3792 100644
--- a/src/dawn/native/opengl/ShaderModuleGL.cpp
+++ b/src/dawn/native/opengl/ShaderModuleGL.cpp
@@ -35,12 +35,10 @@
 #include "dawn/native/CacheRequest.h"
 #include "dawn/native/Pipeline.h"
 #include "dawn/native/TintUtils.h"
+#include "dawn/native/opengl/BindingPoint.h"
 #include "dawn/native/opengl/DeviceGL.h"
 #include "dawn/native/opengl/PipelineLayoutGL.h"
-#include "dawn/native/stream/BlobSource.h"
-#include "dawn/native/stream/ByteVectorSink.h"
 #include "dawn/platform/DawnPlatform.h"
-#include "dawn/platform/metrics/HistogramMacros.h"
 #include "dawn/platform/tracing/TraceEvent.h"
 
 #include "tint/tint.h"
@@ -102,10 +100,7 @@
 DAWN_MAKE_CACHE_REQUEST(GLSLCompilationRequest, GLSL_COMPILATION_REQUEST_MEMBERS);
 #undef GLSL_COMPILATION_REQUEST_MEMBERS
 
-#define GLSL_COMPILATION_MEMBERS(X)     \
-    X(std::string, glsl)                \
-    X(bool, needsInternalUniformBuffer) \
-    X(tint::TextureBuiltinsFromUniformOptions::BindingPointToFieldAndOffset, bindingPointToData)
+#define GLSL_COMPILATION_MEMBERS(X) X(std::string, glsl)
 
 DAWN_SERIALIZABLE(struct, GLSLCompilation, GLSL_COMPILATION_MEMBERS){};
 #undef GLSL_COMPILATION_MEMBERS
@@ -176,14 +171,16 @@
     const PipelineLayout* layout,
     bool* needsPlaceholderSampler,
     bool* needsTextureBuiltinUniformBuffer,
-    tint::TextureBuiltinsFromUniformOptions::BindingPointToFieldAndOffset* bindingPointToData)
-    const {
+    BindingPointToFunctionAndOffset* bindingPointToData) const {
     TRACE_EVENT0(GetDevice()->GetPlatform(), General, "TranslateToGLSL");
 
     const OpenGLVersion& version = ToBackend(GetDevice())->GetGL().GetVersion();
 
     GLSLCompilationRequest req = {};
 
+    auto tintProgram = GetTintProgram();
+    req.inputProgram = &(tintProgram->program);
+
     using tint::BindingPoint;
     // Since (non-Vulkan) GLSL does not support descriptor sets, generate a
     // mapping from the original group/binding pair to a binding-only
@@ -227,10 +224,48 @@
         }
     }
 
+    tint::inspector::Inspector inspector(*req.inputProgram);
+
     // Some texture builtin functions are unsupported on GLSL ES. These are emulated with internal
     // uniforms.
     tint::TextureBuiltinsFromUniformOptions textureBuiltinsFromUniform;
     textureBuiltinsFromUniform.ubo_binding = {kMaxBindGroups + 1, 0};
+
+    auto textureBuiltinsFromUniformData = inspector.GetTextureQueries(programmableStage.entryPoint);
+    bool needsInternalUBO = false;
+    if (!textureBuiltinsFromUniformData.empty()) {
+        needsInternalUBO = true;
+        for (size_t i = 0; i < textureBuiltinsFromUniformData.size(); ++i) {
+            const auto& info = textureBuiltinsFromUniformData[i];
+
+            // This is the unmodified binding point from the WGSL shader.
+            BindingPoint srcBindingPoint{info.group, info.binding};
+            textureBuiltinsFromUniform.ubo_bindingpoint_ordering.emplace_back(srcBindingPoint);
+
+            // The remapped binding point is inserted into the Dawn data structure.
+            const BindGroupLayoutInternalBase* bgl =
+                layout->GetBindGroupLayout(BindGroupIndex{info.group});
+            BindingPoint dstBindingPoint = BindingPoint{
+                info.group,
+                static_cast<uint32_t>(bgl->GetBindingIndex(BindingNumber{info.binding}))};
+
+            BindPointFunction type = BindPointFunction::kTextureNumLevels;
+            switch (info.type) {
+                case tint::inspector::Inspector::TextureQueryType::kTextureNumLevels:
+                    type = BindPointFunction::kTextureNumLevels;
+                    break;
+                case tint::inspector::Inspector::TextureQueryType::kTextureNumSamples:
+                    type = BindPointFunction::kTextureNumSamples;
+                    break;
+            }
+
+            // Note, the `sizeof(uint32_t)` has to match up with the data type created by the
+            // `TextureBuiltinsFromUniform` when it creates the UBO structure.
+            bindingPointToData->emplace(
+                dstBindingPoint, std::pair{type, static_cast<uint32_t>(i * sizeof(uint32_t))});
+        }
+    }
+
     // Remap the internal ubo binding as well.
     glBindings.emplace(textureBuiltinsFromUniform.ubo_binding,
                        BindingPoint{0, layout->GetInternalUniformBinding()});
@@ -242,8 +277,6 @@
 
     const CombinedLimits& limits = GetDevice()->GetLimits();
 
-    auto tintProgram = GetTintProgram();
-    req.inputProgram = &(tintProgram->program);
     req.stage = stage;
     req.entryPointName = programmableStage.entryPoint;
     req.substituteOverrideConfig = std::move(substituteOverrideConfig);
@@ -275,7 +308,6 @@
     BindingPoint placeholderBindingPoint{static_cast<uint32_t>(kMaxBindGroupsTyped), 0};
 
     *needsPlaceholderSampler = false;
-    tint::inspector::Inspector inspector(*req.inputProgram);
     // Find all the sampler/texture pairs for this entry point, and create
     // CombinedSamplers for them. CombinedSampler records the binding points
     // of the original texture and sampler, and generates a unique name. The
@@ -381,17 +413,16 @@
             if (r.stage == SingleShaderStage::Compute) {
                 // Validate workgroup size after program runs transforms.
                 Extent3D _;
-                DAWN_TRY_ASSIGN(
-                    _, ValidateComputeStageWorkgroupSize(program, remappedEntryPoint.c_str(),
-                                                         r.limits, /* fullSubgroups */ {}));
+                DAWN_TRY_ASSIGN(_, ValidateComputeStageWorkgroupSize(
+                                       program, remappedEntryPoint.c_str(), r.limits,
+                                       /* fullSubgroups */ {}));
             }
 
             auto result = tint::glsl::writer::Generate(program, r.tintOptions, remappedEntryPoint);
             DAWN_INVALID_IF(result != tint::Success, "An error occurred while generating GLSL:\n%s",
                             result.Failure().reason.str());
 
-            return GLSLCompilation{{std::move(result->glsl), result->needs_internal_uniform_buffer,
-                                    result->bindpoint_to_data}};
+            return GLSLCompilation{{std::move(result->glsl)}};
         },
         "OpenGL.CompileShaderToGLSL");
 
@@ -423,20 +454,7 @@
     }
 
     GetDevice()->GetBlobCache()->EnsureStored(compilationResult);
-    *needsTextureBuiltinUniformBuffer = compilationResult->needsInternalUniformBuffer;
-
-    // Since the TextureBuiltinsFromUniform transform runs before BindingRemapper,
-    // we need to take care of their binding remappings here.
-    for (const auto& e : compilationResult->bindingPointToData) {
-        tint::BindingPoint bindingPoint = e.first;
-
-        const BindGroupLayoutInternalBase* bgl =
-            layout->GetBindGroupLayout(BindGroupIndex{bindingPoint.group});
-        bindingPoint.binding =
-            static_cast<uint32_t>(bgl->GetBindingIndex(BindingNumber{bindingPoint.binding}));
-
-        bindingPointToData->emplace(bindingPoint, e.second);
-    }
+    *needsTextureBuiltinUniformBuffer = needsInternalUBO;
 
     *combinedSamplers = std::move(combinedSamplerInfo);
     return shader;
diff --git a/src/dawn/native/opengl/ShaderModuleGL.h b/src/dawn/native/opengl/ShaderModuleGL.h
index 91efd24..a452b2fc 100644
--- a/src/dawn/native/opengl/ShaderModuleGL.h
+++ b/src/dawn/native/opengl/ShaderModuleGL.h
@@ -28,14 +28,12 @@
 #ifndef SRC_DAWN_NATIVE_OPENGL_SHADERMODULEGL_H_
 #define SRC_DAWN_NATIVE_OPENGL_SHADERMODULEGL_H_
 
-#include <memory>
 #include <string>
 #include <vector>
 
-#include "include/tint/tint.h"
-
 #include "dawn/native/Serializable.h"
 #include "dawn/native/ShaderModule.h"
+#include "dawn/native/opengl/BindingPoint.h"
 #include "dawn/native/opengl/opengl_platform.h"
 
 namespace dawn::native {
@@ -88,16 +86,14 @@
         ShaderModuleParseResult* parseResult,
         OwnedCompilationMessages* compilationMessages);
 
-    ResultOrError<GLuint> CompileShader(
-        const OpenGLFunctions& gl,
-        const ProgrammableStage& programmableStage,
-        SingleShaderStage stage,
-        CombinedSamplerInfo* combinedSamplers,
-        const PipelineLayout* layout,
-        bool* needsPlaceholderSampler,
-        bool* needsTextureBuiltinUniformBuffer,
-        tint::TextureBuiltinsFromUniformOptions::BindingPointToFieldAndOffset* bindingPointToData)
-        const;
+    ResultOrError<GLuint> CompileShader(const OpenGLFunctions& gl,
+                                        const ProgrammableStage& programmableStage,
+                                        SingleShaderStage stage,
+                                        CombinedSamplerInfo* combinedSamplers,
+                                        const PipelineLayout* layout,
+                                        bool* needsPlaceholderSampler,
+                                        bool* needsTextureBuiltinUniformBuffer,
+                                        BindingPointToFunctionAndOffset* bindingPointToData) const;
 
   private:
     ShaderModule(Device* device, const UnpackedPtr<ShaderModuleDescriptor>& descriptor);
diff --git a/src/tint/api/options/texture_builtins_from_uniform.h b/src/tint/api/options/texture_builtins_from_uniform.h
index bddb274..ac107e5 100644
--- a/src/tint/api/options/texture_builtins_from_uniform.h
+++ b/src/tint/api/options/texture_builtins_from_uniform.h
@@ -28,8 +28,7 @@
 #ifndef SRC_TINT_API_OPTIONS_TEXTURE_BUILTINS_FROM_UNIFORM_H_
 #define SRC_TINT_API_OPTIONS_TEXTURE_BUILTINS_FROM_UNIFORM_H_
 
-#include <unordered_map>
-#include <utility>
+#include <vector>
 
 #include "src/tint/api/common/binding_point.h"
 #include "src/tint/utils/reflection/reflection.h"
@@ -39,25 +38,16 @@
 /// Options used to specify a mapping of binding points to indices into a UBO
 /// from which to load buffer sizes.
 struct TextureBuiltinsFromUniformOptions {
-    /// Indicate the type of field for each entry to push.
-    enum class Field {
-        /// The number of mip levels of the bonnd texture view.
-        TextureNumLevels,
-        /// The number of samples per texel of the bound multipsampled texture.
-        TextureNumSamples,
-    };
-
-    /// Records the field and the byte offset of the data to push in the internal uniform buffer.
-    using FieldAndOffset = std::pair<Field, uint32_t>;
-    /// Maps from binding point to data entry with the information to populate the data.
-    using BindingPointToFieldAndOffset = std::unordered_map<BindingPoint, FieldAndOffset>;
-
     /// The binding point to use to generate a uniform buffer from which to read
     /// buffer sizes.
     BindingPoint ubo_binding = {};
 
+    /// Ordered list of binding points in the uniform buffer for polyfilling `textureNumSamples` and
+    /// `textureNumLevels`
+    std::vector<BindingPoint> ubo_bindingpoint_ordering = {};
+
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
-    TINT_REFLECT(ubo_binding);
+    TINT_REFLECT(ubo_binding, ubo_bindingpoint_ordering);
 };
 
 }  // namespace tint
diff --git a/src/tint/cmd/tint/main.cc b/src/tint/cmd/tint/main.cc
index 474088e..9797feb 100644
--- a/src/tint/cmd/tint/main.cc
+++ b/src/tint/cmd/tint/main.cc
@@ -1040,6 +1040,8 @@
     std::cerr << "GLSL writer not enabled in tint build" << std::endl;
     return false;
 #else
+    tint::inspector::Inspector inspector(program);
+
     auto generate = [&](const tint::Program& prg, const std::string entry_point_name,
                         [[maybe_unused]] tint::ast::PipelineStage stage) -> bool {
         tint::glsl::writer::Options gen_options;
@@ -1049,7 +1051,20 @@
 
         tint::TextureBuiltinsFromUniformOptions textureBuiltinsFromUniform;
         constexpr uint32_t kMaxBindGroups = 4u;
+
         textureBuiltinsFromUniform.ubo_binding = {kMaxBindGroups, 0u};
+
+        auto textureBuiltinsFromUniformData = inspector.GetTextureQueries(entry_point_name);
+        if (!textureBuiltinsFromUniformData.empty()) {
+            for (size_t i = 0; i < textureBuiltinsFromUniformData.size(); ++i) {
+                const auto& info = textureBuiltinsFromUniformData[i];
+
+                // This is the unmodified binding point from the WGSL shader.
+                tint::BindingPoint srcBindingPoint{info.group, info.binding};
+                textureBuiltinsFromUniform.ubo_bindingpoint_ordering.emplace_back(srcBindingPoint);
+            }
+        }
+
         gen_options.texture_builtins_from_uniform = std::move(textureBuiltinsFromUniform);
 
         auto result = tint::glsl::writer::Generate(prg, gen_options, entry_point_name);
@@ -1086,8 +1101,6 @@
         return true;
     };
 
-    tint::inspector::Inspector inspector(program);
-
     if (inspector.GetEntryPoints().empty()) {
         // Pass empty string here so that the GLSL generator will generate
         // code for all functions, reachable or not.
diff --git a/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc b/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
index 2026bd2..f1831e9 100644
--- a/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
@@ -217,11 +217,10 @@
     // TextureBuiltinsFromUniform must come before CombineSamplers to preserve texture binding point
     // info, instead of combined sampler binding point. As a result, TextureBuiltinsFromUniform also
     // comes before BindingRemapper so the binding point info it reflects is before remapping.
-    if (options.texture_builtins_from_uniform) {
-        manager.Add<TextureBuiltinsFromUniform>();
-        data.Add<TextureBuiltinsFromUniform::Config>(
-            options.texture_builtins_from_uniform->ubo_binding);
-    }
+    manager.Add<TextureBuiltinsFromUniform>();
+    data.Add<TextureBuiltinsFromUniform::Config>(
+        options.texture_builtins_from_uniform.ubo_binding,
+        options.texture_builtins_from_uniform.ubo_bindingpoint_ordering);
 
     data.Add<CombineSamplers::BindingInfo>(options.binding_map, options.placeholder_binding_point);
     manager.Add<CombineSamplers>();
@@ -249,10 +248,6 @@
     SanitizedResult result;
     ast::transform::DataMap outputs;
     result.program = manager.Run(in, data, outputs);
-    if (auto* res = outputs.Get<TextureBuiltinsFromUniform::Result>()) {
-        result.needs_internal_uniform_buffer = true;
-        result.bindpoint_to_data = std::move(res->bindpoint_to_data);
-    }
     return result;
 }
 
diff --git a/src/tint/lang/glsl/writer/ast_printer/ast_printer.h b/src/tint/lang/glsl/writer/ast_printer/ast_printer.h
index cfaf716..b35664f 100644
--- a/src/tint/lang/glsl/writer/ast_printer/ast_printer.h
+++ b/src/tint/lang/glsl/writer/ast_printer/ast_printer.h
@@ -67,13 +67,6 @@
 
     /// The sanitized program.
     Program program;
-
-    /// True if the shader needs a UBO.
-    bool needs_internal_uniform_buffer = false;
-
-    /// Store a map of global texture variable binding point to the byte offset and data type to
-    /// push into the internal uniform buffer.
-    TextureBuiltinsFromUniformOptions::BindingPointToFieldAndOffset bindpoint_to_data;
 };
 
 /// Sanitize a program in preparation for generating GLSL.
diff --git a/src/tint/lang/glsl/writer/ast_raise/BUILD.bazel b/src/tint/lang/glsl/writer/ast_raise/BUILD.bazel
index 540f314..c23fd29 100644
--- a/src/tint/lang/glsl/writer/ast_raise/BUILD.bazel
+++ b/src/tint/lang/glsl/writer/ast_raise/BUILD.bazel
@@ -52,7 +52,6 @@
   ],
   deps = [
     "//src/tint/api/common",
-    "//src/tint/api/options",
     "//src/tint/lang/core",
     "//src/tint/lang/core/constant",
     "//src/tint/lang/core/type",
@@ -92,7 +91,6 @@
   ],
   deps = [
     "//src/tint/api/common",
-    "//src/tint/api/options",
     "//src/tint/lang/core",
     "//src/tint/lang/core/constant",
     "//src/tint/lang/core/ir",
diff --git a/src/tint/lang/glsl/writer/ast_raise/BUILD.cmake b/src/tint/lang/glsl/writer/ast_raise/BUILD.cmake
index 0ca869d..1a658c0 100644
--- a/src/tint/lang/glsl/writer/ast_raise/BUILD.cmake
+++ b/src/tint/lang/glsl/writer/ast_raise/BUILD.cmake
@@ -53,7 +53,6 @@
 
 tint_target_add_dependencies(tint_lang_glsl_writer_ast_raise lib
   tint_api_common
-  tint_api_options
   tint_lang_core
   tint_lang_core_constant
   tint_lang_core_type
@@ -96,7 +95,6 @@
 
 tint_target_add_dependencies(tint_lang_glsl_writer_ast_raise_test test
   tint_api_common
-  tint_api_options
   tint_lang_core
   tint_lang_core_constant
   tint_lang_core_ir
diff --git a/src/tint/lang/glsl/writer/ast_raise/BUILD.gn b/src/tint/lang/glsl/writer/ast_raise/BUILD.gn
index 23c0067..166a313 100644
--- a/src/tint/lang/glsl/writer/ast_raise/BUILD.gn
+++ b/src/tint/lang/glsl/writer/ast_raise/BUILD.gn
@@ -55,7 +55,6 @@
     ]
     deps = [
       "${tint_src_dir}/api/common",
-      "${tint_src_dir}/api/options",
       "${tint_src_dir}/lang/core",
       "${tint_src_dir}/lang/core/constant",
       "${tint_src_dir}/lang/core/type",
@@ -96,7 +95,6 @@
       deps = [
         "${tint_src_dir}:gmock_and_gtest",
         "${tint_src_dir}/api/common",
-        "${tint_src_dir}/api/options",
         "${tint_src_dir}/lang/core",
         "${tint_src_dir}/lang/core/constant",
         "${tint_src_dir}/lang/core/ir",
diff --git a/src/tint/lang/glsl/writer/ast_raise/texture_builtins_from_uniform.cc b/src/tint/lang/glsl/writer/ast_raise/texture_builtins_from_uniform.cc
index bc40549..5ac9fec 100644
--- a/src/tint/lang/glsl/writer/ast_raise/texture_builtins_from_uniform.cc
+++ b/src/tint/lang/glsl/writer/ast_raise/texture_builtins_from_uniform.cc
@@ -27,8 +27,6 @@
 
 #include "src/tint/lang/glsl/writer/ast_raise/texture_builtins_from_uniform.h"
 
-#include <memory>
-#include <queue>
 #include <string>
 #include <utility>
 #include <variant>
@@ -49,7 +47,6 @@
 
 TINT_INSTANTIATE_TYPEINFO(tint::glsl::writer::TextureBuiltinsFromUniform);
 TINT_INSTANTIATE_TYPEINFO(tint::glsl::writer::TextureBuiltinsFromUniform::Config);
-TINT_INSTANTIATE_TYPEINFO(tint::glsl::writer::TextureBuiltinsFromUniform::Result);
 
 namespace tint::glsl::writer {
 
@@ -58,27 +55,10 @@
 /// The member name of the texture builtin values.
 constexpr std::string_view kTextureBuiltinValuesMemberNamePrefix = "texture_builtin_value_";
 
-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()) {
-                // GLSL ES  has no native support for the counterpart of
-                // textureNumLevels (textureQueryLevels) and textureNumSamples (textureSamples)
-                if (builtin->Fn() == wgsl::BuiltinFn::kTextureNumLevels) {
-                    return true;
-                }
-                if (builtin->Fn() == wgsl::BuiltinFn::kTextureNumSamples) {
-                    return true;
-                }
-            }
-        }
-    }
-    return false;
-}
-
 }  // namespace
 
 TextureBuiltinsFromUniform::TextureBuiltinsFromUniform() = default;
+
 TextureBuiltinsFromUniform::~TextureBuiltinsFromUniform() = default;
 
 /// PIMPL state for the transform
@@ -86,11 +66,8 @@
     /// Constructor
     /// @param program the source program
     /// @param in the input transform data
-    /// @param out the output transform data
-    explicit State(const Program& program,
-                   const ast::transform::DataMap& in,
-                   ast::transform::DataMap& out)
-        : src(program), inputs(in), outputs(out) {}
+    explicit State(const Program& program, const ast::transform::DataMap& in)
+        : src(program), inputs(in) {}
 
     /// Runs the transform
     /// @returns the new program or SkipTransform if the transform is not required
@@ -103,12 +80,14 @@
                     std::string(tint::TypeInfo::Of<TextureBuiltinsFromUniform>().name));
             return resolver::Resolve(b);
         }
+        ubo_bindingpoint_ordering = cfg->ubo_bindingpoint_ordering;
 
-        if (!ShouldRun(src)) {
+        // If there's no interested texture builtin at all, skip the transform.
+        if (ubo_bindingpoint_ordering.empty()) {
             return SkipTransform;
         }
 
-        // The dependency order declartions guaranteed that we traverse interested functions in the
+        // The dependency order declarations guaranteed that we traverse interested functions in the
         // following order:
         // 1. texture builtins
         // 2. user function directly calls texture builtins
@@ -143,21 +122,17 @@
                             auto* texture_sem = sem.GetVal(texture_expr)->RootIdentifier();
                             TINT_ASSERT(texture_sem);
 
-                            TextureBuiltinsFromUniformOptions::Field dataType =
-                                GetFieldFromBuiltinFunctionType(builtin->Fn());
-
                             tint::Switch(
                                 texture_sem,
                                 [&](const sem::GlobalVariable* global) {
                                     // This texture variable is a global variable.
-                                    auto binding = GetAndRecordGlobalBinding(global, dataType);
+                                    auto binding = GetGlobalBinding(global);
                                     // Record the call and binding to be replaced later.
                                     builtin_to_replace.Add(call_expr, binding);
                                 },
                                 [&](const sem::Variable* variable) {
                                     // This texture variable is a user function parameter.
-                                    auto new_param =
-                                        GetAndRecordFunctionParameter(fn, variable, dataType);
+                                    auto new_param = GetAndRecordFunctionParameter(fn, variable);
                                     // Record the call and new_param to be replaced later.
                                     builtin_to_replace.Add(call_expr, new_param);
                                 },  //
@@ -187,15 +162,14 @@
                                         texture_sem,
                                         [&](const sem::GlobalVariable* global) {
                                             // This texture variable is a global variable.
-                                            auto binding =
-                                                GetAndRecordGlobalBinding(global, info->field);
+                                            auto binding = GetGlobalBinding(global);
                                             // Record the binding to add to args.
                                             args.Push(binding);
                                         },
                                         [&](const sem::Variable* variable) {
                                             // This texture variable is a user function parameter.
-                                            auto new_param = GetAndRecordFunctionParameter(
-                                                fn, variable, info->field);
+                                            auto new_param =
+                                                GetAndRecordFunctionParameter(fn, variable);
                                             // Record adding extra function parameter
                                             args.Push(new_param);
                                         },  //
@@ -207,11 +181,6 @@
             }
         }
 
-        // If there's no interested texture builtin at all, skip the transform.
-        if (bindpoint_to_data.empty()) {
-            return SkipTransform;
-        }
-
         // If any functions need extra params, add them now.
         if (!fn_to_data.IsEmpty()) {
             for (auto pair : fn_to_data) {
@@ -263,8 +232,6 @@
             }
         }
 
-        outputs.Add<Result>(bindpoint_to_data);
-
         ctx.Clone();
         return resolver::Resolve(b);
     }
@@ -274,8 +241,6 @@
     const Program& src;
     /// The transform inputs
     const ast::transform::DataMap& inputs;
-    /// The transform outputs
-    ast::transform::DataMap& outputs;
     /// The target program builder
     ProgramBuilder b;
     /// The clone context
@@ -283,19 +248,10 @@
     /// Alias to the semantic info in ctx.src
     const sem::Info& sem = src.Sem();
 
-    /// The bindpoint to byte offset and field to pass out in transform result.
-    /// For one texture type, it could only be passed into one of the
-    /// textureNumLevels or textureNumSamples because their accepting param texture
-    /// type is different. There cannot be a binding entry with both field type.
-    /// Note: because this transform must be run before CombineSampler and BindingRemapper,
-    /// the binding number here is before remapped.
-    Result::BindingPointToFieldAndOffset bindpoint_to_data;
+    /// Ordered list of binding points for where they appear in the UBO
+    std::vector<BindingPoint> ubo_bindingpoint_ordering;
 
     struct FunctionExtraParamInfo {
-        using Field = TextureBuiltinsFromUniformOptions::Field;
-        // The kind of texture information this parameter holds.
-        Field field = Field::TextureNumLevels;
-
         // The extra passed in param that corresponds to the texture param.
         const ast::Parameter* param = nullptr;
 
@@ -337,6 +293,7 @@
 
     /// The internal uniform buffer
     const ast::Variable* ubo = nullptr;
+
     /// Get or create a UBO including u32 scalars for texture builtin values.
     /// @returns the symbol of the uniform buffer variable.
     Symbol GetUboSym() {
@@ -348,15 +305,13 @@
         auto* cfg = inputs.Get<Config>();
 
         Vector<const ast::StructMember*, 16> new_members;
-        new_members.Resize(bindpoint_to_data.size());
-        for (auto it : bindpoint_to_data) {
-            // Emit a u32 scalar for each binding that needs builtin value passed in.
-            size_t i = it.second.second / sizeof(uint32_t);
-            TINT_ASSERT(i < new_members.Length());
+        new_members.Resize(ubo_bindingpoint_ordering.size());
+
+        for (size_t i = 0; i < ubo_bindingpoint_ordering.size(); ++i) {
             // Append the vector index with the variable name to avoid unstable naming issue.
             auto sym = b.Symbols().New(std::string(kTextureBuiltinValuesMemberNamePrefix) +
                                        std::to_string(i));
-            bindpoint_to_syms.Add(it.first, sym);
+            bindpoint_to_syms.Add(ubo_bindingpoint_ordering[i], sym);
             new_members[i] = b.Member(sym, b.ty.u32());
         }
 
@@ -403,48 +358,31 @@
     /// @param binding of the global variable.
     /// @returns an expression of the builtin value.
     const ast::Expression* GetUniformValue(const BindingPoint& binding) {
-        auto iter = bindpoint_to_data.find(binding);
-        TINT_ASSERT(iter != bindpoint_to_data.end());
-
         // Make sure GetUboSym() is called first to initialize the uniform buffer struct.
         auto ubo_sym = GetUboSym();
+
         // Load the builtin value from the UBO.
         auto member_sym = bindpoint_to_syms.Get(binding);
         TINT_ASSERT(member_sym.has_value());
-        auto* builtin_value = b.MemberAccessor(ubo_sym, *member_sym);
 
-        return builtin_value;
+        return b.MemberAccessor(ubo_sym, *member_sym);
     }
 
-    /// Get and return the binding of the global texture variable. Record in bindpoint_to_data if
-    /// first visited.
+    /// Get and return the binding of the global texture variable.
     /// @param global global variable of the texture variable.
-    /// @param field type of the interested builtin function data related to this texture.
     /// @returns binding of the global variable.
-    BindingPoint GetAndRecordGlobalBinding(const sem::GlobalVariable* global,
-                                           TextureBuiltinsFromUniformOptions::Field field) {
-        auto binding = global->Attributes().binding_point.value();
-        auto iter = bindpoint_to_data.find(binding);
-        if (iter == bindpoint_to_data.end()) {
-            // First visit, recording the binding.
-            uint32_t index = static_cast<uint32_t>(bindpoint_to_data.size());
-            bindpoint_to_data.emplace(
-                binding,
-                Result::FieldAndOffset{field, index * static_cast<uint32_t>(sizeof(uint32_t))});
-        }
-        return binding;
+    BindingPoint GetGlobalBinding(const sem::GlobalVariable* global) {
+        return global->Attributes().binding_point.value();
     }
 
     /// Find which function param is the given texture variable.
-    /// Add a new u32 param relates to this texture param. Record in fn_to_data if first visited.
+    /// Add a new u32 param relates to this texture param. Record in fn_to_data if first
+    /// visited.
     /// @param fn the current function scope.
     /// @param var the texture variable.
-    /// @param field type of the interested builtin function data related to this texture.
     /// @returns the new u32 function parameter.
-    const ast::Parameter* GetAndRecordFunctionParameter(
-        const sem::Function* fn,
-        const sem::Variable* var,
-        TextureBuiltinsFromUniformOptions::Field field) {
+    const ast::Parameter* GetAndRecordFunctionParameter(const sem::Function* fn,
+                                                        const sem::Variable* var) {
         auto& param_to_info = fn_to_data.GetOrCreate(
             fn, [&] { return Hashmap<const ast::Parameter*, FunctionExtraParamInfo, 4>(); });
 
@@ -456,50 +394,36 @@
             }
         }
         TINT_ASSERT(param);
+
         // Get or record a new u32 param to this function if first visited.
         auto entry = param_to_info.Get(param);
         if (entry.has_value()) {
             return entry->param;
         }
+
         const ast::Parameter* new_param = b.Param(b.Sym(), b.ty.u32());
         size_t idx = param_to_info.Count();
-        param_to_info.Add(param, FunctionExtraParamInfo{field, new_param, idx});
+        param_to_info.Add(param, FunctionExtraParamInfo{new_param, idx});
         return new_param;
     }
-
-    /// Get the uniform options field for the builtin function.
-    /// @param type of the builtin function
-    /// @returns corresponding TextureBuiltinsFromUniformOptions::Field for the builtin
-    static TextureBuiltinsFromUniformOptions::Field GetFieldFromBuiltinFunctionType(
-        wgsl::BuiltinFn type) {
-        switch (type) {
-            case wgsl::BuiltinFn::kTextureNumLevels:
-                return TextureBuiltinsFromUniformOptions::Field::TextureNumLevels;
-            case wgsl::BuiltinFn::kTextureNumSamples:
-                return TextureBuiltinsFromUniformOptions::Field::TextureNumSamples;
-            default:
-                TINT_UNREACHABLE() << "unsupported builtin function type " << type;
-        }
-        return TextureBuiltinsFromUniformOptions::Field::TextureNumLevels;
-    }
 };
 
 ast::transform::Transform::ApplyResult TextureBuiltinsFromUniform::Apply(
     const Program& src,
     const ast::transform::DataMap& inputs,
-    ast::transform::DataMap& outputs) const {
-    return State{src, inputs, outputs}.Run();
+    ast::transform::DataMap&) const {
+    return State{src, inputs}.Run();
 }
 
-TextureBuiltinsFromUniform::Config::Config(BindingPoint ubo_bp) : ubo_binding(ubo_bp) {}
+TextureBuiltinsFromUniform::Config::Config(BindingPoint ubo_bp,
+                                           const std::vector<BindingPoint>& ordering)
+    : ubo_binding(ubo_bp), ubo_bindingpoint_ordering(ordering) {}
+
 TextureBuiltinsFromUniform::Config::Config(const Config&) = default;
+
 TextureBuiltinsFromUniform::Config& TextureBuiltinsFromUniform::Config::operator=(const Config&) =
     default;
-TextureBuiltinsFromUniform::Config::~Config() = default;
 
-TextureBuiltinsFromUniform::Result::Result(BindingPointToFieldAndOffset bindpoint_to_data_in)
-    : bindpoint_to_data(std::move(bindpoint_to_data_in)) {}
-TextureBuiltinsFromUniform::Result::Result(const Result&) = default;
-TextureBuiltinsFromUniform::Result::~Result() = default;
+TextureBuiltinsFromUniform::Config::~Config() = default;
 
 }  // namespace tint::glsl::writer
diff --git a/src/tint/lang/glsl/writer/ast_raise/texture_builtins_from_uniform.h b/src/tint/lang/glsl/writer/ast_raise/texture_builtins_from_uniform.h
index a54f02b..550e0f8 100644
--- a/src/tint/lang/glsl/writer/ast_raise/texture_builtins_from_uniform.h
+++ b/src/tint/lang/glsl/writer/ast_raise/texture_builtins_from_uniform.h
@@ -28,11 +28,9 @@
 #ifndef SRC_TINT_LANG_GLSL_WRITER_AST_RAISE_TEXTURE_BUILTINS_FROM_UNIFORM_H_
 #define SRC_TINT_LANG_GLSL_WRITER_AST_RAISE_TEXTURE_BUILTINS_FROM_UNIFORM_H_
 
-#include <unordered_map>
-#include <unordered_set>
+#include <vector>
 
 #include "src/tint/api/common/binding_point.h"
-#include "src/tint/api/options/texture_builtins_from_uniform.h"
 #include "src/tint/lang/wgsl/ast/transform/transform.h"
 
 // Forward declarations
@@ -47,6 +45,7 @@
 /// builtin functions are not available in some version of GLSL.
 ///
 /// The generated uniform buffer will have the form:
+///
 /// ```
 /// struct internal_uniform {
 ///  texture_builtin_value_0 : u32,
@@ -54,15 +53,17 @@
 ///
 /// @group(0) @binding(0) var tex : texture_2d<f32>;
 /// ```
+///
 /// The binding group and number used for this uniform buffer are provided via
 /// the `Config` transform input.
 ///
-/// The transform coverts the texture builtins calls into values lookup from the internal
-/// buffer. If the texture is a function parameter instead of a global variable, this transform
-/// also takes care of adding extra paramters and arguments to these functions and their callsites.
+/// The transform coverts the texture builtins calls into values lookup from the internal buffer. If
+/// the texture is a function parameter instead of a global variable, this transform also takes care
+/// of adding extra parameters and arguments to these functions and their call-sites.
 ///
 /// This transform must run before `CombineSamplers` transform so that the binding point of the
 /// original texture object can be preserved.
+///
 class TextureBuiltinsFromUniform final
     : public Castable<TextureBuiltinsFromUniform, ast::transform::Transform> {
   public:
@@ -75,7 +76,8 @@
     struct Config final : public Castable<Config, ast::transform::Data> {
         /// Constructor
         /// @param ubo_bp the binding point to use for the generated uniform buffer.
-        explicit Config(BindingPoint ubo_bp);
+        /// @param ordering the ordered list of binding points to appear in the UBO
+        Config(BindingPoint ubo_bp, const std::vector<BindingPoint>& ordering);
 
         /// Copy constructor
         Config(const Config&);
@@ -89,34 +91,14 @@
 
         /// The binding point to use for the generated uniform buffer.
         BindingPoint ubo_binding;
-    };
 
-    /// Information produced about what the transform did.
-    /// If there were no calls to the textureNumLevels() or textureNumSamples() builtin, then no
-    /// Result will be emitted.
-    struct Result final : public Castable<Result, ast::transform::Data> {
-        /// Using for shorter names
-        /// Records the field and the byte offset of the data to push in the internal uniform
-        /// buffer.
-        using FieldAndOffset = TextureBuiltinsFromUniformOptions::FieldAndOffset;
-        /// Maps from binding point to data entry with the information to populate the data.
-        using BindingPointToFieldAndOffset =
-            TextureBuiltinsFromUniformOptions::BindingPointToFieldAndOffset;
-
-        /// Constructor
-        /// @param bindpoint_to_data_in mapping from binding points of global texture variables to
-        /// the byte offsets and data types needed to be pushed into the internal uniform buffer.
-        explicit Result(BindingPointToFieldAndOffset bindpoint_to_data_in);
-
-        /// Copy constructor
-        Result(const Result&);
-
-        /// Destructor
-        ~Result() override;
-
-        /// A map of global texture variable binding point to the byte offset and data type to push
-        /// into the internal uniform buffer.
-        BindingPointToFieldAndOffset bindpoint_to_data;
+        /// The set of binding points which will be in the extra UBO buffer. The binding points are
+        /// provided in the order that the data will be in the UBO buffer.
+        ///
+        /// Note, a given `BindingPoint` _must_ only appear once in the ordering list. This works
+        /// because the two types of calls we're substituting `textureNumLevels` and
+        /// `textureNumSamples` work on a disjoint set of texture types.
+        std::vector<BindingPoint> ubo_bindingpoint_ordering;
     };
 
     /// @copydoc ast::transform::Transform::Apply
diff --git a/src/tint/lang/glsl/writer/ast_raise/texture_builtins_from_uniform_test.cc b/src/tint/lang/glsl/writer/ast_raise/texture_builtins_from_uniform_test.cc
index cabe7e1..3ee9775 100644
--- a/src/tint/lang/glsl/writer/ast_raise/texture_builtins_from_uniform_test.cc
+++ b/src/tint/lang/glsl/writer/ast_raise/texture_builtins_from_uniform_test.cc
@@ -36,53 +36,6 @@
 
 using TextureBuiltinsFromUniformTest = ast::transform::TransformTest;
 
-TEST_F(TextureBuiltinsFromUniformTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
-
-    ast::transform::DataMap data;
-    data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
-
-    EXPECT_FALSE(ShouldRun<TextureBuiltinsFromUniform>(src, data));
-}
-
-TEST_F(TextureBuiltinsFromUniformTest, ShouldRunNoTextureNumLevels) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@compute @workgroup_size(1)
-fn main() {
-  _ = textureDimensions(t);
-}
-)";
-
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
-
-    ast::transform::DataMap data;
-    data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
-
-    EXPECT_FALSE(ShouldRun<TextureBuiltinsFromUniform>(src, data));
-}
-
-TEST_F(TextureBuiltinsFromUniformTest, ShouldRunWithTextureNumLevels) {
-    auto* src = R"(
-@group(0) @binding(0) var t : texture_2d<f32>;
-
-@compute @workgroup_size(1)
-fn main() {
-  var len : u32 = textureNumLevels(t);
-}
-)";
-
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
-
-    ast::transform::DataMap data;
-    data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
-
-    EXPECT_TRUE(ShouldRun<TextureBuiltinsFromUniform>(src, data));
-}
-
 TEST_F(TextureBuiltinsFromUniformTest, Error_MissingTransformData) {
     auto* src = R"(
 @group(0) @binding(0) var t : texture_2d<f32>;
@@ -126,25 +79,14 @@
 }
 )";
 
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
+    std::vector<BindingPoint> bps = {{0, 0}};
+    TextureBuiltinsFromUniform::Config cfg({0, 30u}, bps);
 
     ast::transform::DataMap data;
     data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
 
     auto got = Run<TextureBuiltinsFromUniform>(src, data);
-
     EXPECT_EQ(expect, str(got));
-    auto* val = got.data.Get<TextureBuiltinsFromUniform::Result>();
-    ASSERT_NE(val, nullptr);
-    // Note: Using the following EXPECT_EQ directly on BindingPointToFieldAndOffset seems to cause
-    // compiler to hang. EXPECT_EQ(
-    //     TextureBuiltinsFromUniformOptions::BindingPointToFieldAndOffset{
-    //         {BindgPoint{0u, 0u},
-    //          std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 0u)}},
-    //     val->bindpoint_to_data);
-    EXPECT_EQ(1u, val->bindpoint_to_data.size());
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 0u),
-              val->bindpoint_to_data.at(BindingPoint{0, 0}));
 }
 
 TEST_F(TextureBuiltinsFromUniformTest, BasicTextureNumSamples) {
@@ -172,19 +114,14 @@
 }
 )";
 
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
+    std::vector<BindingPoint> bps = {{0, 0}};
+    TextureBuiltinsFromUniform::Config cfg({0, 30u}, bps);
 
     ast::transform::DataMap data;
     data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
 
     auto got = Run<TextureBuiltinsFromUniform>(src, data);
-
     EXPECT_EQ(expect, str(got));
-    auto* val = got.data.Get<TextureBuiltinsFromUniform::Result>();
-    ASSERT_NE(val, nullptr);
-    EXPECT_EQ(1u, val->bindpoint_to_data.size());
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumSamples, 0u),
-              val->bindpoint_to_data.at(BindingPoint{0, 0}));
 }
 
 TEST_F(TextureBuiltinsFromUniformTest, SameBuiltinCalledMultipleTimes) {
@@ -214,19 +151,14 @@
 }
 )";
 
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
+    std::vector<BindingPoint> bps = {{0, 0}};
+    TextureBuiltinsFromUniform::Config cfg({0, 30u}, bps);
 
     ast::transform::DataMap data;
     data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
 
     auto got = Run<TextureBuiltinsFromUniform>(src, data);
-
     EXPECT_EQ(expect, str(got));
-    auto* val = got.data.Get<TextureBuiltinsFromUniform::Result>();
-    ASSERT_NE(val, nullptr);
-    EXPECT_EQ(1u, val->bindpoint_to_data.size());
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 0u),
-              val->bindpoint_to_data.at(BindingPoint{0, 0}));
 }
 
 TEST_F(TextureBuiltinsFromUniformTest, SameBuiltinCalledMultipleTimesTextureNumSamples) {
@@ -256,19 +188,14 @@
 }
 )";
 
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
+    std::vector<BindingPoint> bps = {{0, 0}};
+    TextureBuiltinsFromUniform::Config cfg({0, 30u}, bps);
 
     ast::transform::DataMap data;
     data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
 
     auto got = Run<TextureBuiltinsFromUniform>(src, data);
-
     EXPECT_EQ(expect, str(got));
-    auto* val = got.data.Get<TextureBuiltinsFromUniform::Result>();
-    ASSERT_NE(val, nullptr);
-    EXPECT_EQ(1u, val->bindpoint_to_data.size());
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumSamples, 0u),
-              val->bindpoint_to_data.at(BindingPoint{0, 0}));
 }
 
 TEST_F(TextureBuiltinsFromUniformTest, TextureAsFunctionParameterBasic) {
@@ -304,19 +231,14 @@
 }
 )";
 
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
+    std::vector<BindingPoint> bps = {{0, 0}};
+    TextureBuiltinsFromUniform::Config cfg({0, 30u}, bps);
 
     ast::transform::DataMap data;
     data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
 
     auto got = Run<TextureBuiltinsFromUniform>(src, data);
-
     EXPECT_EQ(expect, str(got));
-    auto* val = got.data.Get<TextureBuiltinsFromUniform::Result>();
-    ASSERT_NE(val, nullptr);
-    EXPECT_EQ(1u, val->bindpoint_to_data.size());
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 0u),
-              val->bindpoint_to_data.at(BindingPoint{0, 0}));
 }
 
 TEST_F(TextureBuiltinsFromUniformTest, TextureAsFunctionParameterUsedTwice) {
@@ -356,19 +278,14 @@
 }
 )";
 
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
+    std::vector<BindingPoint> bps = {{0, 0}};
+    TextureBuiltinsFromUniform::Config cfg({0, 30u}, bps);
 
     ast::transform::DataMap data;
     data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
 
     auto got = Run<TextureBuiltinsFromUniform>(src, data);
-
     EXPECT_EQ(expect, str(got));
-    auto* val = got.data.Get<TextureBuiltinsFromUniform::Result>();
-    ASSERT_NE(val, nullptr);
-    EXPECT_EQ(1u, val->bindpoint_to_data.size());
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 0u),
-              val->bindpoint_to_data.at(BindingPoint{0, 0}));
 }
 
 TEST_F(TextureBuiltinsFromUniformTest, TextureAsFunctionParameterMultipleParameters) {
@@ -412,23 +329,14 @@
 }
 )";
 
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
+    std::vector<BindingPoint> bps = {{0, 0}, {0, 1}, {0, 2}};
+    TextureBuiltinsFromUniform::Config cfg({0, 30u}, bps);
 
     ast::transform::DataMap data;
     data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
 
     auto got = Run<TextureBuiltinsFromUniform>(src, data);
-
     EXPECT_EQ(expect, str(got));
-    auto* val = got.data.Get<TextureBuiltinsFromUniform::Result>();
-    ASSERT_NE(val, nullptr);
-    EXPECT_EQ(3u, val->bindpoint_to_data.size());
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 0u),
-              val->bindpoint_to_data.at(BindingPoint{0, 0}));
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 4u),
-              val->bindpoint_to_data.at(BindingPoint{0, 1}));
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 8u),
-              val->bindpoint_to_data.at(BindingPoint{0, 2}));
 }
 
 TEST_F(TextureBuiltinsFromUniformTest, TextureAsFunctionParameterNested) {
@@ -472,19 +380,14 @@
 }
 )";
 
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
+    std::vector<BindingPoint> bps = {{0, 0}};
+    TextureBuiltinsFromUniform::Config cfg({0, 30u}, bps);
 
     ast::transform::DataMap data;
     data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
 
     auto got = Run<TextureBuiltinsFromUniform>(src, data);
-
     EXPECT_EQ(expect, str(got));
-    auto* val = got.data.Get<TextureBuiltinsFromUniform::Result>();
-    ASSERT_NE(val, nullptr);
-    EXPECT_EQ(1u, val->bindpoint_to_data.size());
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 0u),
-              val->bindpoint_to_data.at(BindingPoint{0, 0}));
 }
 
 TEST_F(TextureBuiltinsFromUniformTest, TextureAsFunctionParameterMixed) {
@@ -549,25 +452,14 @@
 }
 )";
 
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
+    std::vector<BindingPoint> bps = {{0, 1}, {0, 3}, {0, 0}, {0, 2}};
+    TextureBuiltinsFromUniform::Config cfg({0, 30u}, bps);
 
     ast::transform::DataMap data;
     data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
 
     auto got = Run<TextureBuiltinsFromUniform>(src, data);
-
     EXPECT_EQ(expect, str(got));
-    auto* val = got.data.Get<TextureBuiltinsFromUniform::Result>();
-    ASSERT_NE(val, nullptr);
-    EXPECT_EQ(4u, val->bindpoint_to_data.size());
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 0u),
-              val->bindpoint_to_data.at(BindingPoint{0, 1}));
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 4u),
-              val->bindpoint_to_data.at(BindingPoint{0, 3}));
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 8u),
-              val->bindpoint_to_data.at(BindingPoint{0, 0}));
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 12u),
-              val->bindpoint_to_data.at(BindingPoint{0, 2}));
 }
 
 TEST_F(TextureBuiltinsFromUniformTest, MultipleTextures) {
@@ -625,7 +517,8 @@
 }
 )";
 
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
+    std::vector<BindingPoint> bps = {{0, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4}, {1, 0}};
+    TextureBuiltinsFromUniform::Config cfg({0, 30u}, bps);
 
     ast::transform::DataMap data;
     data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
@@ -633,21 +526,6 @@
     auto got = Run<TextureBuiltinsFromUniform>(src, data);
 
     EXPECT_EQ(expect, str(got));
-    auto* val = got.data.Get<TextureBuiltinsFromUniform::Result>();
-    ASSERT_NE(val, nullptr);
-    EXPECT_EQ(6u, val->bindpoint_to_data.size());
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 0u),
-              val->bindpoint_to_data.at(BindingPoint{0, 0}));
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumSamples, 4u),
-              val->bindpoint_to_data.at(BindingPoint{0, 1}));
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 8u),
-              val->bindpoint_to_data.at(BindingPoint{0, 2}));
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 12u),
-              val->bindpoint_to_data.at(BindingPoint{0, 3}));
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 16u),
-              val->bindpoint_to_data.at(BindingPoint{0, 4}));
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumSamples, 20u),
-              val->bindpoint_to_data.at(BindingPoint{1, 0}));
 }
 
 TEST_F(TextureBuiltinsFromUniformTest, BindingPointExist) {
@@ -682,7 +560,8 @@
 }
 )";
 
-    TextureBuiltinsFromUniform::Config cfg({0, 30u});
+    std::vector<BindingPoint> bps = {{0, 0}};
+    TextureBuiltinsFromUniform::Config cfg({0, 30u}, bps);
 
     ast::transform::DataMap data;
     data.Add<TextureBuiltinsFromUniform::Config>(std::move(cfg));
@@ -690,10 +569,6 @@
     auto got = Run<TextureBuiltinsFromUniform>(src, data);
 
     EXPECT_EQ(expect, str(got));
-    auto* val = got.data.Get<TextureBuiltinsFromUniform::Result>();
-    ASSERT_NE(val, nullptr);
-    EXPECT_EQ(std::make_pair(TextureBuiltinsFromUniformOptions::Field::TextureNumLevels, 0u),
-              val->bindpoint_to_data.at(BindingPoint{0, 0}));
 }
 
 }  // namespace
diff --git a/src/tint/lang/glsl/writer/common/BUILD.bazel b/src/tint/lang/glsl/writer/common/BUILD.bazel
index 1a61a9b..71f4677 100644
--- a/src/tint/lang/glsl/writer/common/BUILD.bazel
+++ b/src/tint/lang/glsl/writer/common/BUILD.bazel
@@ -50,7 +50,6 @@
   deps = [
     "//src/tint/api/common",
     "//src/tint/api/options",
-    "//src/tint/lang/core",
     "//src/tint/lang/wgsl/sem",
     "//src/tint/utils/macros",
     "//src/tint/utils/math",
diff --git a/src/tint/lang/glsl/writer/common/BUILD.cmake b/src/tint/lang/glsl/writer/common/BUILD.cmake
index 8c176fe..a015049 100644
--- a/src/tint/lang/glsl/writer/common/BUILD.cmake
+++ b/src/tint/lang/glsl/writer/common/BUILD.cmake
@@ -51,7 +51,6 @@
 tint_target_add_dependencies(tint_lang_glsl_writer_common lib
   tint_api_common
   tint_api_options
-  tint_lang_core
   tint_lang_wgsl_sem
   tint_utils_macros
   tint_utils_math
diff --git a/src/tint/lang/glsl/writer/common/BUILD.gn b/src/tint/lang/glsl/writer/common/BUILD.gn
index a91c610..e833eb3 100644
--- a/src/tint/lang/glsl/writer/common/BUILD.gn
+++ b/src/tint/lang/glsl/writer/common/BUILD.gn
@@ -49,7 +49,6 @@
     deps = [
       "${tint_src_dir}/api/common",
       "${tint_src_dir}/api/options",
-      "${tint_src_dir}/lang/core",
       "${tint_src_dir}/lang/wgsl/sem",
       "${tint_src_dir}/utils/macros",
       "${tint_src_dir}/utils/math",
diff --git a/src/tint/lang/glsl/writer/common/options.h b/src/tint/lang/glsl/writer/common/options.h
index b795d10..c97026e 100644
--- a/src/tint/lang/glsl/writer/common/options.h
+++ b/src/tint/lang/glsl/writer/common/options.h
@@ -28,14 +28,12 @@
 #ifndef SRC_TINT_LANG_GLSL_WRITER_COMMON_OPTIONS_H_
 #define SRC_TINT_LANG_GLSL_WRITER_COMMON_OPTIONS_H_
 
-#include <optional>
 #include <string>
 #include <unordered_map>
 
 #include "src/tint/api/options/binding_remapper.h"
 #include "src/tint/api/options/external_texture.h"
 #include "src/tint/api/options/texture_builtins_from_uniform.h"
-#include "src/tint/lang/core/access.h"
 #include "src/tint/lang/glsl/writer/common/version.h"
 #include "src/tint/lang/wgsl/sem/sampler_texture_pair.h"
 
@@ -83,7 +81,7 @@
     /// Options used to map WGSL textureNumLevels/textureNumSamples builtins to internal uniform
     /// buffer values. If not specified, emits corresponding GLSL builtins
     /// textureQueryLevels/textureSamples directly.
-    std::optional<TextureBuiltinsFromUniformOptions> texture_builtins_from_uniform = std::nullopt;
+    TextureBuiltinsFromUniformOptions texture_builtins_from_uniform = {};
 
     /// Reflect the fields of this class so that it can be used by tint::ForeachField()
     TINT_REFLECT(disable_robustness,
diff --git a/src/tint/lang/glsl/writer/output.h b/src/tint/lang/glsl/writer/output.h
index bb52015..8f205d1 100644
--- a/src/tint/lang/glsl/writer/output.h
+++ b/src/tint/lang/glsl/writer/output.h
@@ -32,7 +32,6 @@
 #include <utility>
 #include <vector>
 
-#include "src/tint/api/options/texture_builtins_from_uniform.h"
 #include "src/tint/lang/wgsl/ast/pipeline_stage.h"
 
 namespace tint::glsl::writer {
@@ -50,13 +49,6 @@
 
     /// The generated GLSL.
     std::string glsl = "";
-
-    /// True if the shader needs a UBO.
-    bool needs_internal_uniform_buffer = false;
-
-    /// Store a map of global texture variable binding points to the byte offset and data type to
-    /// push into the internal uniform buffer.
-    TextureBuiltinsFromUniformOptions::BindingPointToFieldAndOffset bindpoint_to_data;
 };
 
 }  // namespace tint::glsl::writer
diff --git a/src/tint/lang/glsl/writer/writer.cc b/src/tint/lang/glsl/writer/writer.cc
index c1ff290..17bd7ef 100644
--- a/src/tint/lang/glsl/writer/writer.cc
+++ b/src/tint/lang/glsl/writer/writer.cc
@@ -76,8 +76,6 @@
     }
 
     output.glsl = impl->Result();
-    output.needs_internal_uniform_buffer = sanitized_result.needs_internal_uniform_buffer;
-    output.bindpoint_to_data = std::move(sanitized_result.bindpoint_to_data);
 
     return output;
 }
diff --git a/src/tint/lang/wgsl/inspector/inspector.cc b/src/tint/lang/wgsl/inspector/inspector.cc
index d7fe564..bc78781 100644
--- a/src/tint/lang/wgsl/inspector/inspector.cc
+++ b/src/tint/lang/wgsl/inspector/inspector.cc
@@ -1141,6 +1141,7 @@
                 });
         }
     }
+
     return res;
 }