diff --git a/src/content/test/data/gpu/webgl-overly-large-uniform.html b/src/content/test/data/gpu/webgl-overly-large-uniform.html new file mode 100644 index 0000000000000..9b28fa5ebc2d6 --- /dev/null +++ b/src/content/test/data/gpu/webgl-overly-large-uniform.html @@ -0,0 +1,137 @@ + +
+ + + + + + + + diff --git a/src/content/test/gpu/gpu_tests/context_lost_integration_test.py b/src/content/test/gpu/gpu_tests/context_lost_integration_test.py index 9096205323b23..315338ca8795a 100755 --- a/src/content/test/gpu/gpu_tests/context_lost_integration_test.py +++ b/src/content/test/gpu/gpu_tests/context_lost_integration_test.py @@ -160,6 +160,8 @@ class ContextLostIntegrationTest(gpu_integration_test.GpuIntegrationTest): 'webgl_with_select_element.html'), ('ContextLost_WebGLContextLostInHiddenTab', 'webgl.html?query=kill_after_notification'), + ('ContextLost_WebGLContextLostOverlyLargeUniform', + 'webgl-overly-large-uniform.html'), ('ContextLost_WebGLBlockedAfterJSNavigation', 'webgl-domain-blocking-page1.html'), ('ContextLost_WebGLUnblockedAfterUserInitiatedReload', @@ -387,6 +389,16 @@ class ContextLostIntegrationTest(gpu_integration_test.GpuIntegrationTest): tab.Activate() self._WaitForTabAndCheckCompletion() + def _ContextLost_WebGLContextLostOverlyLargeUniform(self, + test_path: str) -> None: + self.RestartBrowserIfNecessaryWithArgs([ + cba.DISABLE_DOMAIN_BLOCKING_FOR_3D_APIS, + '--enable-features=DisableArrayBufferSizeLimitsForTesting' + ]) + self._NavigateAndWaitForLoad(test_path) + # No reason to wait more than 10 seconds for this test to complete. + self._WaitForTabAndCheckCompletion(timeout=10) + def _ContextLost_WebGLBlockedAfterJSNavigation(self, test_path): self.RestartBrowserIfNecessaryWithArgs([]) self._NavigateAndWaitForLoad(test_path) diff --git a/src/gpu/command_buffer/client/client_discardable_manager_unittest.cc b/src/gpu/command_buffer/client/client_discardable_manager_unittest.cc index 4b86fca0a696d..026ee81f42bdc 100644 --- a/src/gpu/command_buffer/client/client_discardable_manager_unittest.cc +++ b/src/gpu/command_buffer/client/client_discardable_manager_unittest.cc @@ -45,6 +45,9 @@ class FakeCommandBuffer : public CommandBuffer { EXPECT_TRUE(found != active_ids_.end()); active_ids_.erase(found); } + void ForceLostContext(error::ContextLostReason reason) override { + // No-op; doesn't need to be exercised here. + } private: int32_t next_id_ = 1; diff --git a/src/gpu/command_buffer/client/client_test_helper.cc b/src/gpu/command_buffer/client/client_test_helper.cc index 4fd55a3274a87..826f466f40cfa 100644 --- a/src/gpu/command_buffer/client/client_test_helper.cc +++ b/src/gpu/command_buffer/client/client_test_helper.cc @@ -158,6 +158,13 @@ void MockClientCommandBuffer::DelegateToFake() { this, &FakeCommandBufferServiceBase::DestroyTransferBufferHelper)); } +void MockClientCommandBuffer::ForceLostContext( + error::ContextLostReason reason) { + // TODO(kbr): add a test for a call to this method. + SetParseError(error::kLostContext); + SetContextLostReason(reason); +} + MockClientCommandBufferMockFlush::MockClientCommandBufferMockFlush() { DelegateToFake(); } diff --git a/src/gpu/command_buffer/client/client_test_helper.h b/src/gpu/command_buffer/client/client_test_helper.h index bd0cc0739efaa..2548be832472a 100644 --- a/src/gpu/command_buffer/client/client_test_helper.h +++ b/src/gpu/command_buffer/client/client_test_helper.h @@ -86,6 +86,8 @@ class MockClientCommandBuffer : public CommandBuffer, void SetTokenForSetGetBuffer(int32_t token) { token_ = token; } + void ForceLostContext(error::ContextLostReason reason) override; + private: int32_t put_offset_ = 0; int32_t token_ = 10000; // All token checks in the tests should pass. diff --git a/src/gpu/command_buffer/client/cmd_buffer_helper.cc b/src/gpu/command_buffer/client/cmd_buffer_helper.cc index 620fa6535a890..5d9d1c23dddf7 100644 --- a/src/gpu/command_buffer/client/cmd_buffer_helper.cc +++ b/src/gpu/command_buffer/client/cmd_buffer_helper.cc @@ -157,7 +157,8 @@ void CommandBufferHelper::UpdateCachedState(const CommandBuffer::State& state) { (state.set_get_buffer_count != set_get_buffer_count_); cached_get_offset_ = service_on_old_buffer_ ? 0 : state.get_offset; cached_last_token_read_ = state.token; - context_lost_ = error::IsError(state.error); + // Don't transition from a lost context to a working context. + context_lost_ |= error::IsError(state.error); } bool CommandBufferHelper::WaitForGetOffsetInRange(int32_t start, int32_t end) { @@ -296,7 +297,6 @@ void CommandBufferHelper::WaitForAvailableEntries(int32_t count) { if (!AllocateRingBuffer()) return; DCHECK(HaveRingBuffer()); - DCHECK(count < total_entry_count_); if (put_ + count > total_entry_count_) { // There's not enough room between the current put and the end of the // buffer, so we need to wrap. We will add noops all the way to the end, @@ -344,7 +344,16 @@ void CommandBufferHelper::WaitForAvailableEntries(int32_t count) { put_)) return; CalcImmediateEntries(count); - DCHECK_GE(immediate_entry_count_, count); + if (immediate_entry_count_ < count) { + // Tell the underlying command buffer to signal a lost context to higher + // levels. + command_buffer_->ForceLostContext(error::kGuilty); + // Free the ring buffer and lose context. + FreeRingBuffer(); + usable_ = false; + context_lost_ = true; + return; + } } } } diff --git a/src/gpu/command_buffer/common/command_buffer.h b/src/gpu/command_buffer/common/command_buffer.h index 6335c4757f2e2..5ac9069424fe3 100644 --- a/src/gpu/command_buffer/common/command_buffer.h +++ b/src/gpu/command_buffer/common/command_buffer.h @@ -125,6 +125,10 @@ class GPU_EXPORT CommandBuffer { // before it is safe to call this function to destroy it. virtual void DestroyTransferBuffer(int32_t id) = 0; + // Forcibly lose this context. Used by higher-level code when it determines + // the necessity to do so. Has no effect if the context has already been lost. + virtual void ForceLostContext(error::ContextLostReason reason) = 0; + private: DISALLOW_COPY_AND_ASSIGN(CommandBuffer); }; diff --git a/src/gpu/command_buffer/service/command_buffer_direct.cc b/src/gpu/command_buffer/service/command_buffer_direct.cc index e32139b6bfd65..6a7f7e55487ef 100644 --- a/src/gpu/command_buffer/service/command_buffer_direct.cc +++ b/src/gpu/command_buffer/service/command_buffer_direct.cc @@ -63,6 +63,11 @@ void CommandBufferDirect::DestroyTransferBuffer(int32_t id) { service_.DestroyTransferBuffer(id); } +void CommandBufferDirect::ForceLostContext(error::ContextLostReason reason) { + service_.SetContextLostReason(reason); + service_.SetParseError(error::kLostContext); +} + CommandBufferServiceClient::CommandBatchProcessedResult CommandBufferDirect::OnCommandBatchProcessed() { return kContinueExecution; diff --git a/src/gpu/command_buffer/service/command_buffer_direct.h b/src/gpu/command_buffer/service/command_buffer_direct.h index d03ac687b6f4b..4d8175c3f56fa 100644 --- a/src/gpu/command_buffer/service/command_buffer_direct.h +++ b/src/gpu/command_buffer/service/command_buffer_direct.h @@ -40,6 +40,7 @@ class GPU_EXPORT CommandBufferDirect : public CommandBuffer, TransferBufferAllocationOption option = TransferBufferAllocationOption::kLoseContextOnOOM) override; void DestroyTransferBuffer(int32_t id) override; + void ForceLostContext(error::ContextLostReason reason) override; // CommandBufferServiceClient implementation: CommandBatchProcessedResult OnCommandBatchProcessed() override; diff --git a/src/gpu/ipc/client/command_buffer_proxy_impl.cc b/src/gpu/ipc/client/command_buffer_proxy_impl.cc index bfafcdc9524d8..77455d492fbfc 100644 --- a/src/gpu/ipc/client/command_buffer_proxy_impl.cc +++ b/src/gpu/ipc/client/command_buffer_proxy_impl.cc @@ -407,6 +407,22 @@ void CommandBufferProxyImpl::DestroyTransferBuffer(int32_t id) { GpuCommandBufferMsg_DestroyTransferBuffer(route_id_, id)); } +void CommandBufferProxyImpl::ForceLostContext(error::ContextLostReason reason) { + CheckLock(); + base::AutoLock lock(last_state_lock_); + if (last_state_.error == gpu::error::kLostContext) { + // Per specification, do nothing if the context is already lost. + return; + } + last_state_.error = gpu::error::kLostContext; + // The caller determines the context lost reason. + last_state_.context_lost_reason = reason; + // Calling code may be in an indeterminate state (possibly including + // being in a GpuControlClient callback), so avoid re-entering the + // GpuControlClient here. + DisconnectChannelInFreshCallStack(); +} + void CommandBufferProxyImpl::SetGpuControlClient(GpuControlClient* client) { CheckLock(); gpu_control_client_ = client; diff --git a/src/gpu/ipc/client/command_buffer_proxy_impl.h b/src/gpu/ipc/client/command_buffer_proxy_impl.h index 3a541f708a32e..540ca3486d869 100644 --- a/src/gpu/ipc/client/command_buffer_proxy_impl.h +++ b/src/gpu/ipc/client/command_buffer_proxy_impl.h @@ -111,6 +111,7 @@ class GPU_EXPORT CommandBufferProxyImpl : public gpu::CommandBuffer, TransferBufferAllocationOption option = TransferBufferAllocationOption::kLoseContextOnOOM) override; void DestroyTransferBuffer(int32_t id) override; + void ForceLostContext(error::ContextLostReason reason) override; // gpu::GpuControl implementation: void SetGpuControlClient(GpuControlClient* client) override; diff --git a/src/gpu/ipc/in_process_command_buffer.cc b/src/gpu/ipc/in_process_command_buffer.cc index 18dc45de9fa89..e3e9c59e54588 100644 --- a/src/gpu/ipc/in_process_command_buffer.cc +++ b/src/gpu/ipc/in_process_command_buffer.cc @@ -1040,6 +1040,21 @@ void InProcessCommandBuffer::DestroyTransferBuffer(int32_t id) { gpu_thread_weak_ptr_factory_.GetWeakPtr(), id)); } +void InProcessCommandBuffer::ForceLostContext(error::ContextLostReason reason) { + ScheduleGpuTask( + base::BindOnce(&InProcessCommandBuffer::ForceLostContextOnGpuThread, + gpu_thread_weak_ptr_factory_.GetWeakPtr(), reason)); +} + +void InProcessCommandBuffer::ForceLostContextOnGpuThread( + error::ContextLostReason reason) { + DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_); + + // Similar implementation to CommandBufferDirect. + command_buffer_->SetContextLostReason(reason); + command_buffer_->SetParseError(error::kLostContext); +} + void InProcessCommandBuffer::DestroyTransferBufferOnGpuThread(int32_t id) { DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_); command_buffer_->DestroyTransferBuffer(id); diff --git a/src/gpu/ipc/in_process_command_buffer.h b/src/gpu/ipc/in_process_command_buffer.h index 82d4dc1c7263d..5c32e278a30ca 100644 --- a/src/gpu/ipc/in_process_command_buffer.h +++ b/src/gpu/ipc/in_process_command_buffer.h @@ -142,6 +142,7 @@ class GL_IN_PROCESS_CONTEXT_EXPORT InProcessCommandBuffer TransferBufferAllocationOption option = TransferBufferAllocationOption::kLoseContextOnOOM) override; void DestroyTransferBuffer(int32_t id) override; + void ForceLostContext(error::ContextLostReason reason) override; // GpuControl implementation (called on client thread): void SetGpuControlClient(GpuControlClient*) override; @@ -313,6 +314,7 @@ class GL_IN_PROCESS_CONTEXT_EXPORT InProcessCommandBuffer void RegisterTransferBufferOnGpuThread(int32_t id, scoped_refptr