[glsl] Add OffsetFirstIndex transform
Use the push constant layout to load the offset values and add them to
the builtins.
Bug: 42251044
Change-Id: Ie46d6cc462960254dbc31467e27edabcaa841c3f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/209855
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/glsl/writer/raise/BUILD.bazel b/src/tint/lang/glsl/writer/raise/BUILD.bazel
index 8122943..172c47c 100644
--- a/src/tint/lang/glsl/writer/raise/BUILD.bazel
+++ b/src/tint/lang/glsl/writer/raise/BUILD.bazel
@@ -42,6 +42,7 @@
"binary_polyfill.cc",
"bitcast_polyfill.cc",
"builtin_polyfill.cc",
+ "offset_first_index.cc",
"raise.cc",
"shader_io.cc",
"texture_builtins_from_uniform.cc",
@@ -51,6 +52,7 @@
"binary_polyfill.h",
"bitcast_polyfill.h",
"builtin_polyfill.h",
+ "offset_first_index.h",
"raise.h",
"shader_io.h",
"texture_builtins_from_uniform.h",
@@ -103,6 +105,7 @@
"binary_polyfill_test.cc",
"bitcast_polyfill_test.cc",
"builtin_polyfill_test.cc",
+ "offset_first_index_test.cc",
"shader_io_test.cc",
"texture_builtins_from_uniform_test.cc",
"texture_polyfill_test.cc",
diff --git a/src/tint/lang/glsl/writer/raise/BUILD.cmake b/src/tint/lang/glsl/writer/raise/BUILD.cmake
index 0893974..16fc29d 100644
--- a/src/tint/lang/glsl/writer/raise/BUILD.cmake
+++ b/src/tint/lang/glsl/writer/raise/BUILD.cmake
@@ -47,6 +47,8 @@
lang/glsl/writer/raise/bitcast_polyfill.h
lang/glsl/writer/raise/builtin_polyfill.cc
lang/glsl/writer/raise/builtin_polyfill.h
+ lang/glsl/writer/raise/offset_first_index.cc
+ lang/glsl/writer/raise/offset_first_index.h
lang/glsl/writer/raise/raise.cc
lang/glsl/writer/raise/raise.h
lang/glsl/writer/raise/shader_io.cc
@@ -110,6 +112,7 @@
lang/glsl/writer/raise/binary_polyfill_test.cc
lang/glsl/writer/raise/bitcast_polyfill_test.cc
lang/glsl/writer/raise/builtin_polyfill_test.cc
+ lang/glsl/writer/raise/offset_first_index_test.cc
lang/glsl/writer/raise/shader_io_test.cc
lang/glsl/writer/raise/texture_builtins_from_uniform_test.cc
lang/glsl/writer/raise/texture_polyfill_test.cc
diff --git a/src/tint/lang/glsl/writer/raise/BUILD.gn b/src/tint/lang/glsl/writer/raise/BUILD.gn
index 3886a8a..208d910 100644
--- a/src/tint/lang/glsl/writer/raise/BUILD.gn
+++ b/src/tint/lang/glsl/writer/raise/BUILD.gn
@@ -51,6 +51,8 @@
"bitcast_polyfill.h",
"builtin_polyfill.cc",
"builtin_polyfill.h",
+ "offset_first_index.cc",
+ "offset_first_index.h",
"raise.cc",
"raise.h",
"shader_io.cc",
@@ -105,6 +107,7 @@
"binary_polyfill_test.cc",
"bitcast_polyfill_test.cc",
"builtin_polyfill_test.cc",
+ "offset_first_index_test.cc",
"shader_io_test.cc",
"texture_builtins_from_uniform_test.cc",
"texture_polyfill_test.cc",
diff --git a/src/tint/lang/glsl/writer/raise/offset_first_index.cc b/src/tint/lang/glsl/writer/raise/offset_first_index.cc
new file mode 100644
index 0000000..dd431ad
--- /dev/null
+++ b/src/tint/lang/glsl/writer/raise/offset_first_index.cc
@@ -0,0 +1,126 @@
+// 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.
+
+#include "src/tint/lang/glsl/writer/raise/offset_first_index.h"
+
+#include "src/tint/lang/core/fluent_types.h" // IWYU pragma: export
+#include "src/tint/lang/core/ir/builder.h"
+#include "src/tint/lang/core/ir/module.h"
+#include "src/tint/lang/core/ir/validator.h"
+
+namespace tint::glsl::writer::raise {
+namespace {
+
+using namespace tint::core::fluent_types; // NOLINT
+using namespace tint::core::number_suffixes; // NOLINT
+
+/// PIMPL state for the transform.
+struct State {
+ /// The configuration options.
+ const OffsetFirstIndexConfig& config;
+
+ /// The IR module.
+ core::ir::Module& ir;
+
+ /// The IR builder.
+ core::ir::Builder b{ir};
+
+ /// Process the module.
+ void Process() {
+ // Find module-scope `in` variables that have `instance_index` or `vertex_index` builtins.
+ for (auto* global : *ir.root_block) {
+ auto* var = global->As<core::ir::Var>();
+ if (!var) {
+ continue;
+ }
+
+ auto builtin = var->Attributes().builtin;
+ if (builtin == core::BuiltinValue::kInstanceIndex) {
+ if (config.first_instance_offset) {
+ AddOffset(var, *config.first_instance_offset);
+ }
+ }
+ if (builtin == core::BuiltinValue::kVertexIndex) {
+ if (config.first_vertex_offset) {
+ AddOffset(var, *config.first_vertex_offset);
+ }
+ }
+ }
+ }
+
+ /// Add an offset to the value loaded from @p var.
+ /// @param var the variable that contains the builtin value
+ /// @param push_constant_offset the offset in the push constants where the offset is stored
+ void AddOffset(core::ir::Var* var, uint32_t push_constant_offset) {
+ // ShaderIO transforms these input builtins such that they are loaded a single time and then
+ // converted to u32. We add the offset to the result of the conversion.
+ auto* load = GetSingularUse<core::ir::Load>(var);
+ auto* index = GetSingularUse<core::ir::Convert>(load);
+
+ // Replace users of the original load with the result of the offset calculation.
+ auto* offset_index = b.InstructionResult<u32>();
+ index->Result(0)->ReplaceAllUsesWith(offset_index);
+
+ // Load the offset from the push constant structure and add it to the index.
+ b.InsertAfter(index, [&] {
+ auto* push_constants = config.push_constant_layout.var;
+ auto idx = u32(config.push_constant_layout.IndexOf(push_constant_offset));
+ auto* offset = b.Load(b.Access<ptr<push_constant, u32>>(push_constants, idx));
+ b.AddWithResult(offset_index, index, offset);
+ });
+ }
+
+ /// Assert that @p inst has a single use and that it is of type @p T, and return that use.
+ /// @param inst the instruction to get the use for
+ /// @returns the use
+ template <typename T>
+ core::ir::Instruction* GetSingularUse(core::ir::Instruction* inst) {
+ auto& usages = inst->Result(0)->UsagesUnsorted();
+ TINT_ASSERT(usages.Count() == 1);
+ auto* index = usages.begin()->instruction->As<T>();
+ TINT_ASSERT(index);
+ return index;
+ }
+};
+
+} // namespace
+
+Result<SuccessType> OffsetFirstIndex(core::ir::Module& ir, const OffsetFirstIndexConfig& config) {
+ auto result = ValidateAndDumpIfNeeded(ir, "glsl.OffsetFirstIndex transform",
+ core::ir::Capabilities{
+ core::ir::Capability::kAllowHandleVarsWithoutBindings,
+ });
+ if (result != Success) {
+ return result.Failure();
+ }
+
+ State{config, ir}.Process();
+
+ return Success;
+}
+
+} // namespace tint::glsl::writer::raise
diff --git a/src/tint/lang/glsl/writer/raise/offset_first_index.h b/src/tint/lang/glsl/writer/raise/offset_first_index.h
new file mode 100644
index 0000000..d1a8433
--- /dev/null
+++ b/src/tint/lang/glsl/writer/raise/offset_first_index.h
@@ -0,0 +1,63 @@
+// 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_TINT_LANG_GLSL_WRITER_RAISE_OFFSET_FIRST_INDEX_H_
+#define SRC_TINT_LANG_GLSL_WRITER_RAISE_OFFSET_FIRST_INDEX_H_
+
+#include "src/tint/lang/core/ir/transform/prepare_push_constants.h"
+#include "src/tint/utils/result/result.h"
+
+// Forward declarations.
+namespace tint::core::ir {
+class Module;
+} // namespace tint::core::ir
+
+namespace tint::glsl::writer::raise {
+
+/// OffsetFirstIndexConfig describes the configuration options for the OffsetFirstIndex transform.
+struct OffsetFirstIndexConfig {
+ /// Push constant layout information.
+ const core::ir::transform::PushConstantLayout& push_constant_layout;
+
+ /// Offset of the firstVertex push constant.
+ std::optional<uint32_t> first_vertex_offset{};
+
+ /// Offset of the firstInstance push constant.
+ std::optional<uint32_t> first_instance_offset{};
+};
+
+/// OffsetFirstIndex is a transform that adds an offset to the `vertex_index` and `instance_index`
+/// builtin values.
+///
+/// @param module the module to transform
+/// @returns success or failure
+Result<SuccessType> OffsetFirstIndex(core::ir::Module& module,
+ const OffsetFirstIndexConfig& config);
+
+} // namespace tint::glsl::writer::raise
+
+#endif // SRC_TINT_LANG_GLSL_WRITER_RAISE_OFFSET_FIRST_INDEX_H_
diff --git a/src/tint/lang/glsl/writer/raise/offset_first_index_test.cc b/src/tint/lang/glsl/writer/raise/offset_first_index_test.cc
new file mode 100644
index 0000000..f59cb1f
--- /dev/null
+++ b/src/tint/lang/glsl/writer/raise/offset_first_index_test.cc
@@ -0,0 +1,376 @@
+// 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.
+
+#include "src/tint/lang/glsl/writer/raise/offset_first_index.h"
+
+#include "gtest/gtest.h"
+#include "src/tint/lang/core/fluent_types.h"
+#include "src/tint/lang/core/ir/transform/helper_test.h"
+#include "src/tint/lang/core/number.h"
+
+using namespace tint::core::fluent_types; // NOLINT
+using namespace tint::core::number_suffixes; // NOLINT
+
+namespace tint::glsl::writer::raise {
+namespace {
+
+using GlslWriter_OffsetFirstIndexTest = core::ir::transform::TransformTest;
+
+TEST_F(GlslWriter_OffsetFirstIndexTest, NoModify_NoBuiltins) {
+ auto* func = b.Function("foo", ty.vec4<f32>(), core::ir::Function::PipelineStage::kVertex);
+ func->SetReturnBuiltin(core::BuiltinValue::kPosition);
+ b.Append(func->Block(), [&] { //
+ b.Return(func, b.Zero(ty.vec4<f32>()));
+ });
+
+ auto* src = R"(
+%foo = @vertex func():vec4<f32> [@position] {
+ $B1: {
+ ret vec4<f32>(0.0f)
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ core::ir::transform::PreparePushConstantsConfig push_constants_config;
+ auto push_constants = PreparePushConstants(mod, push_constants_config);
+ EXPECT_EQ(push_constants, Success);
+
+ OffsetFirstIndexConfig config{push_constants.Get()};
+ config.first_vertex_offset = 4;
+ config.first_instance_offset = 8;
+ Run(OffsetFirstIndex, config);
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(GlslWriter_OffsetFirstIndexTest, NoModify_BuiltinsWithNoOffsets) {
+ core::IOAttributes attributes;
+
+ auto* vertex_idx = b.Var("vertex_index", ty.ptr(core::AddressSpace::kIn, ty.u32()));
+ attributes.builtin = core::BuiltinValue::kVertexIndex;
+ vertex_idx->SetAttributes(attributes);
+ mod.root_block->Append(vertex_idx);
+
+ auto* instance_idx = b.Var("instance_index", ty.ptr(core::AddressSpace::kIn, ty.u32()));
+ attributes.builtin = core::BuiltinValue::kInstanceIndex;
+ instance_idx->SetAttributes(attributes);
+ mod.root_block->Append(instance_idx);
+
+ auto* func = b.Function("foo", ty.vec4<f32>(), core::ir::Function::PipelineStage::kVertex);
+ func->SetReturnBuiltin(core::BuiltinValue::kPosition);
+
+ b.Append(func->Block(), [&] {
+ auto* vertex = b.Convert<u32>(b.Load(vertex_idx));
+ auto* instance = b.Convert<u32>(b.Load(instance_idx));
+ b.Let("add", b.Add<u32>(vertex, instance));
+ b.Return(func, b.Zero(ty.vec4<f32>()));
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %vertex_index:ptr<__in, u32, read> = var @builtin(vertex_index)
+ %instance_index:ptr<__in, u32, read> = var @builtin(instance_index)
+}
+
+%foo = @vertex func():vec4<f32> [@position] {
+ $B2: {
+ %4:u32 = load %vertex_index
+ %5:u32 = convert %4
+ %6:u32 = load %instance_index
+ %7:u32 = convert %6
+ %8:u32 = add %5, %7
+ %add:u32 = let %8
+ ret vec4<f32>(0.0f)
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ core::ir::transform::PreparePushConstantsConfig push_constants_config;
+ auto push_constants = PreparePushConstants(mod, push_constants_config);
+ EXPECT_EQ(push_constants, Success);
+
+ OffsetFirstIndexConfig config{push_constants.Get()};
+ Run(OffsetFirstIndex, config);
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(GlslWriter_OffsetFirstIndexTest, VertexOffset) {
+ core::IOAttributes attributes;
+
+ auto* vertex_idx = b.Var("vertex_index", ty.ptr(core::AddressSpace::kIn, ty.u32()));
+ attributes.builtin = core::BuiltinValue::kVertexIndex;
+ vertex_idx->SetAttributes(attributes);
+ mod.root_block->Append(vertex_idx);
+
+ auto* instance_idx = b.Var("instance_index", ty.ptr(core::AddressSpace::kIn, ty.u32()));
+ attributes.builtin = core::BuiltinValue::kInstanceIndex;
+ instance_idx->SetAttributes(attributes);
+ mod.root_block->Append(instance_idx);
+
+ auto* func = b.Function("foo", ty.vec4<f32>(), core::ir::Function::PipelineStage::kVertex);
+ func->SetReturnBuiltin(core::BuiltinValue::kPosition);
+
+ b.Append(func->Block(), [&] {
+ auto* vertex = b.Convert<u32>(b.Load(vertex_idx));
+ auto* instance = b.Convert<u32>(b.Load(instance_idx));
+ b.Let("add", b.Add<u32>(vertex, instance));
+ b.Return(func, b.Zero(ty.vec4<f32>()));
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %vertex_index:ptr<__in, u32, read> = var @builtin(vertex_index)
+ %instance_index:ptr<__in, u32, read> = var @builtin(instance_index)
+}
+
+%foo = @vertex func():vec4<f32> [@position] {
+ $B2: {
+ %4:u32 = load %vertex_index
+ %5:u32 = convert %4
+ %6:u32 = load %instance_index
+ %7:u32 = convert %6
+ %8:u32 = add %5, %7
+ %add:u32 = let %8
+ ret vec4<f32>(0.0f)
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+tint_push_constant_struct = struct @align(4), @block {
+ first_vertex:u32 @offset(4)
+}
+
+$B1: { # root
+ %vertex_index:ptr<__in, u32, read> = var @builtin(vertex_index)
+ %instance_index:ptr<__in, u32, read> = var @builtin(instance_index)
+ %tint_push_constants:ptr<push_constant, tint_push_constant_struct, read> = var
+}
+
+%foo = @vertex func():vec4<f32> [@position] {
+ $B2: {
+ %5:u32 = load %vertex_index
+ %6:u32 = convert %5
+ %7:ptr<push_constant, u32, read> = access %tint_push_constants, 0u
+ %8:u32 = load %7
+ %9:u32 = add %6, %8
+ %10:u32 = load %instance_index
+ %11:u32 = convert %10
+ %12:u32 = add %9, %11
+ %add:u32 = let %12
+ ret vec4<f32>(0.0f)
+ }
+}
+)";
+
+ core::ir::transform::PreparePushConstantsConfig push_constants_config;
+ push_constants_config.AddInternalConstant(4, mod.symbols.New("first_vertex"), ty.u32());
+ auto push_constants = PreparePushConstants(mod, push_constants_config);
+ EXPECT_EQ(push_constants, Success);
+
+ OffsetFirstIndexConfig config{push_constants.Get()};
+ config.first_vertex_offset = 4;
+ Run(OffsetFirstIndex, config);
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(GlslWriter_OffsetFirstIndexTest, InstanceOffset) {
+ core::IOAttributes attributes;
+
+ auto* vertex_idx = b.Var("vertex_index", ty.ptr(core::AddressSpace::kIn, ty.u32()));
+ attributes.builtin = core::BuiltinValue::kVertexIndex;
+ vertex_idx->SetAttributes(attributes);
+ mod.root_block->Append(vertex_idx);
+
+ auto* instance_idx = b.Var("instance_index", ty.ptr(core::AddressSpace::kIn, ty.u32()));
+ attributes.builtin = core::BuiltinValue::kInstanceIndex;
+ instance_idx->SetAttributes(attributes);
+ mod.root_block->Append(instance_idx);
+
+ auto* func = b.Function("foo", ty.vec4<f32>(), core::ir::Function::PipelineStage::kVertex);
+ func->SetReturnBuiltin(core::BuiltinValue::kPosition);
+
+ b.Append(func->Block(), [&] {
+ auto* vertex = b.Convert<u32>(b.Load(vertex_idx));
+ auto* instance = b.Convert<u32>(b.Load(instance_idx));
+ b.Let("add", b.Add<u32>(vertex, instance));
+ b.Return(func, b.Zero(ty.vec4<f32>()));
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %vertex_index:ptr<__in, u32, read> = var @builtin(vertex_index)
+ %instance_index:ptr<__in, u32, read> = var @builtin(instance_index)
+}
+
+%foo = @vertex func():vec4<f32> [@position] {
+ $B2: {
+ %4:u32 = load %vertex_index
+ %5:u32 = convert %4
+ %6:u32 = load %instance_index
+ %7:u32 = convert %6
+ %8:u32 = add %5, %7
+ %add:u32 = let %8
+ ret vec4<f32>(0.0f)
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+tint_push_constant_struct = struct @align(4), @block {
+ first_instance:u32 @offset(4)
+}
+
+$B1: { # root
+ %vertex_index:ptr<__in, u32, read> = var @builtin(vertex_index)
+ %instance_index:ptr<__in, u32, read> = var @builtin(instance_index)
+ %tint_push_constants:ptr<push_constant, tint_push_constant_struct, read> = var
+}
+
+%foo = @vertex func():vec4<f32> [@position] {
+ $B2: {
+ %5:u32 = load %vertex_index
+ %6:u32 = convert %5
+ %7:u32 = load %instance_index
+ %8:u32 = convert %7
+ %9:ptr<push_constant, u32, read> = access %tint_push_constants, 0u
+ %10:u32 = load %9
+ %11:u32 = add %8, %10
+ %12:u32 = add %6, %11
+ %add:u32 = let %12
+ ret vec4<f32>(0.0f)
+ }
+}
+)";
+
+ core::ir::transform::PreparePushConstantsConfig push_constants_config;
+ push_constants_config.AddInternalConstant(4, mod.symbols.New("first_instance"), ty.u32());
+ auto push_constants = PreparePushConstants(mod, push_constants_config);
+ EXPECT_EQ(push_constants, Success);
+
+ OffsetFirstIndexConfig config{push_constants.Get()};
+ config.first_instance_offset = 4;
+ Run(OffsetFirstIndex, config);
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(GlslWriter_OffsetFirstIndexTest, VertexAndInstanceOffset) {
+ core::IOAttributes attributes;
+
+ auto* vertex_idx = b.Var("vertex_index", ty.ptr(core::AddressSpace::kIn, ty.u32()));
+ attributes.builtin = core::BuiltinValue::kVertexIndex;
+ vertex_idx->SetAttributes(attributes);
+ mod.root_block->Append(vertex_idx);
+
+ auto* instance_idx = b.Var("instance_index", ty.ptr(core::AddressSpace::kIn, ty.u32()));
+ attributes.builtin = core::BuiltinValue::kInstanceIndex;
+ instance_idx->SetAttributes(attributes);
+ mod.root_block->Append(instance_idx);
+
+ auto* func = b.Function("foo", ty.vec4<f32>(), core::ir::Function::PipelineStage::kVertex);
+ func->SetReturnBuiltin(core::BuiltinValue::kPosition);
+
+ b.Append(func->Block(), [&] {
+ auto* vertex = b.Convert<u32>(b.Load(vertex_idx));
+ auto* instance = b.Convert<u32>(b.Load(instance_idx));
+ b.Let("add", b.Add<u32>(vertex, instance));
+ b.Return(func, b.Zero(ty.vec4<f32>()));
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %vertex_index:ptr<__in, u32, read> = var @builtin(vertex_index)
+ %instance_index:ptr<__in, u32, read> = var @builtin(instance_index)
+}
+
+%foo = @vertex func():vec4<f32> [@position] {
+ $B2: {
+ %4:u32 = load %vertex_index
+ %5:u32 = convert %4
+ %6:u32 = load %instance_index
+ %7:u32 = convert %6
+ %8:u32 = add %5, %7
+ %add:u32 = let %8
+ ret vec4<f32>(0.0f)
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+tint_push_constant_struct = struct @align(4), @block {
+ first_vertex:u32 @offset(4)
+ first_instance:u32 @offset(8)
+}
+
+$B1: { # root
+ %vertex_index:ptr<__in, u32, read> = var @builtin(vertex_index)
+ %instance_index:ptr<__in, u32, read> = var @builtin(instance_index)
+ %tint_push_constants:ptr<push_constant, tint_push_constant_struct, read> = var
+}
+
+%foo = @vertex func():vec4<f32> [@position] {
+ $B2: {
+ %5:u32 = load %vertex_index
+ %6:u32 = convert %5
+ %7:ptr<push_constant, u32, read> = access %tint_push_constants, 0u
+ %8:u32 = load %7
+ %9:u32 = add %6, %8
+ %10:u32 = load %instance_index
+ %11:u32 = convert %10
+ %12:ptr<push_constant, u32, read> = access %tint_push_constants, 1u
+ %13:u32 = load %12
+ %14:u32 = add %11, %13
+ %15:u32 = add %9, %14
+ %add:u32 = let %15
+ ret vec4<f32>(0.0f)
+ }
+}
+)";
+
+ core::ir::transform::PreparePushConstantsConfig push_constants_config;
+ push_constants_config.AddInternalConstant(4, mod.symbols.New("first_vertex"), ty.u32());
+ push_constants_config.AddInternalConstant(8, mod.symbols.New("first_instance"), ty.u32());
+ auto push_constants = PreparePushConstants(mod, push_constants_config);
+ EXPECT_EQ(push_constants, Success);
+
+ OffsetFirstIndexConfig config{push_constants.Get()};
+ config.first_vertex_offset = 4;
+ config.first_instance_offset = 8;
+ Run(OffsetFirstIndex, config);
+ EXPECT_EQ(expect, str());
+}
+
+} // namespace
+} // namespace tint::glsl::writer::raise
diff --git a/src/tint/lang/glsl/writer/raise/raise.cc b/src/tint/lang/glsl/writer/raise/raise.cc
index b2e66e8..cf69fde 100644
--- a/src/tint/lang/glsl/writer/raise/raise.cc
+++ b/src/tint/lang/glsl/writer/raise/raise.cc
@@ -55,6 +55,7 @@
#include "src/tint/lang/glsl/writer/raise/binary_polyfill.h"
#include "src/tint/lang/glsl/writer/raise/bitcast_polyfill.h"
#include "src/tint/lang/glsl/writer/raise/builtin_polyfill.h"
+#include "src/tint/lang/glsl/writer/raise/offset_first_index.h"
#include "src/tint/lang/glsl/writer/raise/shader_io.h"
#include "src/tint/lang/glsl/writer/raise/texture_builtins_from_uniform.h"
#include "src/tint/lang/glsl/writer/raise/texture_polyfill.h"
@@ -149,7 +150,6 @@
RUN_TRANSFORM(core::ir::transform::BlockDecoratedStructs, module);
- // TODO(dsinclair): OffsetFirstIndex
// TODO(dsinclair): CombineSamplers
// TODO(dsinclair): PadStructs
@@ -191,6 +191,12 @@
RUN_TRANSFORM(raise::ShaderIO, module,
raise::ShaderIOConfig{push_constant_layout.Get(), options.depth_range_offsets});
+ // Must come after ShaderIO as it operates on module-scope `in` variables.
+ RUN_TRANSFORM(
+ raise::OffsetFirstIndex, module,
+ raise::OffsetFirstIndexConfig{push_constant_layout.Get(), options.first_vertex_offset,
+ options.first_instance_offset});
+
RUN_TRANSFORM(core::ir::transform::Std140, module);
// These transforms need to be run last as various transforms introduce terminator arguments,
diff --git a/test/tint/bug/chromium/1251009.wgsl.expected.ir.glsl b/test/tint/bug/chromium/1251009.wgsl.expected.ir.glsl
index bd82bea..afbbb22 100644
--- a/test/tint/bug/chromium/1251009.wgsl.expected.ir.glsl
+++ b/test/tint/bug/chromium/1251009.wgsl.expected.ir.glsl
@@ -29,7 +29,8 @@
VertexInputs0 v_1 = VertexInputs0(v, tint_symbol_loc0_Input);
uint v_2 = tint_symbol_loc1_Input;
uint v_3 = uint(gl_InstanceID);
- gl_Position = tint_symbol_inner(v_1, v_2, v_3, VertexInputs1(tint_symbol_loc2_Input, tint_symbol_loc3_Input));
+ uint v_4 = (v_3 + tint_push_constants.tint_first_instance);
+ gl_Position = tint_symbol_inner(v_1, v_2, v_4, VertexInputs1(tint_symbol_loc2_Input, tint_symbol_loc3_Input));
gl_Position[1u] = -(gl_Position.y);
gl_Position[2u] = ((2.0f * gl_Position.z) - gl_Position.w);
gl_PointSize = 1.0f;
diff --git a/test/tint/bug/tint/824.wgsl.expected.ir.glsl b/test/tint/bug/tint/824.wgsl.expected.ir.glsl
index 998732c..d63adac 100644
--- a/test/tint/bug/tint/824.wgsl.expected.ir.glsl
+++ b/test/tint/bug/tint/824.wgsl.expected.ir.glsl
@@ -23,10 +23,11 @@
}
void main() {
uint v = uint(gl_VertexID);
- Output v_1 = tint_symbol_inner(v, uint(gl_InstanceID));
- gl_Position = v_1.Position;
+ uint v_1 = uint(gl_InstanceID);
+ Output v_2 = tint_symbol_inner(v, (v_1 + tint_push_constants.tint_first_instance));
+ gl_Position = v_2.Position;
gl_Position[1u] = -(gl_Position.y);
gl_Position[2u] = ((2.0f * gl_Position.z) - gl_Position.w);
- tint_symbol_loc0_Output = v_1.color;
+ tint_symbol_loc0_Output = v_2.color;
gl_PointSize = 1.0f;
}
diff --git a/test/tint/types/functions/shader_io/vertex_input_builtins.wgsl.expected.ir.glsl b/test/tint/types/functions/shader_io/vertex_input_builtins.wgsl.expected.ir.glsl
index 69964dc..9e343bf 100644
--- a/test/tint/types/functions/shader_io/vertex_input_builtins.wgsl.expected.ir.glsl
+++ b/test/tint/types/functions/shader_io/vertex_input_builtins.wgsl.expected.ir.glsl
@@ -12,7 +12,8 @@
}
void main() {
uint v = uint(gl_VertexID);
- gl_Position = tint_symbol_inner(v, uint(gl_InstanceID));
+ uint v_1 = uint(gl_InstanceID);
+ gl_Position = tint_symbol_inner(v, (v_1 + tint_push_constants.tint_first_instance));
gl_Position[1u] = -(gl_Position.y);
gl_Position[2u] = ((2.0f * gl_Position.z) - gl_Position.w);
gl_PointSize = 1.0f;
diff --git a/test/tint/types/functions/shader_io/vertex_input_builtins_struct.wgsl.expected.ir.glsl b/test/tint/types/functions/shader_io/vertex_input_builtins_struct.wgsl.expected.ir.glsl
index 11b1fee..586cbfe 100644
--- a/test/tint/types/functions/shader_io/vertex_input_builtins_struct.wgsl.expected.ir.glsl
+++ b/test/tint/types/functions/shader_io/vertex_input_builtins_struct.wgsl.expected.ir.glsl
@@ -17,7 +17,8 @@
}
void main() {
uint v = uint(gl_VertexID);
- gl_Position = tint_symbol_inner(VertexInputs(v, uint(gl_InstanceID)));
+ uint v_1 = uint(gl_InstanceID);
+ gl_Position = tint_symbol_inner(VertexInputs(v, (v_1 + tint_push_constants.tint_first_instance)));
gl_Position[1u] = -(gl_Position.y);
gl_Position[2u] = ((2.0f * gl_Position.z) - gl_Position.w);
gl_PointSize = 1.0f;
diff --git a/test/tint/types/functions/shader_io/vertex_input_mixed.wgsl.expected.ir.glsl b/test/tint/types/functions/shader_io/vertex_input_mixed.wgsl.expected.ir.glsl
index 19fc8ed..ed3116c 100644
--- a/test/tint/types/functions/shader_io/vertex_input_mixed.wgsl.expected.ir.glsl
+++ b/test/tint/types/functions/shader_io/vertex_input_mixed.wgsl.expected.ir.glsl
@@ -33,7 +33,8 @@
VertexInputs0 v_2 = VertexInputs0(v_1, tint_symbol_loc0_Input);
uint v_3 = tint_symbol_loc1_Input;
uint v_4 = uint(gl_InstanceID);
- gl_Position = tint_symbol_inner(v_2, v_3, v_4, VertexInputs1(tint_symbol_loc2_Input, tint_symbol_loc3_Input));
+ uint v_5 = (v_4 + tint_push_constants.tint_first_instance);
+ gl_Position = tint_symbol_inner(v_2, v_3, v_5, VertexInputs1(tint_symbol_loc2_Input, tint_symbol_loc3_Input));
gl_Position[1u] = -(gl_Position.y);
gl_Position[2u] = ((2.0f * gl_Position.z) - gl_Position.w);
gl_PointSize = 1.0f;
diff --git a/test/tint/types/functions/shader_io/vertex_input_mixed_f16.wgsl.expected.ir.glsl b/test/tint/types/functions/shader_io/vertex_input_mixed_f16.wgsl.expected.ir.glsl
index c66c061..a082a5d 100644
--- a/test/tint/types/functions/shader_io/vertex_input_mixed_f16.wgsl.expected.ir.glsl
+++ b/test/tint/types/functions/shader_io/vertex_input_mixed_f16.wgsl.expected.ir.glsl
@@ -39,8 +39,9 @@
VertexInputs0 v_2 = VertexInputs0(v_1, tint_symbol_loc0_Input);
uint v_3 = tint_symbol_loc1_Input;
uint v_4 = uint(gl_InstanceID);
- VertexInputs1 v_5 = VertexInputs1(tint_symbol_loc2_Input, tint_symbol_loc3_Input, tint_symbol_loc5_Input);
- gl_Position = tint_symbol_inner(v_2, v_3, v_4, v_5, tint_symbol_loc4_Input);
+ uint v_5 = (v_4 + tint_push_constants.tint_first_instance);
+ VertexInputs1 v_6 = VertexInputs1(tint_symbol_loc2_Input, tint_symbol_loc3_Input, tint_symbol_loc5_Input);
+ gl_Position = tint_symbol_inner(v_2, v_3, v_5, v_6, tint_symbol_loc4_Input);
gl_Position[1u] = -(gl_Position.y);
gl_Position[2u] = ((2.0f * gl_Position.z) - gl_Position.w);
gl_PointSize = 1.0f;
diff --git a/test/tint/var/uses/instance_index.wgsl.expected.ir.glsl b/test/tint/var/uses/instance_index.wgsl.expected.ir.glsl
index fe37517..d0ce286 100644
--- a/test/tint/var/uses/instance_index.wgsl.expected.ir.glsl
+++ b/test/tint/var/uses/instance_index.wgsl.expected.ir.glsl
@@ -10,7 +10,8 @@
return vec4(float(b));
}
void main() {
- gl_Position = tint_symbol_inner(uint(gl_InstanceID));
+ uint v = uint(gl_InstanceID);
+ gl_Position = tint_symbol_inner((v + tint_push_constants.tint_first_instance));
gl_Position[1u] = -(gl_Position.y);
gl_Position[2u] = ((2.0f * gl_Position.z) - gl_Position.w);
gl_PointSize = 1.0f;
diff --git a/test/tint/var/uses/push_constant_and_instance_index.wgsl.expected.ir.glsl b/test/tint/var/uses/push_constant_and_instance_index.wgsl.expected.ir.glsl
index e91048e..67dd105 100644
--- a/test/tint/var/uses/push_constant_and_instance_index.wgsl.expected.ir.glsl
+++ b/test/tint/var/uses/push_constant_and_instance_index.wgsl.expected.ir.glsl
@@ -12,7 +12,8 @@
return vec4((v + float(b)));
}
void main() {
- gl_Position = tint_symbol_inner(uint(gl_InstanceID));
+ uint v_1 = uint(gl_InstanceID);
+ gl_Position = tint_symbol_inner((v_1 + tint_push_constants.tint_first_instance));
gl_Position[1u] = -(gl_Position.y);
gl_Position[2u] = ((2.0f * gl_Position.z) - gl_Position.w);
gl_PointSize = 1.0f;