[WebGPU backend] Add ShaderModule

WebGPU ShaderModule doesn't compile shader on the backend.
There is no compiled backend blob cache. There is still front-end
cache for WGSL parse result.

Bug: 413053623
Change-Id: I2bfd325474e47d5a5c322e267b703ef2e566058c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/258978
Commit-Queue: Shrek Shao <shrekshao@google.com>
Reviewed-by: Gregg Tavares <gman@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index 30c2773..064c5de 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -735,6 +735,8 @@
       "webgpu/PhysicalDeviceWGPU.h",
       "webgpu/QueueWGPU.cpp",
       "webgpu/QueueWGPU.h",
+      "webgpu/ShaderModuleWGPU.cpp",
+      "webgpu/ShaderModuleWGPU.h",
       "webgpu/TextureWGPU.cpp",
       "webgpu/TextureWGPU.h",
       "webgpu/WebGPUError.h",
diff --git a/src/dawn/native/CMakeLists.txt b/src/dawn/native/CMakeLists.txt
index 4ff9ce8..967eac0 100644
--- a/src/dawn/native/CMakeLists.txt
+++ b/src/dawn/native/CMakeLists.txt
@@ -594,6 +594,7 @@
         "webgpu/ObjectWGPU.h"
         "webgpu/PhysicalDeviceWGPU.h"
         "webgpu/QueueWGPU.h"
+        "webgpu/ShaderModuleWGPU.h"
         "webgpu/TextureWGPU.h"
         "webgpu/WebGPUError.h"
     )
@@ -606,6 +607,7 @@
         "webgpu/DeviceWGPU.cpp"
         "webgpu/PhysicalDeviceWGPU.cpp"
         "webgpu/QueueWGPU.cpp"
+        "webgpu/ShaderModuleWGPU.cpp"
         "webgpu/TextureWGPU.cpp"
     )
 endif()
diff --git a/src/dawn/native/webgpu/DeviceWGPU.cpp b/src/dawn/native/webgpu/DeviceWGPU.cpp
index 0aedd6d..a9fc719 100644
--- a/src/dawn/native/webgpu/DeviceWGPU.cpp
+++ b/src/dawn/native/webgpu/DeviceWGPU.cpp
@@ -57,6 +57,7 @@
 #include "dawn/native/webgpu/CommandBufferWGPU.h"
 #include "dawn/native/webgpu/PhysicalDeviceWGPU.h"
 #include "dawn/native/webgpu/QueueWGPU.h"
+#include "dawn/native/webgpu/ShaderModuleWGPU.h"
 #include "dawn/native/webgpu/TextureWGPU.h"
 
 #include "tint/tint.h"
