[WebGPU] Implement errors for GPURenderPipeline creation master
authorjustin_fan@apple.com <justin_fan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 24 Jul 2019 01:42:37 +0000 (01:42 +0000)
committerjustin_fan@apple.com <justin_fan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 24 Jul 2019 01:42:37 +0000 (01:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=200046

Reviewed by Myles C. Maxfield.

Source/WebCore:

Replacing error logging in GPURenderPipeline creation with GPUError generation.
Update GPUErrorScopes to re-use an error message prefix for less boiler-plate.

Test: webgpu/render-pipeline-errors.html

* Modules/webgpu/WebGPUDevice.cpp:
(WebCore::WebGPUDevice::createRenderPipeline const):
* Modules/webgpu/WebGPURenderPipelineDescriptor.cpp:
(WebCore::WebGPURenderPipelineDescriptor::tryCreateGPURenderPipelineDescriptor const):
* Modules/webgpu/WebGPURenderPipelineDescriptor.h:
* platform/graphics/gpu/GPUDevice.cpp:
(WebCore::GPUDevice::tryCreateRenderPipeline const):
* platform/graphics/gpu/GPUDevice.h:
* platform/graphics/gpu/GPUErrorScopes.cpp:
(WebCore::GPUErrorScopes::generatePrefixedError):
* platform/graphics/gpu/GPUErrorScopes.h:
(WebCore::GPUErrorScopes::setErrorPrefix):
* platform/graphics/gpu/GPURenderPipeline.h:
* platform/graphics/gpu/cocoa/GPURenderPipelineMetal.mm:
(WebCore::tryCreateMtlDepthStencilState):
(WebCore::trySetVertexInput):
(WebCore::trySetColorStates):
(WebCore::trySetMetalFunctions):
(WebCore::trySetFunctions):
(WebCore::convertRenderPipelineDescriptor):
(WebCore::tryCreateMtlRenderPipelineState):
(WebCore::GPURenderPipeline::tryCreate):
(WebCore::GPURenderPipeline::GPURenderPipeline):

LayoutTests:

Add test to cover reproducible render pipeline creation errors.

* webgpu/js/webgpu-functions.js:
(runTestsWithDevice):
* webgpu/render-pipeline-errors-expected.txt: Added.
* webgpu/render-pipeline-errors.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@247764 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
LayoutTests/ChangeLog
LayoutTests/webgpu/js/webgpu-functions.js
LayoutTests/webgpu/render-pipeline-errors-expected.txt [new file with mode: 0644]
LayoutTests/webgpu/render-pipeline-errors.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/webgpu/WebGPUDevice.cpp
Source/WebCore/Modules/webgpu/WebGPURenderPipelineDescriptor.cpp
Source/WebCore/Modules/webgpu/WebGPURenderPipelineDescriptor.h
Source/WebCore/platform/graphics/gpu/GPUDevice.cpp
Source/WebCore/platform/graphics/gpu/GPUDevice.h
Source/WebCore/platform/graphics/gpu/GPUErrorScopes.cpp
Source/WebCore/platform/graphics/gpu/GPUErrorScopes.h
Source/WebCore/platform/graphics/gpu/GPURenderPipeline.h
Source/WebCore/platform/graphics/gpu/cocoa/GPURenderPipelineMetal.mm

index e1e0456..de067d5 100644 (file)
@@ -1,3 +1,17 @@
+2019-07-23  Justin Fan  <justin_fan@apple.com>
+
+        [WebGPU] Implement errors for GPURenderPipeline creation
+        https://bugs.webkit.org/show_bug.cgi?id=200046
+
+        Reviewed by Myles C. Maxfield.
+
+        Add test to cover reproducible render pipeline creation errors.
+
+        * webgpu/js/webgpu-functions.js:
+        (runTestsWithDevice):
+        * webgpu/render-pipeline-errors-expected.txt: Added.
+        * webgpu/render-pipeline-errors.html: Added.
+
 2019-07-23  Said Abou-Hallawa  <sabouhallawa@apple.com>
 
         CanvasRenderingContext2D.setTransfrom() reads only the aliases attributes of DOMMatrix2DInit
index 8a1aef5..2b96abf 100644 (file)
@@ -158,8 +158,10 @@ function runTestsWithDevice(tests) {
             var device = await getBasicDevice();
         } catch (e) { /* WebGPU is not supported. */ }
     
-        for (let name in tests)
-            devicePromiseTest(device, tests[name], name);
+        for (let name in tests) {
+            if (!name.startsWith("_"))
+                devicePromiseTest(device, tests[name], name);
+        }
     });
 }
 
