[WebGPU backend] Add webgpu::Sampler

Bug: 413053623
Change-Id: I63debf820369a4680909b4f6f6201c056112622f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/263137
Reviewed-by: Gregg Tavares <gman@google.com>
Reviewed-by: Gregg Tavares <gman@chromium.org>
Commit-Queue: Shrek Shao <shrekshao@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index e9d5cb2..49e1a15 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -741,6 +741,8 @@
       "webgpu/QueueWGPU.h",
       "webgpu/RenderPipelineWGPU.cpp",
       "webgpu/RenderPipelineWGPU.h",
+      "webgpu/SamplerWGPU.cpp",
+      "webgpu/SamplerWGPU.h",
       "webgpu/ShaderModuleWGPU.cpp",
       "webgpu/ShaderModuleWGPU.h",
       "webgpu/TextureWGPU.cpp",
@@ -766,7 +768,7 @@
   if (dawn_enable_opengl) {
     public_deps += [
       ":opengl_loader_gen",
-      "${dawn_root}/src/tint/api/common:common",
+      "${dawn_root}/src/tint/api/common",
       "${dawn_root}/third_party/khronos:khronos_platform",
     ]
     sources += get_target_outputs(":opengl_loader_gen")
diff --git a/src/dawn/native/CMakeLists.txt b/src/dawn/native/CMakeLists.txt
index 21ef415..72b92fc 100644
--- a/src/dawn/native/CMakeLists.txt
+++ b/src/dawn/native/CMakeLists.txt
@@ -600,6 +600,7 @@
         "webgpu/PipelineLayoutWGPU.h"
         "webgpu/QueueWGPU.h"
         "webgpu/RenderPipelineWGPU.h"
+        "webgpu/SamplerWGPU.h"
         "webgpu/ShaderModuleWGPU.h"
         "webgpu/ToWGPU.h"
         "webgpu/TextureWGPU.h"
@@ -617,6 +618,7 @@
         "webgpu/PipelineLayoutWGPU.cpp"
         "webgpu/QueueWGPU.cpp"
         "webgpu/RenderPipelineWGPU.cpp"
+        "webgpu/SamplerWGPU.cpp"
         "webgpu/ShaderModuleWGPU.cpp"
         "webgpu/ToWGPU.cpp"
         "webgpu/TextureWGPU.cpp"
diff --git a/src/dawn/native/webgpu/BindGroupWGPU.cpp b/src/dawn/native/webgpu/BindGroupWGPU.cpp
index db755ff..3cacdea 100644
--- a/src/dawn/native/webgpu/BindGroupWGPU.cpp
+++ b/src/dawn/native/webgpu/BindGroupWGPU.cpp
@@ -32,6 +32,7 @@
 #include "dawn/native/webgpu/BindGroupLayoutWGPU.h"
 #include "dawn/native/webgpu/BufferWGPU.h"
 #include "dawn/native/webgpu/DeviceWGPU.h"
+#include "dawn/native/webgpu/SamplerWGPU.h"
 #include "dawn/native/webgpu/TextureWGPU.h"
 
 namespace dawn::native::webgpu {
@@ -45,9 +46,8 @@
         .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,
+        .sampler =
+            entry->sampler == nullptr ? nullptr : ToBackend(entry->sampler)->GetInnerHandle(),
         .textureView = entry->textureView == nullptr
                            ? nullptr
                            : ToBackend(entry->textureView)->GetInnerHandle(),
diff --git a/src/dawn/native/webgpu/DeviceWGPU.cpp b/src/dawn/native/webgpu/DeviceWGPU.cpp
index 97558b1..83c2b92 100644
--- a/src/dawn/native/webgpu/DeviceWGPU.cpp
+++ b/src/dawn/native/webgpu/DeviceWGPU.cpp
@@ -44,7 +44,6 @@
 #include "dawn/native/PhysicalDevice.h"
 #include "dawn/native/QuerySet.h"
 #include "dawn/native/Queue.h"
-#include "dawn/native/Sampler.h"
 #include "dawn/native/Surface.h"
 #include "dawn/native/SwapChain.h"
 #include "dawn/native/Texture.h"
@@ -58,6 +57,7 @@
 #include "dawn/native/webgpu/PipelineLayoutWGPU.h"
 #include "dawn/native/webgpu/QueueWGPU.h"
 #include "dawn/native/webgpu/RenderPipelineWGPU.h"
+#include "dawn/native/webgpu/SamplerWGPU.h"
 #include "dawn/native/webgpu/ShaderModuleWGPU.h"
 #include "dawn/native/webgpu/TextureWGPU.h"
 
@@ -179,7 +179,7 @@
     return RenderPipeline::CreateUninitialized(this, descriptor);
 }
 ResultOrError<Ref<SamplerBase>> Device::CreateSamplerImpl(const SamplerDescriptor* descriptor) {
-    return Ref<SamplerBase>{nullptr};
+    return Sampler::Create(this, descriptor);
 }
 ResultOrError<Ref<ShaderModuleBase>> Device::CreateShaderModuleImpl(
     const UnpackedPtr<ShaderModuleDescriptor>& descriptor,
diff --git a/src/dawn/native/webgpu/SamplerWGPU.cpp b/src/dawn/native/webgpu/SamplerWGPU.cpp
new file mode 100644
index 0000000..a82536c
--- /dev/null
+++ b/src/dawn/native/webgpu/SamplerWGPU.cpp
@@ -0,0 +1,75 @@
+// 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/SamplerWGPU.h"
+
+#include "dawn/common/StringViewUtils.h"
+#include "dawn/native/webgpu/DeviceWGPU.h"
+
+namespace dawn::native::webgpu {
+
+namespace {
+
+WGPUSamplerDescriptor ToWGPU(const SamplerDescriptor* desc) {
+    return {
+        .nextInChain = nullptr,
+        .label = ToOutputStringView(desc->label),
+        .addressModeU = ToAPI(desc->addressModeU),
+        .addressModeV = ToAPI(desc->addressModeV),
+        .addressModeW = ToAPI(desc->addressModeW),
+        .magFilter = ToAPI(desc->magFilter),
+        .minFilter = ToAPI(desc->minFilter),
+        .mipmapFilter = ToAPI(desc->mipmapFilter),
+        .lodMinClamp = desc->lodMinClamp,
+        .lodMaxClamp = desc->lodMaxClamp,
+        .compare = ToAPI(desc->compare),
+        .maxAnisotropy = desc->maxAnisotropy,
+    };
+}
+
+}  // namespace
+
+// static
+ResultOrError<Ref<Sampler>> Sampler::Create(Device* device, const SamplerDescriptor* descriptor) {
+    Ref<Sampler> sampler = AcquireRef(new Sampler(device, descriptor));
+    DAWN_TRY(sampler->Initialize());
+    return sampler;
+}
+
+Sampler::Sampler(Device* device, const SamplerDescriptor* descriptor)
+    : SamplerBase(device, descriptor), ObjectWGPU(device->wgpu.samplerRelease) {
+    WGPUSamplerDescriptor desc = ToWGPU(descriptor);
+    mInnerHandle = ToBackend(GetDevice())
+                       ->wgpu.deviceCreateSampler(ToBackend(GetDevice())->GetInnerHandle(), &desc);
+    DAWN_ASSERT(mInnerHandle);
+}
+
+MaybeError Sampler::Initialize() {
+    return {};
+}
+
+}  // namespace dawn::native::webgpu
diff --git a/src/dawn/native/webgpu/SamplerWGPU.h b/src/dawn/native/webgpu/SamplerWGPU.h
new file mode 100644
index 0000000..776492a
--- /dev/null
+++ b/src/dawn/native/webgpu/SamplerWGPU.h
@@ -0,0 +1,53 @@
+// 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_SAMPLERWGPU_H_
+#define SRC_DAWN_NATIVE_WEBGPU_SAMPLERWGPU_H_
+
+#include "dawn/native/Sampler.h"
+#include "dawn/native/webgpu/ObjectWGPU.h"
+
+#include "dawn/webgpu.h"
+
+namespace dawn::native::webgpu {
+
+class Device;
+
+class Sampler final : public SamplerBase, public ObjectWGPU<WGPUSampler> {
+  public:
+    static ResultOrError<Ref<Sampler>> Create(Device* device, const SamplerDescriptor* descriptor);
+
+  private:
+    Sampler(Device* device, const SamplerDescriptor* descriptor);
+    ~Sampler() override = default;
+
+    MaybeError Initialize();
+};
+
+}  // namespace dawn::native::webgpu
+
+#endif  // SRC_DAWN_NATIVE_WEBGPU_SAMPLERWGPU_H_
diff --git a/src/dawn/tests/end2end/BindGroupTests.cpp b/src/dawn/tests/end2end/BindGroupTests.cpp
index 9d3fadd..48fa1c9 100644
--- a/src/dawn/tests/end2end/BindGroupTests.cpp
+++ b/src/dawn/tests/end2end/BindGroupTests.cpp
@@ -263,8 +263,6 @@
 // shader. In D3D12 for example, these different types of bindings end up in different namespaces,
 // but the register offsets used must match between the shader module and descriptor range.
 TEST_P(BindGroupTests, UBOSamplerAndTexture) {
-    // TODO(crbug.com/413053623): implement webgpu::Sampler.
-    DAWN_SUPPRESS_TEST_IF(IsWebGPUOnWebGPU());
     utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
 
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
diff --git a/src/dawn/tests/end2end/SamplerTests.cpp b/src/dawn/tests/end2end/SamplerTests.cpp
index fd89b51..05e642a 100644
--- a/src/dawn/tests/end2end/SamplerTests.cpp
+++ b/src/dawn/tests/end2end/SamplerTests.cpp
@@ -259,7 +259,8 @@
                       MetalBackend(),
                       OpenGLBackend(),
                       OpenGLESBackend(),
-                      VulkanBackend());
+                      VulkanBackend(),
+                      WebGPUBackend());
 
 class StaticSamplerTest : public SamplerTest {
   protected:
@@ -357,7 +358,8 @@
                       MetalBackend(),
                       OpenGLBackend(),
                       OpenGLESBackend(),
-                      VulkanBackend());
+                      VulkanBackend(),
+                      WebGPUBackend());
 
 }  // anonymous namespace
 }  // namespace dawn