@@ -172,7 +173,7 @@
     const UnpackedPtr<ShaderModuleDescriptor>& descriptor,
     const std::vector<tint::wgsl::Extension>& internalExtensions,
     ShaderModuleParseResult* parseResult) {
-    return Ref<ShaderModuleBase>{nullptr};
+    return ShaderModule::Create(this, descriptor, internalExtensions, parseResult);
 }
 ResultOrError<Ref<SwapChainBase>> Device::CreateSwapChainImpl(Surface* surface,
                                                               SwapChainBase* previousSwapChain,
diff --git a/src/dawn/native/webgpu/ShaderModuleWGPU.cpp b/src/dawn/native/webgpu/ShaderModuleWGPU.cpp
new file mode 100644
index 0000000..894545b
--- /dev/null
+++ b/src/dawn/native/webgpu/ShaderModuleWGPU.cpp
@@ -0,0 +1,73 @@
+// 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/ShaderModuleWGPU.h"
+
+#include <utility>
+
+#include "dawn/native/ShaderModule.h"
+#include "dawn/native/webgpu/DeviceWGPU.h"
+
+namespace dawn::native::webgpu {
+
+// static
+ResultOrError<Ref<ShaderModule>> ShaderModule::Create(
+    Device* device,
+    const UnpackedPtr<ShaderModuleDescriptor>& descriptor,
+    const std::vector<tint::wgsl::Extension>& internalExtensions,
+    ShaderModuleParseResult* parseResult) {
+    Ref<ShaderModule> module = AcquireRef(new ShaderModule(device, descriptor, internalExtensions));
+    DAWN_TRY(module->Initialize(descriptor, parseResult));
+    return module;
+}
+
+ShaderModule::ShaderModule(Device* device,
+                           const UnpackedPtr<ShaderModuleDescriptor>& descriptor,
+                           std::vector<tint::wgsl::Extension> internalExtensions)
+    : ShaderModuleBase(device, descriptor, std::move(internalExtensions)),
+      ObjectWGPU(device->wgpu.shaderModuleRelease) {}
+
+MaybeError ShaderModule::Initialize(const UnpackedPtr<ShaderModuleDescriptor>& descriptor,
+                                    ShaderModuleParseResult* parseResult) {
+    DAWN_TRY(InitializeBase(parseResult));
+
+    if (GetCompilationMessages()->HasWarningsOrErrors()) {
+        // Cached parse result already shows it is an invalid shader.
+        // No need to create the real shader module on the backend.
+        return {};
+    }
+
+    auto desc = ToAPI(*descriptor);
+    mInnerHandle =
+        ToBackend(GetDevice())
+            ->wgpu.deviceCreateShaderModule(ToBackend(GetDevice())->GetInnerHandle(), desc);
+    DAWN_ASSERT(mInnerHandle);
+
+    return {};
+}
+
+}  // namespace dawn::native::webgpu
diff --git a/src/dawn/native/webgpu/ShaderModuleWGPU.h b/src/dawn/native/webgpu/ShaderModuleWGPU.h
new file mode 100644
index 0000000..59c6b4b4
--- /dev/null
+++ b/src/dawn/native/webgpu/ShaderModuleWGPU.h
@@ -0,0 +1,60 @@
+// 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_SHADERMODULEWGPU_H_
+#define SRC_DAWN_NATIVE_WEBGPU_SHADERMODULEWGPU_H_
+
+#include <vector>
+
+#include "dawn/native/ShaderModule.h"
+#include "dawn/native/webgpu/Forward.h"
+#include "dawn/native/webgpu/ObjectWGPU.h"
+
+namespace dawn::native::webgpu {
+
+class Device;
+
+class ShaderModule final : public ShaderModuleBase, public ObjectWGPU<WGPUShaderModule> {
+  public:
+    static ResultOrError<Ref<ShaderModule>> Create(
+        Device* device,
+        const UnpackedPtr<ShaderModuleDescriptor>& descriptor,
+        const std::vector<tint::wgsl::Extension>& internalExtensions,
+        ShaderModuleParseResult* parseResult);
+
+  private:
+    ShaderModule(Device* device,
+                 const UnpackedPtr<ShaderModuleDescriptor>& descriptor,
+                 std::vector<tint::wgsl::Extension> internalExtensions);
+    ~ShaderModule() override = default;
+    MaybeError Initialize(const UnpackedPtr<ShaderModuleDescriptor>& descriptor,
+                          ShaderModuleParseResult* parseResult);
+};
+
+}  // namespace dawn::native::webgpu
+
+#endif  // SRC_DAWN_NATIVE_WEBGPU_SHADERMODULEWGPU_H_
diff --git a/src/dawn/tests/end2end/ShaderModuleCachingTests.cpp b/src/dawn/tests/end2end/ShaderModuleCachingTests.cpp
index 5e99e65..e0b201d 100644
--- a/src/dawn/tests/end2end/ShaderModuleCachingTests.cpp
+++ b/src/dawn/tests/end2end/ShaderModuleCachingTests.cpp
@@ -167,7 +167,9 @@
                       OpenGLESBackend({"blob_cache_hash_validation"}),
                       OpenGLESBackend({}, {"blob_cache_hash_validation"}),
                       VulkanBackend({"blob_cache_hash_validation"}),
-                      VulkanBackend({}, {"blob_cache_hash_validation"}));
+                      VulkanBackend({}, {"blob_cache_hash_validation"}),
+                      WebGPUBackend({"blob_cache_hash_validation"}),
+                      WebGPUBackend({}, {"blob_cache_hash_validation"}));
 
 }  // anonymous namespace
 }  // namespace dawn