diff --git a/LayoutTests/webgpu/render-pipeline-errors-expected.txt b/LayoutTests/webgpu/render-pipeline-errors-expected.txt
new file mode 100644 (file)
index 0000000..d41fcd1
--- /dev/null
@@ -0,0 +1,12 @@
+
+PASS GPURenderPipeline creation succeeds with no errors. 
+PASS Invalid GPUShaderModule for vertex stage should fail. 
+PASS Invalid GPUShaderModule for fragment stage should fail. 
+PASS Empty entry point for vertex stage should fail. 
+PASS Invalid GPUTextureFormat in GPUColorStateDescriptor should fail. 
+PASS Too many GPUColorStateDescriptors should fail. 
+PASS Too many GPUVertexBufferDescriptors should fail. 
+PASS Too many GPUVertexAttributeDescriptors should fail. 
+PASS Duplicate shaderLocation for vertex attributes should fail. 
+PASS Invalid entry point string should fail. 
+
diff --git a/LayoutTests/webgpu/render-pipeline-errors.html b/LayoutTests/webgpu/render-pipeline-errors.html
new file mode 100644 (file)
index 0000000..a493e11
--- /dev/null
@@ -0,0 +1,190 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test Error Scopes.</title>
+<body>
+<script src="js/webgpu-functions.js"></script>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script>
+const positionLocation = 0;
+const colorLocation = 1;
+
+const colorOffset = 4 * 4;
+const vertexStride = 8 * 4;
+
+const whlslSource = `
+struct FragmentData {
+    float4 position : SV_Position;
+    float4 color : attribute(${colorLocation});
+}
+
+vertex FragmentData vertexMain(float4 position : attribute(${positionLocation}), float4 color : attribute(${colorLocation}))
+{
+    FragmentData out;
+
+    out.position = position;
+    out.color = color;
+
+    return out;
+}
+
+fragment float4 fragmentMain(float4 color : attribute(${colorLocation})) : SV_Target 0
+{
+    return color;
+}
+`;
+
+let goodRenderPipelineDescriptor, goodModule;
+
+let tests = {};
+
+tests["GPURenderPipeline creation succeeds with no errors."] = async device => {
+    goodModule = device.createShaderModule({ code: whlslSource, isWHLSL: true });
+    
+    goodRenderPipelineDescriptor = {
+        vertexStage: { module: goodModule, entryPoint: "vertexMain" },
+        fragmentStage: { module: goodModule, entryPoint: "fragmentMain" },
+        primitiveTopology: "triangle-list",
+        colorStates: [{
+            format: "bgra8unorm",
+            alphaBlend: {},
+            colorBlend: {}
+        }],
+        vertexInput: {
+            vertexBuffers: [{
+                stride: vertexStride,
+                attributeSet: [{
+                    shaderLocation: positionLocation,
+                    format: "float4"
+                }, {
+                    shaderLocation: colorLocation,
+                    offset: colorOffset,
+                    format: "float4"
+                }]
+            }]
+        }
+    };
+    Object.freeze(goodRenderPipelineDescriptor);
+
+    device.pushErrorScope("validation");
+    device.createRenderPipeline(goodRenderPipelineDescriptor);
+    return popNullError(device);
+};
+
+tests["Invalid GPUShaderModule for vertex stage should fail."] = async device => {
+    let badDescriptor = Object.assign({}, goodRenderPipelineDescriptor);
+    let badShaderModule = device.createShaderModule({ code: "Foo", isWHLSL: true });
+    badDescriptor.vertexStage = { module: badShaderModule, entryPoint: "vertexMain" };
+
+    return createPipelineAndError(device, badDescriptor);
+};
+
+tests["Invalid GPUShaderModule for fragment stage should fail."] = async device => {
+    let badDescriptor = Object.assign({}, goodRenderPipelineDescriptor);
+    let badShaderModule = device.createShaderModule({ code: "Foo", isWHLSL: true  });
+    badDescriptor.fragmentStage = { module: badShaderModule, entryPoint: "vertexMain" };
+
+    return createPipelineAndError(device, badDescriptor);
+};
+
+tests["Empty entry point for vertex stage should fail."] = async device => {
+    let badDescriptor = Object.assign({}, goodRenderPipelineDescriptor);
+    let badShaderModule = device.createShaderModule({ code: "Foo", isWHLSL: true  });
+    badDescriptor.vertexStage = { module: badShaderModule, entryPoint: "" };
+
+    return createPipelineAndError(device, badDescriptor);
+};
+
+tests["Invalid GPUTextureFormat in GPUColorStateDescriptor should fail."] = async device => {
+    let badDescriptor = Object.assign({}, goodRenderPipelineDescriptor);
+    badDescriptor.colorStates = [{
+        format: "depth32float-stencil8",
+        alphaBlend: {},
+        colorBlend: {}
+    }];
+
+    return createPipelineAndError(device, badDescriptor);
+};
+
+tests["Too many GPUColorStateDescriptors should fail."] = async device => {
+    let badDescriptor = Object.assign({}, goodRenderPipelineDescriptor);
+    let badColorStates = new Array(100);
+    badColorStates.fill({
+        format: "bgra8unorm",
+        alphaBlend: {},
+        colorBlend: {}
+    });
+    badDescriptor.colorStates = badColorStates;
+
+    return createPipelineAndError(device, badDescriptor);
+};
+
+tests["Too many GPUVertexBufferDescriptors should fail."] = async device => {
+    let badDescriptor = Object.assign({}, goodRenderPipelineDescriptor);
+    let badVertexBuffers = new Array(100);
+    badVertexBuffers.fill({
+        stride: vertexStride,
+        attributeSet: [{
+            shaderLocation: positionLocation,
+            format: "float4"
+        }]
+    });
+    badDescriptor.vertexInput = { vertexBuffers: badVertexBuffers };
+
+    return createPipelineAndError(device, badDescriptor);
+};
+
+tests["Too many GPUVertexAttributeDescriptors should fail."] = async device => {
+    let badDescriptor = Object.assign({}, goodRenderPipelineDescriptor);
+    let badAttributes = new Array(100);
+    badAttributes.fill({
+        shaderLocation: positionLocation,
+        format: "float4"
+    });
+
+    badDescriptor.vertexInput = {
+        vertexBuffers: [{
+            stride: vertexStride,
+            attributeSet: badAttributes
+        }]
+    };
+
+    return createPipelineAndError(device, badDescriptor);
+};
+
+tests["Duplicate shaderLocation for vertex attributes should fail."] = async device => {
+    let badDescriptor = Object.assign({}, goodRenderPipelineDescriptor);
+    badDescriptor.vertexInput = {
+        vertexBuffers: [{
+            stride: vertexStride,
+            attributeSet: [{
+                shaderLocation: colorLocation,
+                format: "float4"
+            }, {
+                shaderLocation: colorLocation,
+                offset: colorOffset,
+                format: "float4"
+            }]
+        }]
+    };
+
+    return createPipelineAndError(device, badDescriptor);
+};
+
+tests["Invalid entry point string should fail."] = async device => {
+    let badDescriptor = Object.assign({}, goodRenderPipelineDescriptor);
+    badDescriptor.fragmentStage = { module: goodModule, entryPoint: "bar" };
+
+    return createPipelineAndError(device, badDescriptor);
+};
+
+runTestsWithDevice(tests);
+
+const createPipelineAndError = (device, descriptor) => {
+    device.pushErrorScope("validation");
+    device.createRenderPipeline(descriptor);
+    return popValidationError(device);
+};
+
+</script>
+</body>
\ No newline at end of file
index fd1e331..e7c58a7 100644 (file)
@@ -1,3 +1,39 @@
+2019-07-23  Justin Fan  <justin_fan@apple.com>
+
+        [WebGPU] Implement errors for GPURenderPipeline creation
+        https://bugs.webkit.org/show_bug.cgi?id=200046
+
+        Reviewed by Myles C. Maxfield.
+
+        Replacing error logging in GPURenderPipeline creation with GPUError generation. 
+        Update GPUErrorScopes to re-use an error message prefix for less boiler-plate.
+
+        Test: webgpu/render-pipeline-errors.html
+
+        * Modules/webgpu/WebGPUDevice.cpp:
+        (WebCore::WebGPUDevice::createRenderPipeline const):
+        * Modules/webgpu/WebGPURenderPipelineDescriptor.cpp:
+        (WebCore::WebGPURenderPipelineDescriptor::tryCreateGPURenderPipelineDescriptor const):
+        * Modules/webgpu/WebGPURenderPipelineDescriptor.h:
+        * platform/graphics/gpu/GPUDevice.cpp:
+        (WebCore::GPUDevice::tryCreateRenderPipeline const):
+        * platform/graphics/gpu/GPUDevice.h:
+        * platform/graphics/gpu/GPUErrorScopes.cpp:
+        (WebCore::GPUErrorScopes::generatePrefixedError):
+        * platform/graphics/gpu/GPUErrorScopes.h:
+        (WebCore::GPUErrorScopes::setErrorPrefix):
+        * platform/graphics/gpu/GPURenderPipeline.h:
+        * platform/graphics/gpu/cocoa/GPURenderPipelineMetal.mm:
+        (WebCore::tryCreateMtlDepthStencilState):
+        (WebCore::trySetVertexInput):
+        (WebCore::trySetColorStates):
+        (WebCore::trySetMetalFunctions):
+        (WebCore::trySetFunctions):
+        (WebCore::convertRenderPipelineDescriptor):
+        (WebCore::tryCreateMtlRenderPipelineState):
+        (WebCore::GPURenderPipeline::tryCreate):
+        (WebCore::GPURenderPipeline::GPURenderPipeline):
+
 2019-07-23  Konstantin Tokarev  <annulen@yandex.ru>
 
         ImageDecoderDirect2D::hotSpot() should return WTF::nullopt instead of default constructed value
