diff --git a/patches/chromium/.patches b/patches/chromium/.patches index ffdbef87cb5b4..c0f4e5194e293 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -132,4 +132,5 @@ posix_replace_doubleforkandexec_with_forkandspawn.patch cherry-pick-f427936d32db.patch cherry-pick-22c61cfae5d1.patch remove_default_window_title.patch +cherry-pick-22abbad430b6.patch cherry-pick-3cbd5973d704.patch diff --git a/patches/chromium/cherry-pick-22abbad430b6.patch b/patches/chromium/cherry-pick-22abbad430b6.patch new file mode 100644 index 0000000000000..ad2d1f93d6826 --- /dev/null +++ b/patches/chromium/cherry-pick-22abbad430b6.patch @@ -0,0 +1,427 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Austin Eng +Date: Thu, 23 Jun 2022 03:12:49 +0000 +Subject: WebGPU: Mark the context lost on GPU context lost + +M102 merge issues: + - dawn_control_client_holder.h/cc: + GetWGPUInstance() not present in M102 + +Fixes a bug where completely destructing the context instead of +marking it lost when receiving a context lost notification freed +memory still accessible by the page. + +Fixed: 1336014 +Change-Id: I662e531102af91362b4f62700bfbee507fc44d1f +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3708646 +Commit-Queue: Austin Eng +Cr-Commit-Position: refs/heads/main@{#1017003} +(cherry picked from commit 6c7f327b7a15aabd3fc5d57e9c05b95d02f1cd36) + +diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn +index 1c72562d4f58eb77742bfd9539e5d132683949fa..c56507ad59bc111a22c0abd072c1bd21ac3d8808 100644 +--- a/third_party/blink/renderer/modules/BUILD.gn ++++ b/third_party/blink/renderer/modules/BUILD.gn +@@ -662,6 +662,7 @@ source_set("unit_tests") { + "//third_party/blink/renderer/modules/peerconnection:test_support", + "//third_party/blink/renderer/modules/storage:unit_tests", + "//third_party/blink/renderer/modules/webcodecs:unit_tests", ++ "//third_party/blink/renderer/modules/webgpu:unit_tests", + "//third_party/blink/renderer/modules/webtransport:unit_tests", + "//third_party/blink/renderer/platform", + "//third_party/blink/renderer/platform:test_support", +diff --git a/third_party/blink/renderer/modules/webgpu/BUILD.gn b/third_party/blink/renderer/modules/webgpu/BUILD.gn +index dcfece568d0e0e2bc5dbd6383fb1a0c2235a296c..71f2880467c50c145d5a68b4cfac62cba61f8e27 100644 +--- a/third_party/blink/renderer/modules/webgpu/BUILD.gn ++++ b/third_party/blink/renderer/modules/webgpu/BUILD.gn +@@ -91,3 +91,17 @@ blink_modules_sources("webgpu") { + "//third_party/dawn/include/dawn:headers", + ] + } ++ ++source_set("unit_tests") { ++ testonly = true ++ sources = [ "gpu_context_lost_test.cc" ] ++ ++ deps = [ ++ "//base/test:test_support", ++ "//third_party/blink/renderer/controller:blink_bindings_test_sources", ++ "//third_party/blink/renderer/core", ++ "//third_party/blink/renderer/core:testing", ++ "//third_party/blink/renderer/modules", ++ "//third_party/blink/renderer/platform:test_support", ++ ] ++} +diff --git a/third_party/blink/renderer/modules/webgpu/DEPS b/third_party/blink/renderer/modules/webgpu/DEPS +index ded05aa68e59cadddb678d5254119ae3dad1b47e..9e777ab0473f0bb9cbd46e4d6c1498f1a12a6f40 100644 +--- a/third_party/blink/renderer/modules/webgpu/DEPS ++++ b/third_party/blink/renderer/modules/webgpu/DEPS +@@ -9,6 +9,7 @@ include_rules = [ + "+gpu/command_buffer/client/raster_interface.h", + "+gpu/command_buffer/client/shared_image_interface.h", + "+gpu/command_buffer/client/webgpu_interface.h", ++ "+gpu/command_buffer/client/webgpu_interface_stub.h", + "+media/base/video_frame.h", + "+media/renderers/paint_canvas_video_renderer.h", + "+services/metrics/public/cpp/ukm_builders.h", +diff --git a/third_party/blink/renderer/modules/webgpu/gpu.cc b/third_party/blink/renderer/modules/webgpu/gpu.cc +index 8a1f4dcaf605bc656a6499bd415d9db4db9e8ed4..d86dd384e71894f9aaefaf4491c4fc2aabefbea9 100644 +--- a/third_party/blink/renderer/modules/webgpu/gpu.cc ++++ b/third_party/blink/renderer/modules/webgpu/gpu.cc +@@ -287,4 +287,9 @@ void BoxedMappableWGPUBufferHandles::ClearAndDestroyAll( + contents_.clear(); + } + ++void GPU::SetDawnControlClientHolderForTesting( ++ scoped_refptr dawn_control_client) { ++ dawn_control_client_ = std::move(dawn_control_client); ++} ++ + } // namespace blink +diff --git a/third_party/blink/renderer/modules/webgpu/gpu.h b/third_party/blink/renderer/modules/webgpu/gpu.h +index a7348a1c18a2004734275cb4d6f1f4aa68b11d8e..17362a2ee1b3111b52c93ffd0e3222c3de893479 100644 +--- a/third_party/blink/renderer/modules/webgpu/gpu.h ++++ b/third_party/blink/renderer/modules/webgpu/gpu.h +@@ -9,6 +9,7 @@ + #include "third_party/blink/renderer/bindings/core/v8/script_promise.h" + #include "third_party/blink/renderer/core/execution_context/execution_context.h" + #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" ++#include "third_party/blink/renderer/modules/modules_export.h" + #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" + #include "third_party/blink/renderer/platform/supplementable.h" + +@@ -47,9 +48,9 @@ struct BoxedMappableWGPUBufferHandles + HashSet contents_; + }; + +-class GPU final : public ScriptWrappable, +- public Supplement, +- public ExecutionContextLifecycleObserver { ++class MODULES_EXPORT GPU final : public ScriptWrappable, ++ public Supplement, ++ public ExecutionContextLifecycleObserver { + DEFINE_WRAPPERTYPEINFO(); + + public: +@@ -86,6 +87,9 @@ class GPU final : public ScriptWrappable, + return mappable_buffer_handles_.get(); + } + ++ void SetDawnControlClientHolderForTesting( ++ scoped_refptr dawn_control_client); ++ + private: + void OnRequestAdapterCallback(ScriptState* script_state, + const GPURequestAdapterOptions* options, +diff --git a/third_party/blink/renderer/modules/webgpu/gpu_context_lost_test.cc b/third_party/blink/renderer/modules/webgpu/gpu_context_lost_test.cc +new file mode 100644 +index 0000000000000000000000000000000000000000..1063295a543eaee5650cdaf5b115284b8c2f095e +--- /dev/null ++++ b/third_party/blink/renderer/modules/webgpu/gpu_context_lost_test.cc +@@ -0,0 +1,229 @@ ++// Copyright 2022 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "base/callback.h" ++#include "base/run_loop.h" ++#include "base/test/mock_callback.h" ++#include "gpu/command_buffer/client/webgpu_interface_stub.h" ++#include "testing/gmock/include/gmock/gmock.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h" ++#include "third_party/blink/renderer/core/frame/local_dom_window.h" ++#include "third_party/blink/renderer/core/frame/local_frame.h" ++#include "third_party/blink/renderer/core/frame/navigator.h" ++#include "third_party/blink/renderer/core/testing/dummy_page_holder.h" ++#include "third_party/blink/renderer/modules/webgpu/gpu.h" ++#include "third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h" ++#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h" ++ ++namespace blink { ++ ++namespace { ++ ++class WebGPUContextProviderForTest ++ : public WebGraphicsContext3DProviderForTests { ++ public: ++ explicit WebGPUContextProviderForTest( ++ base::MockCallback* destruction_callback) ++ : WebGraphicsContext3DProviderForTests( ++ std::make_unique()), ++ destruction_callback_(destruction_callback) {} ++ ~WebGPUContextProviderForTest() override { ++ if (destruction_callback_) { ++ destruction_callback_->Run(); ++ } ++ } ++ ++ static WebGPUContextProviderForTest* From( ++ scoped_refptr& dawn_control_client) { ++ return static_cast( ++ dawn_control_client->GetContextProviderWeakPtr()->ContextProvider()); ++ } ++ ++ void ClearDestructionCallback() { destruction_callback_ = nullptr; } ++ ++ void SetLostContextCallback( ++ base::RepeatingClosure lost_context_callback) override { ++ lost_context_callback_ = std::move(lost_context_callback); ++ } ++ ++ void CallLostContextCallback() { lost_context_callback_.Run(); } ++ ++ private: ++ base::MockCallback* destruction_callback_; ++ base::RepeatingClosure lost_context_callback_; ++}; ++ ++class WebGPUContextLostTest : public testing::Test { ++ protected: ++ void SetUp() override { page_ = std::make_unique(); } ++ ++ std::tuple SetUpGPU(V8TestingScope* v8_test_scope) { ++ ExecutionContext* execution_context = ++ ExecutionContext::From(v8_test_scope->GetScriptState()); ++ ++ Navigator* navigator = page_->GetFrame().DomWindow()->navigator(); ++ GPU* gpu = MakeGarbageCollected(*navigator); ++ return std::make_tuple(execution_context, gpu); ++ } ++ ++ std::unique_ptr page_; ++}; ++ ++// Test that the context provider is destructed after the last reference to ++// its owning DawnControlClientHolder is dropped. ++TEST_F(WebGPUContextLostTest, DestructedAfterLastRefDropped) { ++ V8TestingScope v8_test_scope; ++ ExecutionContext* execution_context = ++ ExecutionContext::From(v8_test_scope.GetScriptState()); ++ ++ base::MockCallback destruction_callback; ++ auto context_provider = ++ std::make_unique(&destruction_callback); ++ ++ auto dawn_control_client = DawnControlClientHolder::Create( ++ std::move(context_provider), ++ execution_context->GetTaskRunner(TaskType::kWebGPU)); ++ ++ // Drop the last reference to the DawnControlClientHolder which will ++ // now destroy the context provider. ++ EXPECT_CALL(destruction_callback, Run()).Times(1); ++ dawn_control_client = nullptr; ++} ++ ++// Test that the GPU lost context callback marks the context lost, but does not ++// destruct it. ++TEST_F(WebGPUContextLostTest, GPULostContext) { ++ V8TestingScope v8_test_scope; ++ auto [execution_context, gpu] = SetUpGPU(&v8_test_scope); ++ ++ base::MockCallback destruction_callback; ++ auto context_provider = ++ std::make_unique(&destruction_callback); ++ ++ auto dawn_control_client = DawnControlClientHolder::Create( ++ std::move(context_provider), ++ execution_context->GetTaskRunner(TaskType::kWebGPU)); ++ ++ gpu->SetDawnControlClientHolderForTesting(dawn_control_client); ++ ++ // Trigger the lost context callback, but the context should not be destroyed. ++ EXPECT_CALL(destruction_callback, Run()).Times(0); ++ WebGPUContextProviderForTest::From(dawn_control_client) ++ ->CallLostContextCallback(); ++ testing::Mock::VerifyAndClear(&destruction_callback); ++ ++ // The context should be marked lost. ++ EXPECT_TRUE(dawn_control_client->IsContextLost()); ++ ++ // The context provider should still be live. ++ auto context_provider_weak_ptr = ++ dawn_control_client->GetContextProviderWeakPtr(); ++ EXPECT_NE(context_provider_weak_ptr, nullptr); ++ ++ // Clear the destruction callback since it is stack-allocated in this frame. ++ static_cast( ++ context_provider_weak_ptr->ContextProvider()) ++ ->ClearDestructionCallback(); ++} ++ ++// Test that the GPU lost context callback marks the context lost, and then when ++// the context is recreated, the context still lives until the previous ++// DawnControlClientHolder is destroyed. ++TEST_F(WebGPUContextLostTest, RecreatedAfterGPULostContext) { ++ V8TestingScope v8_test_scope; ++ auto [execution_context, gpu] = SetUpGPU(&v8_test_scope); ++ ++ base::MockCallback destruction_callback; ++ auto context_provider = ++ std::make_unique(&destruction_callback); ++ ++ auto dawn_control_client = DawnControlClientHolder::Create( ++ std::move(context_provider), ++ execution_context->GetTaskRunner(TaskType::kWebGPU)); ++ ++ gpu->SetDawnControlClientHolderForTesting(dawn_control_client); ++ ++ // Trigger the lost context callback, but the context should not be destroyed. ++ EXPECT_CALL(destruction_callback, Run()).Times(0); ++ WebGPUContextProviderForTest::From(dawn_control_client) ++ ->CallLostContextCallback(); ++ testing::Mock::VerifyAndClear(&destruction_callback); ++ ++ // The context should be marked lost. ++ EXPECT_TRUE(dawn_control_client->IsContextLost()); ++ ++ // The context provider should still be live. ++ auto context_provider_weak_ptr = ++ dawn_control_client->GetContextProviderWeakPtr(); ++ EXPECT_NE(context_provider_weak_ptr, nullptr); ++ ++ // Make a new context provider and DawnControlClientHolder ++ base::MockCallback destruction_callback2; ++ auto context_provider2 = ++ std::make_unique(&destruction_callback2); ++ ++ auto dawn_control_client2 = DawnControlClientHolder::Create( ++ std::move(context_provider2), ++ execution_context->GetTaskRunner(TaskType::kWebGPU)); ++ ++ // Set the new context, but the previous context should still not be ++ // destroyed. ++ EXPECT_CALL(destruction_callback, Run()).Times(0); ++ gpu->SetDawnControlClientHolderForTesting(dawn_control_client2); ++ testing::Mock::VerifyAndClear(&destruction_callback); ++ ++ // Drop the last reference to the previous DawnControlClientHolder which will ++ // now destroy the previous context provider. ++ EXPECT_CALL(destruction_callback, Run()).Times(1); ++ dawn_control_client = nullptr; ++ testing::Mock::VerifyAndClear(&destruction_callback); ++ ++ // Clear the destruction callback since it is stack-allocated in this frame. ++ static_cast( ++ dawn_control_client2->GetContextProviderWeakPtr()->ContextProvider()) ++ ->ClearDestructionCallback(); ++} ++ ++// Test that ContextDestroyed lifecycle event destructs the context. ++TEST_F(WebGPUContextLostTest, ContextDestroyed) { ++ V8TestingScope v8_test_scope; ++ auto [execution_context, gpu] = SetUpGPU(&v8_test_scope); ++ ++ base::MockCallback destruction_callback; ++ auto context_provider = ++ std::make_unique(&destruction_callback); ++ ++ auto dawn_control_client = DawnControlClientHolder::Create( ++ std::move(context_provider), ++ execution_context->GetTaskRunner(TaskType::kWebGPU)); ++ ++ gpu->SetDawnControlClientHolderForTesting(dawn_control_client); ++ ++ // Trigger the context destroyed lifecycle event. The context should not be ++ // destroyed yet. ++ EXPECT_CALL(destruction_callback, Run()).Times(0); ++ gpu->ContextDestroyed(); ++ testing::Mock::VerifyAndClear(&destruction_callback); ++ ++ // The context should be marked lost. ++ EXPECT_TRUE(dawn_control_client->IsContextLost()); ++ ++ // Getting the context provider should return null. ++ EXPECT_EQ(dawn_control_client->GetContextProviderWeakPtr(), nullptr); ++ ++ // The context is destructed in a posted task with a fresh callstack to avoid ++ // re-entrancy issues. Expectations should resolve by the end of the next ++ // task. ++ EXPECT_CALL(destruction_callback, Run()).Times(1); ++ base::RunLoop loop; ++ execution_context->GetTaskRunner(TaskType::kWebGPU) ++ ->PostTask(FROM_HERE, loop.QuitClosure()); ++ loop.Run(); ++ testing::Mock::VerifyAndClear(&destruction_callback); ++} ++ ++} // namespace ++ ++} // namespace blink +diff --git a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc +index be5b392fa83a4197958f9f032eac4dd41af61f61..6a6283972e7bf597fdf720eefafb57dc4e3263a2 100644 +--- a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc ++++ b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc +@@ -17,9 +17,17 @@ scoped_refptr DawnControlClientHolder::Create( + auto dawn_control_client_holder = + base::MakeRefCounted(std::move(context_provider), + std::move(task_runner)); ++ // The context lost callback occurs when the client receives ++ // OnGpuControlLostContext. This can happen on fatal errors when the GPU ++ // channel is disconnected: the GPU process crashes, the GPU process fails to ++ // deserialize a message, etc. We mark the context lost, but NOT destroy the ++ // entire WebGraphicsContext3DProvider as that would free services for mapping ++ // shared memory. There may still be outstanding mapped GPUBuffers pointing to ++ // this memory. + dawn_control_client_holder->context_provider_->ContextProvider() + ->SetLostContextCallback(WTF::BindRepeating( +- &DawnControlClientHolder::Destroy, dawn_control_client_holder)); ++ &DawnControlClientHolder::MarkContextLost, ++ dawn_control_client_holder->weak_ptr_factory_.GetWeakPtr())); + return dawn_control_client_holder; + } + +@@ -38,7 +46,7 @@ DawnControlClientHolder::DawnControlClientHolder( + DawnControlClientHolder::~DawnControlClientHolder() = default; + + void DawnControlClientHolder::Destroy() { +- api_channel_->Disconnect(); ++ MarkContextLost(); + + // Destroy the WebGPU context. + // This ensures that GPU resources are eagerly reclaimed. +@@ -68,8 +76,16 @@ DawnControlClientHolder::GetContextProviderWeakPtr() const { + return context_provider_->GetWeakPtr(); + } + ++void DawnControlClientHolder::MarkContextLost() { ++ if (context_lost_) { ++ return; ++ } ++ api_channel_->Disconnect(); ++ context_lost_ = true; ++} ++ + bool DawnControlClientHolder::IsContextLost() const { +- return !context_provider_; ++ return context_lost_; + } + + std::unique_ptr +diff --git a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h +index 550038892c97e8f5f46e7a08c07821aa083b67d1..9e3c086225aff661e2402b974534d27860f4f446 100644 +--- a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h ++++ b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h +@@ -47,6 +47,7 @@ class PLATFORM_EXPORT DawnControlClientHolder + base::WeakPtr GetContextProviderWeakPtr() + const; + const DawnProcTable& GetProcs() const { return procs_; } ++ void MarkContextLost(); + bool IsContextLost() const; + std::unique_ptr GetOrCreateCanvasResource( + const SkImageInfo& info, +@@ -56,11 +57,14 @@ class PLATFORM_EXPORT DawnControlClientHolder + friend class RefCounted; + ~DawnControlClientHolder(); + ++ bool context_lost_ = false; + std::unique_ptr context_provider_; + scoped_refptr task_runner_; + scoped_refptr api_channel_; + DawnProcTable procs_; + WebGPURecyclableResourceCache recyclable_resource_cache_; ++ ++ base::WeakPtrFactory weak_ptr_factory_{this}; + }; + + } // namespace blink