Implement push_constant-based firstIndex transform. This transform offsets the vertex_index and/or instance_index builtin variables by a user-supplied first_vertex and first_instance offset. This is required for shading languages that use zero-based first_* variables such as GLSL and HLSL. Also, GLSL requires push constants to be wrapped in a struct, and the struct to be emitted, but not emitted as an interface block. Thus, change the "skip_push_constants" flag in the AddBlockAttribute to "push_constants_wrap_only", which causes push constants to be wrapped in a struct, but to not have the BlockAttribute added. Bug: tint:2140 Change-Id: I7ce7bb67b58efa110e91fc38d840e674adaf9d0b Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/169461 Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Stephen White <senorblanco@chromium.org> Reviewed-by: Austin Eng <enga@chromium.org> Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/dawn/native/RenderPipeline.cpp b/src/dawn/native/RenderPipeline.cpp index 666f6bc..eaecd18 100644 --- a/src/dawn/native/RenderPipeline.cpp +++ b/src/dawn/native/RenderPipeline.cpp
@@ -974,6 +974,11 @@ mUsesFragDepth = GetStage(SingleShaderStage::Fragment).metadata->usesFragDepth; } + if (HasStage(SingleShaderStage::Vertex)) { + mUsesVertexIndex = GetStage(SingleShaderStage::Vertex).metadata->usesVertexIndex; + mUsesInstanceIndex = GetStage(SingleShaderStage::Vertex).metadata->usesInstanceIndex; + } + SetContentHash(ComputeContentHash()); GetObjectTrackingList()->Track(this); @@ -1168,6 +1173,16 @@ return mUsesFragDepth; } +bool RenderPipelineBase::UsesVertexIndex() const { + DAWN_ASSERT(!IsError()); + return mUsesVertexIndex; +} + +bool RenderPipelineBase::UsesInstanceIndex() const { + DAWN_ASSERT(!IsError()); + return mUsesInstanceIndex; +} + size_t RenderPipelineBase::ComputeContentHash() { ObjectContentHasher recorder;
diff --git a/src/dawn/native/RenderPipeline.h b/src/dawn/native/RenderPipeline.h index ee16c51..987eae5 100644 --- a/src/dawn/native/RenderPipeline.h +++ b/src/dawn/native/RenderPipeline.h
@@ -128,6 +128,8 @@ bool WritesDepth() const; bool WritesStencil() const; bool UsesFragDepth() const; + bool UsesVertexIndex() const; + bool UsesInstanceIndex() const; const AttachmentState* GetAttachmentState() const; @@ -168,6 +170,8 @@ bool mWritesDepth = false; bool mWritesStencil = false; bool mUsesFragDepth = false; + bool mUsesVertexIndex = false; + bool mUsesInstanceIndex = false; }; } // namespace dawn::native
diff --git a/src/dawn/native/opengl/CommandBufferGL.cpp b/src/dawn/native/opengl/CommandBufferGL.cpp index 745e5c2..578c13b 100644 --- a/src/dawn/native/opengl/CommandBufferGL.cpp +++ b/src/dawn/native/opengl/CommandBufferGL.cpp
@@ -1123,6 +1123,10 @@ vertexStateBufferBindingTracker.Apply(gl); bindGroupTracker.Apply(gl); + if (lastPipeline->UsesInstanceIndex()) { + gl.Uniform1ui(PipelineLayout::PushConstantLocation::FirstInstance, + draw->firstInstance); + } if (gl.DrawArraysInstancedBaseInstanceANGLE) { gl.DrawArraysInstancedBaseInstanceANGLE( lastPipeline->GetGLPrimitiveTopology(), draw->firstVertex, @@ -1145,6 +1149,10 @@ vertexStateBufferBindingTracker.Apply(gl); bindGroupTracker.Apply(gl); + if (lastPipeline->UsesInstanceIndex()) { + gl.Uniform1ui(PipelineLayout::PushConstantLocation::FirstInstance, + draw->firstInstance); + } if (gl.DrawElementsInstancedBaseVertexBaseInstanceANGLE) { gl.DrawElementsInstancedBaseVertexBaseInstanceANGLE( lastPipeline->GetGLPrimitiveTopology(), draw->indexCount, indexBufferFormat, @@ -1181,6 +1189,9 @@ case Command::DrawIndirect: { DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>(); + if (lastPipeline->UsesInstanceIndex()) { + gl.Uniform1ui(PipelineLayout::PushConstantLocation::FirstInstance, 0); + } vertexStateBufferBindingTracker.Apply(gl); bindGroupTracker.Apply(gl); @@ -1198,6 +1209,9 @@ case Command::DrawIndexedIndirect: { DrawIndexedIndirectCmd* draw = iter->NextCommand<DrawIndexedIndirectCmd>(); + if (lastPipeline->UsesInstanceIndex()) { + gl.Uniform1ui(PipelineLayout::PushConstantLocation::FirstInstance, 0); + } vertexStateBufferBindingTracker.Apply(gl); bindGroupTracker.Apply(gl);
diff --git a/src/dawn/native/opengl/PipelineLayoutGL.h b/src/dawn/native/opengl/PipelineLayoutGL.h index eb4863b..e2ab802 100644 --- a/src/dawn/native/opengl/PipelineLayoutGL.h +++ b/src/dawn/native/opengl/PipelineLayoutGL.h
@@ -55,6 +55,10 @@ GLuint GetInternalUniformBinding() const; + enum PushConstantLocation { + FirstInstance = 0, + }; + private: ~PipelineLayout() override = default; BindingIndexInfo mIndexInfo;
diff --git a/src/dawn/native/opengl/ShaderModuleGL.cpp b/src/dawn/native/opengl/ShaderModuleGL.cpp index 0ce3792..821a350 100644 --- a/src/dawn/native/opengl/ShaderModuleGL.cpp +++ b/src/dawn/native/opengl/ShaderModuleGL.cpp
@@ -418,6 +418,9 @@ /* fullSubgroups */ {})); } + r.tintOptions.first_instance_offset = + 4 * PipelineLayout::PushConstantLocation::FirstInstance; + 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());
diff --git a/src/dawn/tests/end2end/DepthStencilCopyTests.cpp b/src/dawn/tests/end2end/DepthStencilCopyTests.cpp index 162defa..dd0ca02 100644 --- a/src/dawn/tests/end2end/DepthStencilCopyTests.cpp +++ b/src/dawn/tests/end2end/DepthStencilCopyTests.cpp
@@ -1024,8 +1024,9 @@ // glDrawArraysInstancedBaseInstance. DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES() && !IsANGLE()); - // TODO(crbug.com/dawn/2202): Failing on ANGLE/D3D11 for unknown reasons. - DAWN_TEST_UNSUPPORTED_IF(IsANGLED3D11()); + // TODO(crbug.com/dawn/2202): Failing on ANGLE/SwiftShader due to undiagnosed ANGLE bug. + // Passes on ANGLE/D3D11. + DAWN_TEST_UNSUPPORTED_IF(IsANGLESwiftShader()); // Create a stencil texture constexpr uint32_t kWidth = 4;
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 f1831e9..1a66806 100644 --- a/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc +++ b/src/tint/lang/glsl/writer/ast_printer/ast_printer.cc
@@ -67,6 +67,7 @@ #include "src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h" #include "src/tint/lang/wgsl/ast/transform/manager.h" #include "src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h" +#include "src/tint/lang/wgsl/ast/transform/offset_first_index.h" #include "src/tint/lang/wgsl/ast/transform/preserve_padding.h" #include "src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h" #include "src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h" @@ -152,8 +153,6 @@ data.Add<ast::transform::SingleEntryPoint::Config>(entry_point); } - data.Add<ast::transform::AddBlockAttribute::Config>(true); - manager.Add<ast::transform::PreservePadding>(); // Must come before DirectVariableAccess manager.Add<ast::transform::Unshadow>(); // Must come before DirectVariableAccess @@ -203,6 +202,8 @@ manager.Add<ast::transform::ZeroInitWorkgroupMemory>(); } + manager.Add<ast::transform::OffsetFirstIndex>(); + // CanonicalizeEntryPointIO must come after Robustness manager.Add<ast::transform::CanonicalizeEntryPointIO>(); @@ -245,6 +246,8 @@ data.Add<ast::transform::CanonicalizeEntryPointIO::Config>( ast::transform::CanonicalizeEntryPointIO::ShaderStyle::kGlsl); + data.Add<ast::transform::OffsetFirstIndex::Config>(std::nullopt, options.first_instance_offset); + SanitizedResult result; ast::transform::DataMap outputs; result.program = manager.Run(in, data, outputs); @@ -297,7 +300,8 @@ } bool is_block = ast::HasAttribute<ast::transform::AddBlockAttribute::BlockAttribute>( - str->attributes); + str->attributes) && + !sem->UsedAs(core::AddressSpace::kPushConstant); if (!has_rt_arr && !is_block) { EmitStructType(current_buffer_, sem); } @@ -2093,6 +2097,7 @@ auto name = decl->name->symbol.Name(); auto* type = var->Type()->UnwrapRef(); + out << "layout(location=0) "; EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name); out << ";"; }
diff --git a/src/tint/lang/glsl/writer/common/options.h b/src/tint/lang/glsl/writer/common/options.h index c97026e..1f5298f 100644 --- a/src/tint/lang/glsl/writer/common/options.h +++ b/src/tint/lang/glsl/writer/common/options.h
@@ -28,6 +28,7 @@ #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> @@ -78,6 +79,9 @@ /// Options used in the binding mappings for external textures ExternalTextureOptions external_texture_options = {}; + /// Offset of the firstInstance push constant. + std::optional<int32_t> first_instance_offset; + /// Options used to map WGSL textureNumLevels/textureNumSamples builtins to internal uniform /// buffer values. If not specified, emits corresponding GLSL builtins /// textureQueryLevels/textureSamples directly.
diff --git a/src/tint/lang/wgsl/ast/transform/BUILD.bazel b/src/tint/lang/wgsl/ast/transform/BUILD.bazel index 4b1340a..6229a94 100644 --- a/src/tint/lang/wgsl/ast/transform/BUILD.bazel +++ b/src/tint/lang/wgsl/ast/transform/BUILD.bazel
@@ -56,6 +56,7 @@ "hoist_to_decl_before.cc", "manager.cc", "multiplanar_external_texture.cc", + "offset_first_index.cc", "preserve_padding.cc", "promote_initializers_to_let.cc", "promote_side_effects_to_decl.cc", @@ -91,6 +92,7 @@ "hoist_to_decl_before.h", "manager.h", "multiplanar_external_texture.h", + "offset_first_index.h", "preserve_padding.h", "promote_initializers_to_let.h", "promote_side_effects_to_decl.h", @@ -159,6 +161,7 @@ "hoist_to_decl_before_test.cc", "manager_test.cc", "multiplanar_external_texture_test.cc", + "offset_first_index_test.cc", "preserve_padding_test.cc", "promote_initializers_to_let_test.cc", "promote_side_effects_to_decl_test.cc",
diff --git a/src/tint/lang/wgsl/ast/transform/BUILD.cmake b/src/tint/lang/wgsl/ast/transform/BUILD.cmake index 11ddfaa..068369c 100644 --- a/src/tint/lang/wgsl/ast/transform/BUILD.cmake +++ b/src/tint/lang/wgsl/ast/transform/BUILD.cmake
@@ -73,6 +73,8 @@ lang/wgsl/ast/transform/manager.h lang/wgsl/ast/transform/multiplanar_external_texture.cc lang/wgsl/ast/transform/multiplanar_external_texture.h + lang/wgsl/ast/transform/offset_first_index.cc + lang/wgsl/ast/transform/offset_first_index.h lang/wgsl/ast/transform/preserve_padding.cc lang/wgsl/ast/transform/preserve_padding.h lang/wgsl/ast/transform/promote_initializers_to_let.cc @@ -159,6 +161,7 @@ lang/wgsl/ast/transform/hoist_to_decl_before_test.cc lang/wgsl/ast/transform/manager_test.cc lang/wgsl/ast/transform/multiplanar_external_texture_test.cc + lang/wgsl/ast/transform/offset_first_index_test.cc lang/wgsl/ast/transform/preserve_padding_test.cc lang/wgsl/ast/transform/promote_initializers_to_let_test.cc lang/wgsl/ast/transform/promote_side_effects_to_decl_test.cc
diff --git a/src/tint/lang/wgsl/ast/transform/BUILD.gn b/src/tint/lang/wgsl/ast/transform/BUILD.gn index 4632240..b94227e 100644 --- a/src/tint/lang/wgsl/ast/transform/BUILD.gn +++ b/src/tint/lang/wgsl/ast/transform/BUILD.gn
@@ -78,6 +78,8 @@ "manager.h", "multiplanar_external_texture.cc", "multiplanar_external_texture.h", + "offset_first_index.cc", + "offset_first_index.h", "preserve_padding.cc", "preserve_padding.h", "promote_initializers_to_let.cc", @@ -160,6 +162,7 @@ "hoist_to_decl_before_test.cc", "manager_test.cc", "multiplanar_external_texture_test.cc", + "offset_first_index_test.cc", "preserve_padding_test.cc", "promote_initializers_to_let_test.cc", "promote_side_effects_to_decl_test.cc",
diff --git a/src/tint/lang/wgsl/ast/transform/add_block_attribute.cc b/src/tint/lang/wgsl/ast/transform/add_block_attribute.cc index 607bf54..82e0716 100644 --- a/src/tint/lang/wgsl/ast/transform/add_block_attribute.cc +++ b/src/tint/lang/wgsl/ast/transform/add_block_attribute.cc
@@ -39,7 +39,6 @@ TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::AddBlockAttribute); TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::AddBlockAttribute::BlockAttribute); -TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::AddBlockAttribute::Config); namespace tint::ast::transform { @@ -48,13 +47,12 @@ AddBlockAttribute::~AddBlockAttribute() = default; Transform::ApplyResult AddBlockAttribute::Apply(const Program& src, - const DataMap& inputs, + const DataMap&, DataMap&) const { ProgramBuilder b; program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true}; auto& sem = src.Sem(); - auto* cfg = inputs.Get<Config>(); // A map from a type in the source program to a block-decorated wrapper that contains it in the // destination program. @@ -82,11 +80,6 @@ bool needs_wrapping = !str || // Type is not a structure str->HasFixedFootprint(); // Struct has a fixed footprint - if (cfg && cfg->skip_push_constants && - var->AddressSpace() == core::AddressSpace::kPushConstant) { - continue; - } - if (needs_wrapping) { const char* kMemberName = "inner"; @@ -136,8 +129,4 @@ ctx.dst->AllocateNodeID()); } -AddBlockAttribute::Config::Config(bool skip_push_consts) : skip_push_constants(skip_push_consts) {} - -AddBlockAttribute::Config::~Config() = default; - } // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/add_block_attribute.h b/src/tint/lang/wgsl/ast/transform/add_block_attribute.h index 61a884d..544e1ee 100644 --- a/src/tint/lang/wgsl/ast/transform/add_block_attribute.h +++ b/src/tint/lang/wgsl/ast/transform/add_block_attribute.h
@@ -66,19 +66,6 @@ /// Destructor ~AddBlockAttribute() override; - /// Transform configuration options - struct Config final : public Castable<Config, ast::transform::Data> { - /// Constructor - /// @param skip_push_const whether to skip push constants - explicit Config(bool skip_push_const); - - /// Destructor - ~Config() override; - - /// Whether to skip push constants - bool skip_push_constants; - }; - /// @copydoc Transform::Apply ApplyResult Apply(const Program& program, const DataMap& inputs,
diff --git a/src/tint/lang/wgsl/ast/transform/offset_first_index.cc b/src/tint/lang/wgsl/ast/transform/offset_first_index.cc new file mode 100644 index 0000000..eb64f60 --- /dev/null +++ b/src/tint/lang/wgsl/ast/transform/offset_first_index.cc
@@ -0,0 +1,201 @@ +// 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/wgsl/ast/transform/offset_first_index.h" + +#include <memory> +#include <unordered_map> +#include <utility> + +#include "src/tint/lang/core/builtin_value.h" +#include "src/tint/lang/core/fluent_types.h" +#include "src/tint/lang/wgsl/program/clone_context.h" +#include "src/tint/lang/wgsl/program/program_builder.h" +#include "src/tint/lang/wgsl/resolver/resolve.h" +#include "src/tint/lang/wgsl/sem/function.h" +#include "src/tint/lang/wgsl/sem/member_accessor_expression.h" +#include "src/tint/lang/wgsl/sem/struct.h" +#include "src/tint/lang/wgsl/sem/variable.h" + +using namespace tint::core::fluent_types; // NOLINT + +TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::OffsetFirstIndex); +TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::OffsetFirstIndex::Config); + +namespace tint::ast::transform { +namespace { + +// Push constant names +constexpr char kFirstVertexName[] = "first_vertex"; +constexpr char kFirstInstanceName[] = "first_instance"; + +bool ShouldRun(const Program& program) { + for (auto* fn : program.AST().Functions()) { + if (fn->PipelineStage() == PipelineStage::kVertex) { + return true; + } + } + return false; +} + +} // namespace + +OffsetFirstIndex::OffsetFirstIndex() = default; +OffsetFirstIndex::~OffsetFirstIndex() = default; + +Transform::ApplyResult OffsetFirstIndex::Apply(const Program& src, + const DataMap& inputs, + DataMap&) const { + if (!ShouldRun(src)) { + return SkipTransform; + } + + const Config* cfg = inputs.Get<Config>(); + if (!cfg) { + return SkipTransform; + } + + ProgramBuilder b; + program::CloneContext ctx{&b, &src, /* auto_clone_symbols */ true}; + + // Map of builtin usages + std::unordered_map<const sem::Variable*, const char*> builtin_vars; + std::unordered_map<const core::type::StructMember*, const char*> builtin_members; + + bool has_vertex_index = false; + bool has_instance_index = false; + + // Traverse the AST scanning for builtin accesses via variables (includes + // parameters) or structure member accesses. + for (auto* node : ctx.src->ASTNodes().Objects()) { + if (auto* var = node->As<Variable>()) { + for (auto* attr : var->attributes) { + if (auto* builtin_attr = attr->As<BuiltinAttribute>()) { + core::BuiltinValue builtin = src.Sem().Get(builtin_attr)->Value(); + if (builtin == core::BuiltinValue::kVertexIndex && cfg && + cfg->first_vertex_offset.has_value()) { + auto* sem_var = ctx.src->Sem().Get(var); + builtin_vars.emplace(sem_var, kFirstVertexName); + has_vertex_index = true; + } + if (builtin == core::BuiltinValue::kInstanceIndex && cfg && + cfg->first_instance_offset.has_value()) { + auto* sem_var = ctx.src->Sem().Get(var); + builtin_vars.emplace(sem_var, kFirstInstanceName); + has_instance_index = true; + } + } + } + } + if (auto* member = node->As<StructMember>()) { + for (auto* attr : member->attributes) { + if (auto* builtin_attr = attr->As<BuiltinAttribute>()) { + core::BuiltinValue builtin = src.Sem().Get(builtin_attr)->Value(); + if (builtin == core::BuiltinValue::kVertexIndex && cfg && + cfg->first_vertex_offset.has_value()) { + auto* sem_mem = ctx.src->Sem().Get(member); + builtin_members.emplace(sem_mem, kFirstVertexName); + has_vertex_index = true; + } + if (builtin == core::BuiltinValue::kInstanceIndex && cfg && + cfg->first_instance_offset.has_value()) { + auto* sem_mem = ctx.src->Sem().Get(member); + builtin_members.emplace(sem_mem, kFirstInstanceName); + has_instance_index = true; + } + } + } + } + } + + if (!has_vertex_index && !has_instance_index) { + return SkipTransform; + } + + // Abort on any use of push constants in the module. + for (auto* global : src.AST().GlobalVariables()) { + if (auto* var = global->As<ast::Var>()) { + auto* v = src.Sem().Get(var); + if (TINT_UNLIKELY(v->AddressSpace() == core::AddressSpace::kPushConstant)) { + TINT_ICE() + << "OffsetFirstIndex doesn't know how to handle module that already use push " + "constants (yet)"; + return resolver::Resolve(b); + } + } + } + + b.Enable(wgsl::Extension::kChromiumExperimentalPushConstant); + + // Add push constant members and calculate byte offsets + tint::Vector<const StructMember*, 8> members; + if (has_vertex_index) { + members.Push(b.Member(kFirstVertexName, b.ty.u32(), + Vector{b.MemberOffset(AInt(*cfg->first_vertex_offset))})); + } + if (has_instance_index) { + members.Push(b.Member(kFirstInstanceName, b.ty.u32(), + Vector{b.MemberOffset(AInt(*cfg->first_instance_offset))})); + } + auto struct_ = b.Structure(b.Symbols().New("PushConstants"), std::move(members)); + // Create a global to hold the uniform buffer + Symbol buffer_name = b.Symbols().New("push_constants"); + b.GlobalVar(buffer_name, b.ty.Of(struct_), core::AddressSpace::kPushConstant); + + // Fix up all references to the builtins with the offsets + ctx.ReplaceAll([&](const Expression* expr) -> const Expression* { + if (auto* sem = ctx.src->Sem().GetVal(expr)) { + if (auto* user = sem->UnwrapLoad()->As<sem::VariableUser>()) { + auto it = builtin_vars.find(user->Variable()); + if (it != builtin_vars.end()) { + return ctx.dst->Add(ctx.CloneWithoutTransform(expr), + ctx.dst->MemberAccessor(buffer_name, it->second)); + } + } + if (auto* access = sem->As<sem::StructMemberAccess>()) { + auto it = builtin_members.find(access->Member()); + if (it != builtin_members.end()) { + return ctx.dst->Add(ctx.CloneWithoutTransform(expr), + ctx.dst->MemberAccessor(buffer_name, it->second)); + } + } + } + // Not interested in this expression. Just clone. + return nullptr; + }); + + ctx.Clone(); + return resolver::Resolve(b); +} + +OffsetFirstIndex::Config::Config(std::optional<int32_t> first_vertex_off, + std::optional<int32_t> first_instance_off) + : first_vertex_offset(first_vertex_off), first_instance_offset(first_instance_off) {} + +OffsetFirstIndex::Config::~Config() = default; + +} // namespace tint::ast::transform
diff --git a/src/tint/lang/wgsl/ast/transform/offset_first_index.h b/src/tint/lang/wgsl/ast/transform/offset_first_index.h new file mode 100644 index 0000000..aa37559 --- /dev/null +++ b/src/tint/lang/wgsl/ast/transform/offset_first_index.h
@@ -0,0 +1,111 @@ +// 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_WGSL_AST_TRANSFORM_OFFSET_FIRST_INDEX_H_ +#define SRC_TINT_LANG_WGSL_AST_TRANSFORM_OFFSET_FIRST_INDEX_H_ + +#include "src/tint/lang/wgsl/ast/transform/transform.h" + +namespace tint::ast::transform { + +/// Adds firstVertex/Instance (injected via push constants) to +/// vertex/instance index builtins. +/// +/// This transform assumes that Name transform has been run before. +/// +/// Some shading languages start vertex and instance numbering at 0, +/// regardless of the firstVertex/firstInstance value specified. This transform +/// adds the value of firstVertex/firstInstance to each builtin. This action is +/// performed by adding a new push constant equal to original builtin + +/// firstVertex/firstInstance to each function that references one of +/// these builtins. +/// +/// For D3D, this affects both firstVertex and firstInstance. For OpenGL, +/// it applies to only firstInstance. For this reason, the first_vertex_offset +/// and first_instance_offset are optional, where std::nullopt indicates that +/// no subsitution is to be performed. +/// +/// Before: +/// ``` +/// @builtin(vertex_index) var<in> vert_idx : u32; +/// @builtin(instance_index) var<in> inst_idx : u32; +/// fn func() -> u32 { +/// return vert_idx * inst_idx; +/// } +/// ``` +/// +/// After: +/// ``` +/// struct PushConstants { +/// first_vertex : u32, +/// first_instance : u32, +/// } +/// +/// var<push_constant> push_constants : PushConstants; +/// +/// @builtin(vertex_index) var<in> vert_idx : u32; +/// @builtin(instance_index) var<in> inst_idx : u32; +/// fn func() -> u32 { +/// return (vert_idx + push_constants.first_vertex) * (inst_idx + +/// push_constants.first_instance); +/// } +/// ``` +/// +class OffsetFirstIndex final : public Castable<OffsetFirstIndex, Transform> { + public: + /// Transform configuration options + struct Config final : public Castable<Config, ast::transform::Data> { + /// Constructor + /// @param first_vertex_off Offset of the firstVertex push constant + /// @param first_instance_off Location of the firstInstance push constant + Config(std::optional<int32_t> first_vertex_off, std::optional<int32_t> first_instance_off); + + /// Destructor + ~Config() override; + + /// Offset of the firstVertex push constant + const std::optional<uint32_t> first_vertex_offset; + + /// Offset of the firstInstance push constant + const std::optional<uint32_t> first_instance_offset; + }; + + /// Constructor + OffsetFirstIndex(); + + /// Destructor + ~OffsetFirstIndex() override; + + /// @copydoc Transform::Apply + ApplyResult Apply(const Program& program, + const DataMap& inputs, + DataMap& outputs) const override; +}; + +} // namespace tint::ast::transform + +#endif // SRC_TINT_LANG_WGSL_AST_TRANSFORM_OFFSET_FIRST_INDEX_H_
diff --git a/src/tint/lang/wgsl/ast/transform/offset_first_index_test.cc b/src/tint/lang/wgsl/ast/transform/offset_first_index_test.cc new file mode 100644 index 0000000..37a79a9 --- /dev/null +++ b/src/tint/lang/wgsl/ast/transform/offset_first_index_test.cc
@@ -0,0 +1,856 @@ +// 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/wgsl/ast/transform/offset_first_index.h" + +#include <memory> +#include <utility> +#include <vector> + +#include "src/tint/lang/wgsl/ast/transform/helper_test.h" + +namespace tint::ast::transform { +namespace { + +using OffsetFirstIndexTest = TransformTest; + +TEST_F(OffsetFirstIndexTest, ShouldRunEmptyModule) { + auto* src = R"()"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + EXPECT_FALSE(ShouldRun<OffsetFirstIndex>(src, config)); +} + +TEST_F(OffsetFirstIndexTest, ShouldRunFragmentStage) { + auto* src = R"( +@fragment +fn entry() { + return; +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + EXPECT_FALSE(ShouldRun<OffsetFirstIndex>(src, config)); +} + +TEST_F(OffsetFirstIndexTest, ShouldRunVertexStage) { + auto* src = R"( +@vertex +fn entry() -> @builtin(position) vec4<f32> { + return vec4<f32>(); +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + EXPECT_FALSE(ShouldRun<OffsetFirstIndex>(src, config)); +} + +TEST_F(OffsetFirstIndexTest, ShouldRunVertexStageWithVertexIndex) { + auto* src = R"( +@vertex +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + return vec4<f32>(); +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + EXPECT_TRUE(ShouldRun<OffsetFirstIndex>(src, config)); +} + +TEST_F(OffsetFirstIndexTest, ShouldRunVertexStageWithInstanceIndex) { + auto* src = R"( +@vertex +fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> { + return vec4<f32>(); +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + EXPECT_TRUE(ShouldRun<OffsetFirstIndex>(src, config)); +} + +TEST_F(OffsetFirstIndexTest, EmptyModule) { + auto* src = ""; + auto* expect = ""; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, BasicVertexShader) { + auto* src = R"( +@vertex +fn entry() -> @builtin(position) vec4<f32> { + return vec4<f32>(); +} +)"; + auto* expect = src; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, BasicModuleVertexIndex) { + auto* src = R"( +fn test(vert_idx : u32) -> u32 { + return vert_idx; +} + +@vertex +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + test(vert_idx); + return vec4<f32>(); +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants { + /* @offset(0) */ + first_vertex : u32, +} + +var<push_constant> push_constants : PushConstants; + +fn test(vert_idx : u32) -> u32 { + return vert_idx; +} + +@vertex +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + test((vert_idx + push_constants.first_vertex)); + return vec4<f32>(); +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, BasicModuleVertexIndex_OutOfOrder) { + auto* src = R"( +@vertex +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + test(vert_idx); + return vec4<f32>(); +} + +fn test(vert_idx : u32) -> u32 { + return vert_idx; +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants { + /* @offset(0) */ + first_vertex : u32, +} + +var<push_constant> push_constants : PushConstants; + +@vertex +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + test((vert_idx + push_constants.first_vertex)); + return vec4<f32>(); +} + +fn test(vert_idx : u32) -> u32 { + return vert_idx; +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, BasicModuleInstanceIndex) { + auto* src = R"( +fn test(inst_idx : u32) -> u32 { + return inst_idx; +} + +@vertex +fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> { + test(inst_idx); + return vec4<f32>(); +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants { + @size(4) + padding_0 : u32, + /* @offset(4) */ + first_instance : u32, +} + +var<push_constant> push_constants : PushConstants; + +fn test(inst_idx : u32) -> u32 { + return inst_idx; +} + +@vertex +fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> { + test((inst_idx + push_constants.first_instance)); + return vec4<f32>(); +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, BasicModuleInstanceIndex_OutOfOrder) { + auto* src = R"( +@vertex +fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> { + test(inst_idx); + return vec4<f32>(); +} + +fn test(inst_idx : u32) -> u32 { + return inst_idx; +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants { + @size(4) + padding_0 : u32, + /* @offset(4) */ + first_instance : u32, +} + +var<push_constant> push_constants : PushConstants; + +@vertex +fn entry(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> { + test((inst_idx + push_constants.first_instance)); + return vec4<f32>(); +} + +fn test(inst_idx : u32) -> u32 { + return inst_idx; +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, BasicModuleBothIndexes) { + auto* src = R"( +fn test(instance_idx : u32, vert_idx : u32) -> u32 { + return instance_idx + vert_idx; +} + +struct Inputs { + @builtin(instance_index) instance_idx : u32, + @builtin(vertex_index) vert_idx : u32, +}; + +@vertex +fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> { + test(inputs.instance_idx, inputs.vert_idx); + return vec4<f32>(); +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants { + /* @offset(0) */ + first_vertex : u32, + /* @offset(4) */ + first_instance : u32, +} + +var<push_constant> push_constants : PushConstants; + +fn test(instance_idx : u32, vert_idx : u32) -> u32 { + return (instance_idx + vert_idx); +} + +struct Inputs { + @builtin(instance_index) + instance_idx : u32, + @builtin(vertex_index) + vert_idx : u32, +} + +@vertex +fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> { + test((inputs.instance_idx + push_constants.first_instance), (inputs.vert_idx + push_constants.first_vertex)); + return vec4<f32>(); +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, BasicModuleBothIndexes_OutOfOrder) { + auto* src = R"( +@vertex +fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> { + test(inputs.instance_idx, inputs.vert_idx); + return vec4<f32>(); +} + +struct Inputs { + @builtin(instance_index) instance_idx : u32, + @builtin(vertex_index) vert_idx : u32, +}; + +fn test(instance_idx : u32, vert_idx : u32) -> u32 { + return instance_idx + vert_idx; +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants { + /* @offset(0) */ + first_vertex : u32, + /* @offset(4) */ + first_instance : u32, +} + +var<push_constant> push_constants : PushConstants; + +@vertex +fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> { + test((inputs.instance_idx + push_constants.first_instance), (inputs.vert_idx + push_constants.first_vertex)); + return vec4<f32>(); +} + +struct Inputs { + @builtin(instance_index) + instance_idx : u32, + @builtin(vertex_index) + vert_idx : u32, +} + +fn test(instance_idx : u32, vert_idx : u32) -> u32 { + return (instance_idx + vert_idx); +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, BasicModuleBothIndexesVertexDisabled) { + auto* src = R"( +fn test(instance_idx : u32, vert_idx : u32) -> u32 { + return instance_idx + vert_idx; +} + +struct Inputs { + @builtin(instance_index) instance_idx : u32, + @builtin(vertex_index) vert_idx : u32, +}; + +@vertex +fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> { + test(inputs.instance_idx, inputs.vert_idx); + return vec4<f32>(); +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants { + /* @offset(0) */ + first_instance : u32, +} + +var<push_constant> push_constants : PushConstants; + +fn test(instance_idx : u32, vert_idx : u32) -> u32 { + return (instance_idx + vert_idx); +} + +struct Inputs { + @builtin(instance_index) + instance_idx : u32, + @builtin(vertex_index) + vert_idx : u32, +} + +@vertex +fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> { + test((inputs.instance_idx + push_constants.first_instance), inputs.vert_idx); + return vec4<f32>(); +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(std::nullopt, 0); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, BasicModuleBothIndexesInstanceDisabled) { + auto* src = R"( +fn test(instance_idx : u32, vert_idx : u32) -> u32 { + return instance_idx + vert_idx; +} + +struct Inputs { + @builtin(instance_index) instance_idx : u32, + @builtin(vertex_index) vert_idx : u32, +}; + +@vertex +fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> { + test(inputs.instance_idx, inputs.vert_idx); + return vec4<f32>(); +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants { + /* @offset(0) */ + first_vertex : u32, +} + +var<push_constant> push_constants : PushConstants; + +fn test(instance_idx : u32, vert_idx : u32) -> u32 { + return (instance_idx + vert_idx); +} + +struct Inputs { + @builtin(instance_index) + instance_idx : u32, + @builtin(vertex_index) + vert_idx : u32, +} + +@vertex +fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> { + test(inputs.instance_idx, (inputs.vert_idx + push_constants.first_vertex)); + return vec4<f32>(); +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, std::nullopt); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, BasicModuleBothIndexesBothDisabled) { + auto* src = R"( +fn test(instance_idx : u32, vert_idx : u32) -> u32 { + return (instance_idx + vert_idx); +} + +struct Inputs { + @builtin(instance_index) + instance_idx : u32, + @builtin(vertex_index) + vert_idx : u32, +} + +@vertex +fn entry(inputs : Inputs) -> @builtin(position) vec4<f32> { + test(inputs.instance_idx, inputs.vert_idx); + return vec4<f32>(); +} +)"; + + auto* expect = src; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(std::nullopt, std::nullopt); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +// Test a shader with a user-declared struct called PushConstants, to force +// renaming of the Tint-provided PushConstants struct. +TEST_F(OffsetFirstIndexTest, ForceRenamingPushConstantsStruct) { + auto* src = R"( +struct PushConstants { + f : f32, +} + +@group(0) @binding(0) var<uniform> p : PushConstants; + +@vertex +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + return vec4<f32>(f32(vert_idx) + p.f); +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants_1 { + /* @offset(0) */ + first_vertex : u32, +} + +var<push_constant> push_constants : PushConstants_1; + +struct PushConstants { + f : f32, +} + +@group(0) @binding(0) var<uniform> p : PushConstants; + +@vertex +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + return vec4<f32>((f32((vert_idx + push_constants.first_vertex)) + p.f)); +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +// Test a shader with a user-declared variable called push_constants, to force +// renaming of the Tint-provided push_constants variable. +TEST_F(OffsetFirstIndexTest, ForceRenamingPushConstantsVar) { + auto* src = R"( +@group(0) @binding(0) var<uniform> push_constants : u32; + +@vertex +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + return vec4<f32>(f32(vert_idx)); +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants { + /* @offset(0) */ + first_vertex : u32, +} + +var<push_constant> push_constants_1 : PushConstants; + +@group(0) @binding(0) var<uniform> push_constants : u32; + +@vertex +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + return vec4<f32>(f32((vert_idx + push_constants_1.first_vertex))); +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, NestedCalls) { + auto* src = R"( +fn func1(vert_idx : u32) -> u32 { + return vert_idx; +} + +fn func2(vert_idx : u32) -> u32 { + return func1(vert_idx); +} + +@vertex +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + func2(vert_idx); + return vec4<f32>(); +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants { + /* @offset(0) */ + first_vertex : u32, +} + +var<push_constant> push_constants : PushConstants; + +fn func1(vert_idx : u32) -> u32 { + return vert_idx; +} + +fn func2(vert_idx : u32) -> u32 { + return func1(vert_idx); +} + +@vertex +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + func2((vert_idx + push_constants.first_vertex)); + return vec4<f32>(); +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, NestedCalls_OutOfOrder) { + auto* src = R"( +@vertex +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + func2(vert_idx); + return vec4<f32>(); +} + +fn func2(vert_idx : u32) -> u32 { + return func1(vert_idx); +} + +fn func1(vert_idx : u32) -> u32 { + return vert_idx; +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants { + /* @offset(0) */ + first_vertex : u32, +} + +var<push_constant> push_constants : PushConstants; + +@vertex +fn entry(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + func2((vert_idx + push_constants.first_vertex)); + return vec4<f32>(); +} + +fn func2(vert_idx : u32) -> u32 { + return func1(vert_idx); +} + +fn func1(vert_idx : u32) -> u32 { + return vert_idx; +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, MultipleEntryPoints) { + auto* src = R"( +fn func(i : u32) -> u32 { + return i; +} + +@vertex +fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + func(vert_idx); + return vec4<f32>(); +} + +@vertex +fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> { + func(vert_idx + inst_idx); + return vec4<f32>(); +} + +@vertex +fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> { + func(inst_idx); + return vec4<f32>(); +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants { + /* @offset(0) */ + first_vertex : u32, + /* @offset(4) */ + first_instance : u32, +} + +var<push_constant> push_constants : PushConstants; + +fn func(i : u32) -> u32 { + return i; +} + +@vertex +fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + func((vert_idx + push_constants.first_vertex)); + return vec4<f32>(); +} + +@vertex +fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> { + func(((vert_idx + push_constants.first_vertex) + (inst_idx + push_constants.first_instance))); + return vec4<f32>(); +} + +@vertex +fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> { + func((inst_idx + push_constants.first_instance)); + return vec4<f32>(); +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(OffsetFirstIndexTest, MultipleEntryPoints_OutOfOrder) { + auto* src = R"( +@vertex +fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + func(vert_idx); + return vec4<f32>(); +} + +@vertex +fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> { + func(vert_idx + inst_idx); + return vec4<f32>(); +} + +@vertex +fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> { + func(inst_idx); + return vec4<f32>(); +} + +fn func(i : u32) -> u32 { + return i; +} +)"; + + auto* expect = R"( +enable chromium_experimental_push_constant; + +struct PushConstants { + /* @offset(0) */ + first_vertex : u32, + /* @offset(4) */ + first_instance : u32, +} + +var<push_constant> push_constants : PushConstants; + +@vertex +fn entry_a(@builtin(vertex_index) vert_idx : u32) -> @builtin(position) vec4<f32> { + func((vert_idx + push_constants.first_vertex)); + return vec4<f32>(); +} + +@vertex +fn entry_b(@builtin(vertex_index) vert_idx : u32, @builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> { + func(((vert_idx + push_constants.first_vertex) + (inst_idx + push_constants.first_instance))); + return vec4<f32>(); +} + +@vertex +fn entry_c(@builtin(instance_index) inst_idx : u32) -> @builtin(position) vec4<f32> { + func((inst_idx + push_constants.first_instance)); + return vec4<f32>(); +} + +fn func(i : u32) -> u32 { + return i; +} +)"; + + DataMap config; + config.Add<OffsetFirstIndex::Config>(0, 4); + auto got = Run<OffsetFirstIndex>(src, std::move(config)); + + EXPECT_EQ(expect, str(got)); +} + +} // namespace +} // namespace tint::ast::transform
diff --git a/test/tint/var/uses/push_constant.wgsl.expected.glsl b/test/tint/var/uses/push_constant.wgsl.expected.glsl index 6ed822e..aea31fb 100644 --- a/test/tint/var/uses/push_constant.wgsl.expected.glsl +++ b/test/tint/var/uses/push_constant.wgsl.expected.glsl
@@ -1,8 +1,12 @@ #version 310 es -uniform int a; +struct a_block { + int inner; +}; + +layout(location=0) uniform a_block a; void uses_a() { - int foo = a; + int foo = a.inner; } void main1() { @@ -16,9 +20,13 @@ } #version 310 es -uniform int a; +struct a_block { + int inner; +}; + +layout(location=0) uniform a_block a; void uses_a() { - int foo = a; + int foo = a.inner; } void uses_uses_a() { @@ -36,9 +44,13 @@ } #version 310 es -uniform int b; +struct b_block { + int inner; +}; + +layout(location=0) uniform b_block b; void uses_b() { - int foo = b; + int foo = b.inner; } void main3() {
diff --git a/webgpu-cts/compat-expectations.txt b/webgpu-cts/compat-expectations.txt index 704869e..64687a7 100644 --- a/webgpu-cts/compat-expectations.txt +++ b/webgpu-cts/compat-expectations.txt
@@ -381,91 +381,6 @@ crbug.com/dawn/2123 [ nvidia-0x2184 ] webgpu:api,validation,capability_checks,limits,maxStorageBuffersPerShaderStage:createPipeline,at_over:limitTest="atMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="forward";bindGroupTest="differentGroups" [ Failure ] crbug.com/dawn/2123 [ nvidia-0x2184 ] webgpu:api,validation,capability_checks,limits,maxStorageBuffersPerShaderStage:createPipeline,at_over:limitTest="atMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="forward";bindGroupTest="sameGroup" [ Failure ] -# Instancing issues on NVidia GL -# count=[1-9].*first_instance=[1-9].*instance_count=[1-9] -# These only occur on ANGLE's NVidia GL and GLES backends. ANGLE's SwiftShader -# and Vk backends are fine. -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=1;indexed=false;indirect=false;vertex_buffer_offset=0;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=1;indexed=false;indirect=false;vertex_buffer_offset=32;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=4;indexed=false;indirect=false;vertex_buffer_offset=0;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=4;indexed=false;indirect=false;vertex_buffer_offset=32;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=1;indexed=false;indirect=false;vertex_buffer_offset=0;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=1;indexed=false;indirect=false;vertex_buffer_offset=32;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=4;indexed=false;indirect=false;vertex_buffer_offset=0;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=4;indexed=false;indirect=false;vertex_buffer_offset=32;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=0;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=1;indexed=false;indirect=false;vertex_buffer_offset=0;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=1;indexed=false;indirect=false;vertex_buffer_offset=32;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=4;indexed=false;indirect=false;vertex_buffer_offset=0;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=4;indexed=false;indirect=false;vertex_buffer_offset=32;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=3;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=1;indexed=false;indirect=false;vertex_buffer_offset=0;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=1;indexed=false;indirect=false;vertex_buffer_offset=32;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=1;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=4;indexed=false;indirect=false;vertex_buffer_offset=0;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=4;indexed=false;indirect=false;vertex_buffer_offset=32;index_buffer_offset="_undef_";base_vertex="_undef_" [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=0;index_buffer_offset=16;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=0;base_vertex=9 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=0 [ Failure ] -crbug.com/dawn/2119 webgpu:api,operation,rendering,draw:arguments:first=3;count=6;first_instance=2;instance_count=4;indexed=true;indirect=false;vertex_buffer_offset=32;index_buffer_offset=16;base_vertex=9 [ Failure ] - # Zero init issues, but only with batch__=9 (NVidia driver bug) crbug.com/dawn/2125 [ nvidia-0x2184 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="function";workgroupSize=[1,1,1];batch__=9 [ Failure ] crbug.com/dawn/2125 [ nvidia-0x2184 ] webgpu:shader,execution,zero_init:compute,zero_init:addressSpace="private";workgroupSize=[1,1,1];batch__=9 [ Failure ]