[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;