[WebGPU backend] Add BindGroup / BindGroupLayout

Bug: 413053623
Change-Id: Iebcb23bc75089bda5307f558ec00c8f02975a20c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/259858
Reviewed-by: Gregg Tavares <gman@chromium.org>
Commit-Queue: Shrek Shao <shrekshao@google.com>
Reviewed-by: Loko Kung <lokokung@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index 8629509..30c2773 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -719,6 +719,10 @@
     sources += [
       "webgpu/BackendWGPU.cpp",
       "webgpu/BackendWGPU.h",
+      "webgpu/BindGroupLayoutWGPU.cpp",
+      "webgpu/BindGroupLayoutWGPU.h",
+      "webgpu/BindGroupWGPU.cpp",
+      "webgpu/BindGroupWGPU.h",
       "webgpu/BufferWGPU.cpp",
       "webgpu/BufferWGPU.h",
       "webgpu/CommandBufferWGPU.cpp",
diff --git a/src/dawn/native/CMakeLists.txt b/src/dawn/native/CMakeLists.txt
index 354577a..4ff9ce8 100644
--- a/src/dawn/native/CMakeLists.txt
+++ b/src/dawn/native/CMakeLists.txt
@@ -585,6 +585,8 @@
     )
     list(APPEND private_headers
         "webgpu/BackendWGPU.h"
+        "webgpu/BindGroupWGPU.h"
+        "webgpu/BindGroupLayoutWGPU.h"
         "webgpu/BufferWGPU.h"
         "webgpu/CommandBufferWGPU.h"
         "webgpu/DeviceWGPU.h"
@@ -597,6 +599,8 @@
     )
     list(APPEND sources
         "webgpu/BackendWGPU.cpp"
+        "webgpu/BindGroupWGPU.cpp"
+        "webgpu/BindGroupLayoutWGPU.cpp"
         "webgpu/BufferWGPU.cpp"
         "webgpu/CommandBufferWGPU.cpp"
         "webgpu/DeviceWGPU.cpp"
diff --git a/src/dawn/native/webgpu/BindGroupLayoutWGPU.cpp b/src/dawn/native/webgpu/BindGroupLayoutWGPU.cpp
new file mode 100644
index 0000000..1507d31
--- /dev/null
+++ b/src/dawn/native/webgpu/BindGroupLayoutWGPU.cpp
@@ -0,0 +1,66 @@
+// Copyright 2025 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 "dawn/native/webgpu/BindGroupLayoutWGPU.h"
+
+#include "dawn/native/webgpu/DeviceWGPU.h"
+#include "dawn/native/webgpu/Forward.h"
+
+namespace dawn::native::webgpu {
+
+// static
+ResultOrError<Ref<BindGroupLayout>> BindGroupLayout::Create(
+    Device* device,
+    const UnpackedPtr<BindGroupLayoutDescriptor>& descriptor) {
+    return AcquireRef(new BindGroupLayout(device, descriptor));
+}
+
+BindGroupLayout::BindGroupLayout(Device* device,
+                                 const UnpackedPtr<BindGroupLayoutDescriptor>& descriptor)
+    : BindGroupLayoutInternalBase(device, descriptor),
+      ObjectWGPU(device->wgpu.bindGroupLayoutRelease),
+      mBindGroupAllocator(MakeFrontendBindGroupAllocator<BindGroup>(4096)) {
+    auto desc = ToAPI(*descriptor);
+    mInnerHandle = device->wgpu.deviceCreateBindGroupLayout(device->GetInnerHandle(), desc);
+    DAWN_ASSERT(mInnerHandle);
+}
+
+Ref<BindGroup> BindGroupLayout::AllocateBindGroup(
+    const UnpackedPtr<BindGroupDescriptor>& descriptor) {
+    Device* device = ToBackend(GetDevice());
+    return AcquireRef(mBindGroupAllocator->Allocate(device, descriptor));
+}
+
+void BindGroupLayout::DeallocateBindGroup(BindGroup* bindGroup) {
+    mBindGroupAllocator->Deallocate(bindGroup);
+}
+
+void BindGroupLayout::ReduceMemoryUsage() {
+    mBindGroupAllocator->DeleteEmptySlabs();
+}
+
+}  // namespace dawn::native::webgpu
diff --git a/src/dawn/native/webgpu/BindGroupLayoutWGPU.h b/src/dawn/native/webgpu/BindGroupLayoutWGPU.h
new file mode 100644
index 0000000..6255f62
--- /dev/null
+++ b/src/dawn/native/webgpu/BindGroupLayoutWGPU.h
@@ -0,0 +1,59 @@
+// Copyright 2025 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_DAWN_NATIVE_WEBGPU_BINDGROUPLAYOUTWGPU_H_
+#define SRC_DAWN_NATIVE_WEBGPU_BINDGROUPLAYOUTWGPU_H_
+
+#include "dawn/common/MutexProtected.h"
+#include "dawn/common/SlabAllocator.h"
+#include "dawn/native/BindGroupLayoutInternal.h"
+#include "dawn/native/webgpu/BindGroupWGPU.h"
+#include "dawn/native/webgpu/ObjectWGPU.h"
+
+namespace dawn::native::webgpu {
+
+class BindGroupLayout final : public BindGroupLayoutInternalBase,
+                              public ObjectWGPU<WGPUBindGroupLayout> {
+  public:
+    static ResultOrError<Ref<BindGroupLayout>> Create(
+        Device* device,
+        const UnpackedPtr<BindGroupLayoutDescriptor>& descriptor);
+
+    Ref<BindGroup> AllocateBindGroup(const UnpackedPtr<BindGroupDescriptor>& descriptor);
+    void DeallocateBindGroup(BindGroup* bindGroup);
+    void ReduceMemoryUsage() override;
+
+  private:
+    BindGroupLayout(Device* device, const UnpackedPtr<BindGroupLayoutDescriptor>& descriptor);
+    ~BindGroupLayout() override = default;
+
+    MutexProtected<SlabAllocator<BindGroup>> mBindGroupAllocator;
+};
+
+}  // namespace dawn::native::webgpu
+
+#endif  // SRC_DAWN_NATIVE_WEBGPU_BINDGROUPLAYOUTWGPU_H_
diff --git a/src/dawn/native/webgpu/BindGroupWGPU.cpp b/src/dawn/native/webgpu/BindGroupWGPU.cpp
new file mode 100644
index 0000000..c7e6be7
--- /dev/null
+++ b/src/dawn/native/webgpu/BindGroupWGPU.cpp
@@ -0,0 +1,108 @@
+// Copyright 2025 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 "dawn/native/webgpu/BindGroupWGPU.h"
+
+#include "absl/container/inlined_vector.h"
+#include "dawn/common/StringViewUtils.h"
+#include "dawn/native/webgpu/BindGroupLayoutWGPU.h"
+#include "dawn/native/webgpu/BufferWGPU.h"
+#include "dawn/native/webgpu/DeviceWGPU.h"
+
+namespace dawn::native::webgpu {
+
+namespace {
+
+WGPUBindGroupEntry ToWGPU(const BindGroupEntry* entry) {
+    return {
+        .nextInChain = nullptr,
+        .binding = entry->binding,
+        .buffer = entry->buffer == nullptr ? nullptr : ToBackend(entry->buffer)->GetInnerHandle(),
+        .offset = entry->offset,
+        .size = entry->size,
+        // TODO(crbug.com/413053623): replace with the following when the object implementation is
+        // done.
+        .sampler = nullptr,
+        .textureView = nullptr,
+    };
+}
+
+class ComboBindGroupDescriptor {
+  public:
+    explicit ComboBindGroupDescriptor(const BindGroupDescriptor* desc) {
+        mDesc.nextInChain = nullptr;
+        mDesc.label = ToOutputStringView(desc->label);
+        mDesc.layout = ToBackend(desc->layout->GetInternalBindGroupLayout())->GetInnerHandle();
+        mDesc.entryCount = desc->entryCount;
+        for (uint32_t i = 0; i < desc->entryCount; ++i) {
+            mEntries.push_back(ToWGPU(&desc->entries[i]));
+        }
+        mDesc.entries = mEntries.data();
+    }
+
+    const WGPUBindGroupDescriptor* Get() const { return &mDesc; }
+
+  private:
+    WGPUBindGroupDescriptor mDesc;
+    absl::InlinedVector<WGPUBindGroupEntry, 8> mEntries;
+};
+
+}  // namespace
+
+// static
+ResultOrError<Ref<BindGroup>> BindGroup::Create(
+    Device* device,
+    const UnpackedPtr<BindGroupDescriptor>& descriptor) {
+    Ref<BindGroup> bindGroup =
+        ToBackend(descriptor->layout->GetInternalBindGroupLayout())->AllocateBindGroup(descriptor);
+    DAWN_TRY(bindGroup->Initialize(descriptor));
+    return bindGroup;
+}
+
+BindGroup::BindGroup(Device* device, const UnpackedPtr<BindGroupDescriptor>& descriptor)
+    : BindGroupBase(this, device, descriptor), ObjectWGPU(device->wgpu.bindGroupRelease) {
+    ComboBindGroupDescriptor desc(*descriptor);
+    mInnerHandle =
+        ToBackend(GetDevice())
+            ->wgpu.deviceCreateBindGroup(ToBackend(GetDevice())->GetInnerHandle(), desc.Get());
+    DAWN_ASSERT(mInnerHandle);
+}
+
+MaybeError BindGroup::InitializeImpl() {
+    return {};
+}
+
+void BindGroup::DeleteThis() {
+    // This function must first run the destructor and then deallocate memory. Take a reference to
+    // the BindGroupLayout+SlabAllocator before running the destructor so this function can access
+    // it afterwards and it's not destroyed prematurely.
+    Ref<BindGroupLayout> layout = ToBackend(GetLayout());
+    BindGroupBase::DeleteThis();
+    layout->DeallocateBindGroup(this);
+}
+
+}  // namespace dawn::native::webgpu
diff --git a/src/dawn/native/webgpu/BindGroupWGPU.h b/src/dawn/native/webgpu/BindGroupWGPU.h
new file mode 100644
index 0000000..5d1bd02
--- /dev/null
+++ b/src/dawn/native/webgpu/BindGroupWGPU.h
@@ -0,0 +1,56 @@
+// Copyright 2025 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_DAWN_NATIVE_WEBGPU_BINDGROUPWGPU_H_
+#define SRC_DAWN_NATIVE_WEBGPU_BINDGROUPWGPU_H_
+
+#include "dawn/native/BindGroup.h"
+
+#include "dawn/common/PlacementAllocated.h"
+#include "dawn/native/webgpu/Forward.h"
+#include "dawn/native/webgpu/ObjectWGPU.h"
+
+namespace dawn::native::webgpu {
+
+class BindGroup final : public BindGroupBase,
+                        public PlacementAllocated,
+                        public ObjectWGPU<WGPUBindGroup> {
+  public:
+    static ResultOrError<Ref<BindGroup>> Create(Device* device,
+                                                const UnpackedPtr<BindGroupDescriptor>& descriptor);
+    BindGroup(Device* device, const UnpackedPtr<BindGroupDescriptor>& descriptor);
+
+  private:
+    ~BindGroup() override = default;
+
+    MaybeError InitializeImpl() override;
+    void DeleteThis() override;
+};
+
+}  // namespace dawn::native::webgpu
+
+#endif  // SRC_DAWN_NATIVE_WEBGPU_BINDGROUPWGPU_H_
diff --git a/src/dawn/native/webgpu/CommandBufferWGPU.cpp b/src/dawn/native/webgpu/CommandBufferWGPU.cpp
index 2ea15da..c27f0d4 100644
--- a/src/dawn/native/webgpu/CommandBufferWGPU.cpp
+++ b/src/dawn/native/webgpu/CommandBufferWGPU.cpp
@@ -31,6 +31,7 @@
 
 #include "dawn/common/Assert.h"
 #include "dawn/common/StringViewUtils.h"
+#include "dawn/native/webgpu/BindGroupWGPU.h"
 #include "dawn/native/webgpu/BufferWGPU.h"
 #include "dawn/native/webgpu/DeviceWGPU.h"
 #include "dawn/native/webgpu/TextureWGPU.h"
@@ -238,12 +239,9 @@
                 if (cmd->dynamicOffsetCount > 0) {
                     dynamicOffsets = commands.NextData<uint32_t>(cmd->dynamicOffsetCount);
                 }
-                // TODO(crbug.com/440123094): remove nullptr when GetInnerHandle is implemented for
-                // BindGroupWGPU
-                wgpu.computePassEncoderSetBindGroup(
-                    passEncoder, static_cast<uint32_t>(cmd->index),
-                    nullptr /*ToBackend(cmd->group)->GetInnerHandle()*/, cmd->dynamicOffsetCount,
-                    dynamicOffsets);
+                wgpu.computePassEncoderSetBindGroup(passEncoder, static_cast<uint32_t>(cmd->index),
+                                                    ToBackend(cmd->group)->GetInnerHandle(),
+                                                    cmd->dynamicOffsetCount, dynamicOffsets);
                 break;
             }
             case Command::InsertDebugMarker: {
diff --git a/src/dawn/native/webgpu/DeviceWGPU.cpp b/src/dawn/native/webgpu/DeviceWGPU.cpp
index 8cd3524..0aedd6d 100644
--- a/src/dawn/native/webgpu/DeviceWGPU.cpp
+++ b/src/dawn/native/webgpu/DeviceWGPU.cpp
@@ -51,6 +51,8 @@
 #include "dawn/native/SwapChain.h"
 #include "dawn/native/Texture.h"
 #include "dawn/native/webgpu/BackendWGPU.h"
+#include "dawn/native/webgpu/BindGroupLayoutWGPU.h"
+#include "dawn/native/webgpu/BindGroupWGPU.h"
 #include "dawn/native/webgpu/BufferWGPU.h"
 #include "dawn/native/webgpu/CommandBufferWGPU.h"
 #include "dawn/native/webgpu/PhysicalDeviceWGPU.h"
@@ -128,14 +130,11 @@
 
 ResultOrError<Ref<BindGroupBase>> Device::CreateBindGroupImpl(
     const UnpackedPtr<BindGroupDescriptor>& descriptor) {
-    return Ref<BindGroupBase>{nullptr};
+    return BindGroup::Create(this, descriptor);
 }
 ResultOrError<Ref<BindGroupLayoutInternalBase>> Device::CreateBindGroupLayoutImpl(
     const UnpackedPtr<BindGroupLayoutDescriptor>& descriptor) {
-    // TODO(crbug.com/413053623): Replace with webgpu::BindGroupLayout object.
-    // This placeholder implementation is needed because device is creating empty BindGroupLayout
-    // and getting content hash.
-    return AcquireRef(new BindGroupLayoutInternalBase(this, descriptor));
+    return BindGroupLayout::Create(this, descriptor);
 }
 ResultOrError<Ref<BufferBase>> Device::CreateBufferImpl(
     const UnpackedPtr<BufferDescriptor>& descriptor) {