diff options
Diffstat (limited to 'chromium/third_party/blink/renderer')
10 files changed, 286 insertions, 6 deletions
diff --git a/chromium/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/chromium/third_party/blink/renderer/core/display_lock/display_lock_context.cc index 918c6b7350d..4114d83a195 100644 --- a/chromium/third_party/blink/renderer/core/display_lock/display_lock_context.cc +++ b/chromium/third_party/blink/renderer/core/display_lock/display_lock_context.cc @@ -998,6 +998,9 @@ bool DisplayLockContext::ForceUnlockIfNeeded() { layout_invalidation_reason::kDisplayLock); } } + // If we forced unlock, then we need to prevent subsequent calls to + // Lock() until the next frame. + SetRequestedState(EContentVisibility::kVisible); } return true; } diff --git a/chromium/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc b/chromium/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc index 69f6da14d17..d15636e1ea2 100644 --- a/chromium/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc +++ b/chromium/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc @@ -549,6 +549,9 @@ FontSelector* OffscreenCanvas::GetFontSelector() { if (auto* window = DynamicTo<LocalDOMWindow>(GetExecutionContext())) { return window->document()->GetStyleEngine().GetFontSelector(); } + // TODO(crbug.com/1334864): Temporary mitigation. Remove the following + // CHECK once a more comprehensive solution has been implemented. + CHECK(GetExecutionContext()->IsWorkerGlobalScope()); return To<WorkerGlobalScope>(GetExecutionContext())->GetFontSelector(); } diff --git a/chromium/third_party/blink/renderer/modules/BUILD.gn b/chromium/third_party/blink/renderer/modules/BUILD.gn index 818e3e38fe4..f9428f65f47 100644 --- a/chromium/third_party/blink/renderer/modules/BUILD.gn +++ b/chromium/third_party/blink/renderer/modules/BUILD.gn @@ -663,6 +663,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/chromium/third_party/blink/renderer/modules/webgpu/BUILD.gn b/chromium/third_party/blink/renderer/modules/webgpu/BUILD.gn index e7d4345b4a2..87ffc91763d 100644 --- a/chromium/third_party/blink/renderer/modules/webgpu/BUILD.gn +++ b/chromium/third_party/blink/renderer/modules/webgpu/BUILD.gn @@ -93,3 +93,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/chromium/third_party/blink/renderer/modules/webgpu/DEPS b/chromium/third_party/blink/renderer/modules/webgpu/DEPS index c7774f8346b..1238fddbe36 100644 --- a/chromium/third_party/blink/renderer/modules/webgpu/DEPS +++ b/chromium/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/base/wait_and_replace_sync_token_client.h", "+media/renderers/paint_canvas_video_renderer.h", diff --git a/chromium/third_party/blink/renderer/modules/webgpu/gpu.cc b/chromium/third_party/blink/renderer/modules/webgpu/gpu.cc index faa292a7723..c7516603b90 100644 --- a/chromium/third_party/blink/renderer/modules/webgpu/gpu.cc +++ b/chromium/third_party/blink/renderer/modules/webgpu/gpu.cc @@ -288,4 +288,9 @@ void BoxedMappableWGPUBufferHandles::ClearAndDestroyAll( contents_.clear(); } +void GPU::SetDawnControlClientHolderForTesting( + scoped_refptr<DawnControlClientHolder> dawn_control_client) { + dawn_control_client_ = std::move(dawn_control_client); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/webgpu/gpu.h b/chromium/third_party/blink/renderer/modules/webgpu/gpu.h index 4477129d951..606a331ad18 100644 --- a/chromium/third_party/blink/renderer/modules/webgpu/gpu.h +++ b/chromium/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/heap/collection_support/heap_hash_set.h" #include "third_party/blink/renderer/platform/supplementable.h" @@ -48,9 +49,9 @@ struct BoxedMappableWGPUBufferHandles HashSet<void*> contents_; }; -class GPU final : public ScriptWrappable, - public Supplement<NavigatorBase>, - public ExecutionContextLifecycleObserver { +class MODULES_EXPORT GPU final : public ScriptWrappable, + public Supplement<NavigatorBase>, + public ExecutionContextLifecycleObserver { DEFINE_WRAPPERTYPEINFO(); public: @@ -87,6 +88,9 @@ class GPU final : public ScriptWrappable, return mappable_buffer_handles_.get(); } + void SetDawnControlClientHolderForTesting( + scoped_refptr<DawnControlClientHolder> dawn_control_client); + private: void OnRequestAdapterCallback(ScriptState* script_state, const GPURequestAdapterOptions* options, diff --git a/chromium/third_party/blink/renderer/modules/webgpu/gpu_context_lost_test.cc b/chromium/third_party/blink/renderer/modules/webgpu/gpu_context_lost_test.cc new file mode 100644 index 00000000000..1063295a543 --- /dev/null +++ b/chromium/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<base::OnceClosure>* destruction_callback) + : WebGraphicsContext3DProviderForTests( + std::make_unique<gpu::webgpu::WebGPUInterfaceStub>()), + destruction_callback_(destruction_callback) {} + ~WebGPUContextProviderForTest() override { + if (destruction_callback_) { + destruction_callback_->Run(); + } + } + + static WebGPUContextProviderForTest* From( + scoped_refptr<DawnControlClientHolder>& dawn_control_client) { + return static_cast<WebGPUContextProviderForTest*>( + 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<base::OnceClosure>* destruction_callback_; + base::RepeatingClosure lost_context_callback_; +}; + +class WebGPUContextLostTest : public testing::Test { + protected: + void SetUp() override { page_ = std::make_unique<DummyPageHolder>(); } + + std::tuple<ExecutionContext*, GPU*> SetUpGPU(V8TestingScope* v8_test_scope) { + ExecutionContext* execution_context = + ExecutionContext::From(v8_test_scope->GetScriptState()); + + Navigator* navigator = page_->GetFrame().DomWindow()->navigator(); + GPU* gpu = MakeGarbageCollected<GPU>(*navigator); + return std::make_tuple(execution_context, gpu); + } + + std::unique_ptr<DummyPageHolder> 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<base::OnceClosure> destruction_callback; + auto context_provider = + std::make_unique<WebGPUContextProviderForTest>(&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<base::OnceClosure> destruction_callback; + auto context_provider = + std::make_unique<WebGPUContextProviderForTest>(&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<WebGPUContextProviderForTest*>( + 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<base::OnceClosure> destruction_callback; + auto context_provider = + std::make_unique<WebGPUContextProviderForTest>(&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<base::OnceClosure> destruction_callback2; + auto context_provider2 = + std::make_unique<WebGPUContextProviderForTest>(&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<WebGPUContextProviderForTest*>( + 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<base::OnceClosure> destruction_callback; + auto context_provider = + std::make_unique<WebGPUContextProviderForTest>(&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/chromium/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc index be5b392fa83..6a6283972e7 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc @@ -17,9 +17,17 @@ scoped_refptr<DawnControlClientHolder> DawnControlClientHolder::Create( auto dawn_control_client_holder = base::MakeRefCounted<DawnControlClientHolder>(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<RecyclableCanvasResource> diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h index 550038892c9..9e3c086225a 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h @@ -47,6 +47,7 @@ class PLATFORM_EXPORT DawnControlClientHolder base::WeakPtr<WebGraphicsContext3DProviderWrapper> GetContextProviderWeakPtr() const; const DawnProcTable& GetProcs() const { return procs_; } + void MarkContextLost(); bool IsContextLost() const; std::unique_ptr<RecyclableCanvasResource> GetOrCreateCanvasResource( const SkImageInfo& info, @@ -56,11 +57,14 @@ class PLATFORM_EXPORT DawnControlClientHolder friend class RefCounted<DawnControlClientHolder>; ~DawnControlClientHolder(); + bool context_lost_ = false; std::unique_ptr<WebGraphicsContext3DProviderWrapper> context_provider_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; scoped_refptr<gpu::webgpu::APIChannel> api_channel_; DawnProcTable procs_; WebGPURecyclableResourceCache recyclable_resource_cache_; + + base::WeakPtrFactory<DawnControlClientHolder> weak_ptr_factory_{this}; }; } // namespace blink |