index 13a0cf0..9db0b4c 100644 (file)
@@ -154,11 +154,13 @@ Ref<WebGPUShaderModule> WebGPUDevice::createShaderModule(const WebGPUShaderModul
 
 Ref<WebGPURenderPipeline> WebGPUDevice::createRenderPipeline(const WebGPURenderPipelineDescriptor& descriptor) const
 {
-    auto gpuDescriptor = descriptor.tryCreateGPURenderPipelineDescriptor();
+    m_errorScopes->setErrorPrefix("GPUDevice.createRenderPipeline(): ");
+
+    auto gpuDescriptor = descriptor.tryCreateGPURenderPipelineDescriptor(m_errorScopes);
     if (!gpuDescriptor)
         return WebGPURenderPipeline::create(nullptr);
 
-    auto pipeline = m_device->tryCreateRenderPipeline(*gpuDescriptor);
+    auto pipeline = m_device->tryCreateRenderPipeline(*gpuDescriptor, m_errorScopes);
     return WebGPURenderPipeline::create(WTFMove(pipeline));
 }
 
index bd5c5d6..e428b65 100644 (file)
 
 #if ENABLE(WEBGPU)
 
-#include "Logging.h"
+#include "GPUErrorScopes.h"
 
 namespace WebCore {
 
-Optional<GPURenderPipelineDescriptor> WebGPURenderPipelineDescriptor::tryCreateGPURenderPipelineDescriptor() const
+Optional<GPURenderPipelineDescriptor> WebGPURenderPipelineDescriptor::tryCreateGPURenderPipelineDescriptor(GPUErrorScopes& errorScopes) const
 {
     auto pipelineLayout = layout ? makeRefPtr(layout->pipelineLayout()) : nullptr;
 
@@ -43,7 +43,7 @@ Optional<GPURenderPipelineDescriptor> WebGPURenderPipelineDescriptor::tryCreateG
         fragment = fragmentStage->tryCreateGPUPipelineStageDescriptor();
 
     if (!vertex || (fragmentStage && !fragment)) {
-        LOG(WebGPU, "WebGPUDevice::createRenderPipeline(): Invalid GPUPipelineStageDescriptor!");
+        errorScopes.generatePrefixedError("Invalid GPUPipelineStageDescriptor!");
         return WTF::nullopt;
     }
 
index 0bda5d7..b880190 100644 (file)
 
 namespace WebCore {
 
+class GPUErrorScopes;
+
 struct WebGPURenderPipelineDescriptor : WebGPUPipelineDescriptorBase, GPURenderPipelineDescriptorBase {
-    Optional<GPURenderPipelineDescriptor> tryCreateGPURenderPipelineDescriptor() const;
+    Optional<GPURenderPipelineDescriptor> tryCreateGPURenderPipelineDescriptor(GPUErrorScopes&) const;
 
     WebGPUPipelineStageDescriptor vertexStage;
     Optional<WebGPUPipelineStageDescriptor> fragmentStage;
index 6dbf284..30cef68 100644 (file)
@@ -82,9 +82,9 @@ RefPtr<GPUShaderModule> GPUDevice::tryCreateShaderModule(const GPUShaderModuleDe
     return GPUShaderModule::tryCreate(*this, descriptor);
 }
 
-RefPtr<GPURenderPipeline> GPUDevice::tryCreateRenderPipeline(const GPURenderPipelineDescriptor& descriptor) const
+RefPtr<GPURenderPipeline> GPUDevice::tryCreateRenderPipeline(const GPURenderPipelineDescriptor& descriptor, GPUErrorScopes& errorScopes) const
 {
-    return GPURenderPipeline::tryCreate(*this, descriptor);
+    return GPURenderPipeline::tryCreate(*this, descriptor, errorScopes);
 }
 
 RefPtr<GPUComputePipeline> GPUDevice::tryCreateComputePipeline(const GPUComputePipelineDescriptor& descriptor, Ref<GPUErrorScopes>&& errorScopes) const
index 6d19da0..b948c8a 100644 (file)
@@ -78,7 +78,7 @@ public:
     Ref<GPUPipelineLayout> createPipelineLayout(GPUPipelineLayoutDescriptor&&) const;
 
     RefPtr<GPUShaderModule> tryCreateShaderModule(const GPUShaderModuleDescriptor&) const;
-    RefPtr<GPURenderPipeline> tryCreateRenderPipeline(const GPURenderPipelineDescriptor&) const;
+    RefPtr<GPURenderPipeline> tryCreateRenderPipeline(const GPURenderPipelineDescriptor&, GPUErrorScopes&) const;
     RefPtr<GPUComputePipeline> tryCreateComputePipeline(const GPUComputePipelineDescriptor&, Ref<GPUErrorScopes>&&) const;
 
     RefPtr<GPUCommandBuffer> tryCreateCommandBuffer() const;
index fa03d79..c1256a9 100644 (file)
@@ -63,6 +63,11 @@ void GPUErrorScopes::generateError(const String& message, GPUErrorFilter filter)
     iterator->error = createError(filter, message);
 }
 
+void GPUErrorScopes::generatePrefixedError(const String& message, GPUErrorFilter filter)
+{
+    generateError(m_prefix + message, filter);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(WEBGPU)
index e161f70..e9ee6ea 100644 (file)
@@ -43,7 +43,10 @@ public:
 
     void pushErrorScope(GPUErrorFilter);
     Optional<GPUError> popErrorScope(String& failMessage);
+
     void generateError(const String&, GPUErrorFilter = GPUErrorFilter::Validation);
+    void generatePrefixedError(const String&, GPUErrorFilter = GPUErrorFilter::Validation);
+    void setErrorPrefix(const String& prefix) { m_prefix = prefix; }
 
 private:
     struct ErrorScope {
@@ -54,6 +57,7 @@ private:
     GPUErrorScopes() = default;
 
     Vector<ErrorScope> m_errorScopes;
+    String m_prefix;
 };
 
 } // namespace WebCore
index 6e16532..39eec1f 100644 (file)
@@ -27,6 +27,7 @@
 
 #if ENABLE(WEBGPU)
 
+#include "GPUObjectBase.h"
 #include "GPURenderPipelineDescriptor.h"
 #include <wtf/Optional.h>
 #include <wtf/RefCounted.h>
@@ -45,9 +46,9 @@ class GPUDevice;
 using PlatformRenderPipeline = MTLRenderPipelineState;
 using PlatformRenderPipelineSmartPtr = RetainPtr<MTLRenderPipelineState>;
 
-class GPURenderPipeline : public RefCounted<GPURenderPipeline> {
+class GPURenderPipeline : public GPUObjectBase {
 public:
-    static RefPtr<GPURenderPipeline> tryCreate(const GPUDevice&, const GPURenderPipelineDescriptor&);
+    static RefPtr<GPURenderPipeline> tryCreate(const GPUDevice&, const GPURenderPipelineDescriptor&, GPUErrorScopes&);
 
 #if USE(METAL)
     MTLDepthStencilState *depthStencilState() const { return m_depthStencilState.get(); }
@@ -58,7 +59,7 @@ public:
 
 private:
 #if USE(METAL)
-    GPURenderPipeline(RetainPtr<MTLDepthStencilState>&&, PlatformRenderPipelineSmartPtr&&, GPUPrimitiveTopology, Optional<GPUIndexFormat>);
+    GPURenderPipeline(RetainPtr<MTLDepthStencilState>&&, PlatformRenderPipelineSmartPtr&&, GPUPrimitiveTopology, Optional<GPUIndexFormat>, GPUErrorScopes&);
 
     RetainPtr<MTLDepthStencilState> m_depthStencilState;
 #endif // USE(METAL)
index f56661d..a83c0fa 100644 (file)
@@ -32,7 +32,6 @@
 #import "GPULimits.h"
 #import "GPUPipelineMetalConvertLayout.h"
 #import "GPUUtils.h"
-#import "Logging.h"
 #import "WHLSLPrepare.h"
 #import "WHLSLVertexBufferIndexCalculator.h"
 #import <Metal/Metal.h>
 #import <wtf/HashSet.h>
 #import <wtf/OptionSet.h>
 #import <wtf/Optional.h>
+#import <wtf/text/StringConcatenate.h>
 
 namespace WebCore {
 
-static RetainPtr<MTLDepthStencilState> tryCreateMtlDepthStencilState(const char* const functionName, const GPUDepthStencilStateDescriptor& descriptor, const GPUDevice& device)
+static RetainPtr<MTLDepthStencilState> tryCreateMtlDepthStencilState(const GPUDepthStencilStateDescriptor& descriptor, const GPUDevice& device, GPUErrorScopes& errorScopes)
 {
-#if LOG_DISABLED
-    UNUSED_PARAM(functionName);
-#endif
     RetainPtr<MTLDepthStencilDescriptor> mtlDescriptor;
 
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
@@ -58,7 +55,7 @@ static RetainPtr<MTLDepthStencilState> tryCreateMtlDepthStencilState(const char*
     END_BLOCK_OBJC_EXCEPTIONS;
 
     if (!mtlDescriptor) {
-        LOG(WebGPU, "%s: Unable to create MTLDepthStencilDescriptor!", functionName);
+        errorScopes.generatePrefixedError("Unable to create MTLDepthStencilDescriptor!");
         return nullptr;
     }
 
@@ -77,7 +74,7 @@ static RetainPtr<MTLDepthStencilState> tryCreateMtlDepthStencilState(const char*
     END_BLOCK_OBJC_EXCEPTIONS;
 
     if (!state) {
-        LOG(WebGPU, "%s: Error creating MTLDepthStencilState!", functionName);
+        errorScopes.generatePrefixedError("Error creating MTLDepthStencilState!");
         return nullptr;
     }
 
@@ -150,15 +147,12 @@ static MTLVertexStepFunction mtlStepFunctionForGPUInputStepMode(GPUInputStepMode
 // FIXME: Move this into GPULimits when that is implemented properly.
 constexpr unsigned maxVertexAttributes = 16;
 
-static bool trySetVertexInput(const char* const functionName, const GPUVertexInputDescriptor& descriptor, MTLRenderPipelineDescriptor *mtlDescriptor, Optional<WHLSL::RenderPipelineDescriptor>& whlslDescriptor)
+static bool trySetVertexInput(const GPUVertexInputDescriptor& descriptor, MTLRenderPipelineDescriptor *mtlDescriptor, Optional<WHLSL::RenderPipelineDescriptor>& whlslDescriptor, GPUErrorScopes& errorScopes)
 {
-#if LOG_DISABLED
-    UNUSED_PARAM(functionName);
-#endif
     const auto& buffers = descriptor.vertexBuffers;
 
     if (buffers.size() > maxVertexBuffers) {
-        LOG(WebGPU, "%s: Too many vertex input buffers!", functionName);
+        errorScopes.generatePrefixedError("Too many GPUVertexBufferDescriptors!");
         return false;
     }
 
@@ -178,13 +172,13 @@ static bool trySetVertexInput(const char* const functionName, const GPUVertexInp
         const auto& attributes = buffers[index]->attributeSet;
 
         if (attributes.size() + attributeIndex > maxVertexAttributes) {
-            LOG(WebGPU, "%s: Too many vertex attributes!", functionName);
+            errorScopes.generatePrefixedError("Too many GPUVertexAttributeDescriptors!");
             return false;
         }
 
         NSUInteger inputStride = 0;
         if (!WTF::convertSafely(buffers[index]->stride, inputStride)) {
-            LOG(WebGPU, "%s: Stride for vertex input buffer %u is too large!", functionName, index);
+            errorScopes.generatePrefixedError(makeString("Stride for GPUVertexBufferDescriptor ", index, " is too large!"));
             return false;
         }
 
@@ -198,13 +192,13 @@ static bool trySetVertexInput(const char* const functionName, const GPUVertexInp
 
         for (const auto& attribute : attributes) {
             if (!locations.add(attribute.shaderLocation).isNewEntry) {
-                LOG(WebGPU, "%s: Duplicate shaderLocation %u for vertex attribute!", functionName, attribute.shaderLocation);
+                errorScopes.generatePrefixedError(makeString("Duplicate shaderLocation ", attribute.shaderLocation, " for vertex attribute!"));
                 return false;
             }
 
             NSUInteger offset = 0;
             if (!WTF::convertSafely(attribute.offset, offset)) {
-                LOG(WebGPU, "%s: Buffer offset for vertex attribute %u is too large!", functionName, attribute.shaderLocation);
+                errorScopes.generatePrefixedError(makeString("Buffer offset for vertex attribute ", attribute.shaderLocation, " is too large!"));
                 return false;
             }
 
@@ -299,14 +293,11 @@ static MTLBlendFactor mtlBlendFactorForGPUBlendFactor(GPUBlendFactor factor)
     ASSERT_NOT_REACHED();
 }
 
-static bool trySetColorStates(const char* const functionName, const Vector<GPUColorStateDescriptor>& colorStates, MTLRenderPipelineColorAttachmentDescriptorArray* array, Optional<WHLSL::RenderPipelineDescriptor>& whlslDescriptor)
+static bool trySetColorStates(const Vector<GPUColorStateDescriptor>& colorStates, MTLRenderPipelineColorAttachmentDescriptorArray* array, Optional<WHLSL::RenderPipelineDescriptor>& whlslDescriptor, GPUErrorScopes& errorScopes)
 {
-#if LOG_DISABLED
-    UNUSED_PARAM(functionName);
-#endif
     // FIXME: Replace with maximum number of color attachments per render pass from GPULimits.
     if (colorStates.size() > 4) {
-        LOG(WebGPU, "%s: Invalid number of GPUColorStateDescriptors!", functionName);
+        errorScopes.generatePrefixedError("Too many GPUColorStateDescriptors!");
         return false;
     }
 
@@ -329,7 +320,7 @@ static bool trySetColorStates(const char* const functionName, const Vector<GPUCo
             if (auto format = convertTextureFormat(state.format))
                 whlslDescriptor->attachmentsStateDescriptor.attachmentDescriptors.append({*format, i});
             else {
-                LOG(WebGPU, "%s: Invalid texture format for color attachment %u!", functionName, i);
+                errorScopes.generatePrefixedError(makeString("Invalid GPUTextureFormat for GPUColorStateDescriptor ", i, "!"));
                 return false;
             }
         }
@@ -340,24 +331,20 @@ static bool trySetColorStates(const char* const functionName, const Vector<GPUCo
     return true;
 }
 
-static bool trySetMetalFunctions(const char* const functionName, MTLLibrary *vertexMetalLibrary, MTLLibrary *fragmentMetalLibrary, MTLRenderPipelineDescriptor *mtlDescriptor, const String& vertexEntryPointName, const String& fragmentEntryPointName)
+static bool trySetMetalFunctions(MTLLibrary *vertexMetalLibrary, MTLLibrary *fragmentMetalLibrary, MTLRenderPipelineDescriptor *mtlDescriptor, const String& vertexEntryPointName, const String& fragmentEntryPointName, GPUErrorScopes& errorScopes)
 {
-#if LOG_DISABLED
-    UNUSED_PARAM(functionName);
-#endif
-
     {
         BEGIN_BLOCK_OBJC_EXCEPTIONS;
 
         // Metal requires a vertex shader in all render pipelines.
         if (!vertexMetalLibrary) {
-            LOG(WebGPU, "%s: MTLLibrary for vertex stage does not exist!", functionName);
+            errorScopes.generatePrefixedError("MTLLibrary for vertex stage does not exist!");
             return false;
         }
 
         auto function = adoptNS([vertexMetalLibrary newFunctionWithName:vertexEntryPointName]);
         if (!function) {
-            LOG(WebGPU, "%s: Cannot create vertex MTLFunction \"%s\"!", functionName, vertexEntryPointName.utf8().data());
+            errorScopes.generatePrefixedError(makeString("Cannot create vertex MTLFunction \"", vertexEntryPointName, "\"!"));
             return false;
         }
 
@@ -376,7 +363,7 @@ static bool trySetMetalFunctions(const char* const functionName, MTLLibrary *ver
         auto function = adoptNS([fragmentMetalLibrary newFunctionWithName:fragmentEntryPointName]);
 
         if (!function) {
-            LOG(WebGPU, "%s: Cannot create fragment MTLFunction \"%s\"!", functionName, fragmentEntryPointName.utf8().data());
+            errorScopes.generatePrefixedError(makeString("Cannot create fragment MTLFunction \"", fragmentEntryPointName, "\"!"));
             return false;
         }
 
@@ -389,11 +376,8 @@ static bool trySetMetalFunctions(const char* const functionName, MTLLibrary *ver
     return false;
 }
 
-static bool trySetFunctions(const char* const functionName, const GPUPipelineStageDescriptor& vertexStage, const Optional<GPUPipelineStageDescriptor>& fragmentStage, const GPUDevice& device, MTLRenderPipelineDescriptor* mtlDescriptor, Optional<WHLSL::RenderPipelineDescriptor>& whlslDescriptor)
+static bool trySetFunctions(const GPUPipelineStageDescriptor& vertexStage, const Optional<GPUPipelineStageDescriptor>& fragmentStage, const GPUDevice& device, MTLRenderPipelineDescriptor* mtlDescriptor, Optional<WHLSL::RenderPipelineDescriptor>& whlslDescriptor, GPUErrorScopes& errorScopes)
 {
-#if LOG_DISABLED
-    UNUSED_PARAM(functionName);
-#endif
     RetainPtr<MTLLibrary> vertexLibrary, fragmentLibrary;
     String vertexEntryPoint, fragmentEntryPoint;
 
@@ -407,8 +391,10 @@ static bool trySetFunctions(const char* const functionName, const GPUPipelineSta
             whlslDescriptor->fragmentEntryPointName = fragmentStage->entryPoint;
 
         auto whlslCompileResult = WHLSL::prepare(whlslSource, *whlslDescriptor);
-        if (!whlslCompileResult)
+        if (!whlslCompileResult) {
+            errorScopes.generatePrefixedError("WHLSL compilation failed!");
             return false;
+        }
 
         NSError *error = nil;
 
@@ -416,10 +402,13 @@ static bool trySetFunctions(const char* const functionName, const GPUPipelineSta
         vertexLibrary = adoptNS([device.platformDevice() newLibraryWithSource:whlslCompileResult->metalSource options:nil error:&error]);
         END_BLOCK_OBJC_EXCEPTIONS;
 
+        if (!vertexLibrary && error) {
+            errorScopes.generatePrefixedError(error.localizedDescription.UTF8String);
 #ifndef NDEBUG
-        if (!vertexLibrary)
             NSLog(@"%@", error);
 #endif
+        }
+
         ASSERT(vertexLibrary);
         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=195771 Once we zero-fill variables, there should be no warnings, so we should be able to ASSERT(!error) here.
 
@@ -435,10 +424,10 @@ static bool trySetFunctions(const char* const functionName, const GPUPipelineSta
         }
     }
 
-    return trySetMetalFunctions(functionName, vertexLibrary.get(), fragmentLibrary.get(), mtlDescriptor, vertexEntryPoint, fragmentEntryPoint);
+    return trySetMetalFunctions(vertexLibrary.get(), fragmentLibrary.get(), mtlDescriptor, vertexEntryPoint, fragmentEntryPoint, errorScopes);
 }
 
-static RetainPtr<MTLRenderPipelineDescriptor> convertRenderPipelineDescriptor(const char* const functionName, const GPURenderPipelineDescriptor& descriptor, const GPUDevice& device)
+static RetainPtr<MTLRenderPipelineDescriptor> convertRenderPipelineDescriptor(const GPURenderPipelineDescriptor& descriptor, const GPUDevice& device, GPUErrorScopes& errorScopes)
 {
     RetainPtr<MTLRenderPipelineDescriptor> mtlDescriptor;
 
@@ -449,7 +438,7 @@ static RetainPtr<MTLRenderPipelineDescriptor> convertRenderPipelineDescriptor(co
     END_BLOCK_OBJC_EXCEPTIONS;
 
     if (!mtlDescriptor) {
-        LOG(WebGPU, "%s: Error creating MTLDescriptor!", functionName);
+        errorScopes.generatePrefixedError("Error creating MTLDescriptor!");
         return nullptr;
     }
 
@@ -465,35 +454,35 @@ static RetainPtr<MTLRenderPipelineDescriptor> convertRenderPipelineDescriptor(co
     if (isWhlsl)
         whlslDescriptor = WHLSL::RenderPipelineDescriptor();
 
-    if (!trySetVertexInput(functionName, descriptor.vertexInput, mtlDescriptor.get(), whlslDescriptor))
+    if (!trySetVertexInput(descriptor.vertexInput, mtlDescriptor.get(), whlslDescriptor, errorScopes))
         return nullptr;
 
-    if (!trySetColorStates(functionName, descriptor.colorStates, mtlDescriptor.get().colorAttachments, whlslDescriptor))
+    if (!trySetColorStates(descriptor.colorStates, mtlDescriptor.get().colorAttachments, whlslDescriptor, errorScopes))
         return nullptr;
 
     if (descriptor.layout && whlslDescriptor) {
         if (auto layout = convertLayout(*descriptor.layout))
             whlslDescriptor->layout = WTFMove(*layout);
         else {
-            LOG(WebGPU, "%s: Error converting GPUPipelineLayout!", functionName);
+            errorScopes.generatePrefixedError("Error converting GPUPipelineLayout!");
             return nullptr;
         }
     }
 
-    if (!trySetFunctions(functionName, vertexStage, fragmentStage, device, mtlDescriptor.get(), whlslDescriptor))
+    if (!trySetFunctions(vertexStage, fragmentStage, device, mtlDescriptor.get(), whlslDescriptor, errorScopes))
         return nullptr;
 
     return mtlDescriptor;
 }
 
-static RetainPtr<MTLRenderPipelineState> tryCreateMtlRenderPipelineState(const char* const functionName, const GPURenderPipelineDescriptor& descriptor, const GPUDevice& device)
+static RetainPtr<MTLRenderPipelineState> tryCreateMtlRenderPipelineState(const GPURenderPipelineDescriptor& descriptor, const GPUDevice& device, GPUErrorScopes& errorScopes)
 {
     if (!device.platformDevice()) {
-        LOG(WebGPU, "GPUComputePipeline::tryCreate(): Invalid GPUDevice!");
+        errorScopes.generatePrefixedError("Invalid GPUDevice!");
         return nullptr;
     }
 
-    auto mtlDescriptor = convertRenderPipelineDescriptor(functionName, descriptor, device);
+    auto mtlDescriptor = convertRenderPipelineDescriptor(descriptor, device, errorScopes);
     if (!mtlDescriptor)
         return nullptr;
 
@@ -504,38 +493,37 @@ static RetainPtr<MTLRenderPipelineState> tryCreateMtlRenderPipelineState(const c
     NSError *error = nil;
     pipeline = adoptNS([device.platformDevice() newRenderPipelineStateWithDescriptor:mtlDescriptor.get() error:&error]);
     if (!pipeline)
-        LOG(WebGPU, "%s: %s!", functionName, error.localizedDescription.UTF8String);
+        errorScopes.generatePrefixedError(error.localizedDescription.UTF8String);
 
     END_BLOCK_OBJC_EXCEPTIONS;
 
     return pipeline;
 }
 
-RefPtr<GPURenderPipeline> GPURenderPipeline::tryCreate(const GPUDevice& device, const GPURenderPipelineDescriptor& descriptor)
+RefPtr<GPURenderPipeline> GPURenderPipeline::tryCreate(const GPUDevice& device, const GPURenderPipelineDescriptor& descriptor, GPUErrorScopes& errorScopes)
 {
-    const char* const functionName = "GPURenderPipeline::create()";
-
     if (!device.platformDevice()) {
-        LOG(WebGPU, "%s: Invalid GPUDevice!", functionName);
+        errorScopes.generatePrefixedError("Invalid GPUDevice!");
         return nullptr;
     }
 
     RetainPtr<MTLDepthStencilState> depthStencil;
 
-    if (descriptor.depthStencilState && !(depthStencil = tryCreateMtlDepthStencilState(functionName, *descriptor.depthStencilState, device)))
+    if (descriptor.depthStencilState && !(depthStencil = tryCreateMtlDepthStencilState(*descriptor.depthStencilState, device, errorScopes)))
         return nullptr;
 
     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198387 depthStencilAttachmentDescriptor isn't implemented yet for WHLSL compiler.
 
-    auto pipeline = tryCreateMtlRenderPipelineState(functionName, descriptor, device);
+    auto pipeline = tryCreateMtlRenderPipelineState(descriptor, device, errorScopes);
     if (!pipeline)
         return nullptr;
 
-    return adoptRef(new GPURenderPipeline(WTFMove(depthStencil), WTFMove(pipeline), descriptor.primitiveTopology, descriptor.vertexInput.indexFormat));
+    return adoptRef(new GPURenderPipeline(WTFMove(depthStencil), WTFMove(pipeline), descriptor.primitiveTopology, descriptor.vertexInput.indexFormat, errorScopes));
 }
 
-GPURenderPipeline::GPURenderPipeline(RetainPtr<MTLDepthStencilState>&& depthStencil, RetainPtr<MTLRenderPipelineState>&& pipeline, GPUPrimitiveTopology topology, Optional<GPUIndexFormat> format)
-    : m_depthStencilState(WTFMove(depthStencil))
+GPURenderPipeline::GPURenderPipeline(RetainPtr<MTLDepthStencilState>&& depthStencil, RetainPtr<MTLRenderPipelineState>&& pipeline, GPUPrimitiveTopology topology, Optional<GPUIndexFormat> format, GPUErrorScopes& errorScopes)
+    : GPUObjectBase(makeRef(errorScopes))
+    , m_depthStencilState(WTFMove(depthStencil))
     , m_platformRenderPipeline(WTFMove(pipeline))
     , m_primitiveTopology(topology)
     , m_indexFormat(format)