blob: ecb75b91015eaeb4dc878e232e06976f4c158b5d [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/command_line.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "build/build_config.h"
#include "components/viz/common/gpu/raster_context_provider.h"
#include "components/viz/test/gpu_host_impl_test_api.h"
#include "content/browser/browser_main_loop.h"
#include "content/browser/compositor/image_transport_factory.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/browser/gpu_utils.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/gpu_stream_constants.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h"
#include "content/test/gpu_browsertest_helpers.h"
#include "gpu/ipc/client/command_buffer_proxy_impl.h"
#include "gpu/ipc/client/gpu_channel_host.h"
#include "services/viz/privileged/mojom/gl/gpu_service.mojom.h"
#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
#include "ui/gl/gl_switches.h"
namespace {
// RunLoop implementation that runs until it observes OnContextLost().
class ContextLostRunLoop : public viz::ContextLostObserver {
public:
explicit ContextLostRunLoop(viz::RasterContextProvider* context_provider)
: context_provider_(context_provider) {
context_provider_->AddObserver(this);
}
ContextLostRunLoop(const ContextLostRunLoop&) = delete;
ContextLostRunLoop& operator=(const ContextLostRunLoop&) = delete;
~ContextLostRunLoop() override { context_provider_->RemoveObserver(this); }
void RunUntilContextLost() { run_loop_.Run(); }
private:
// viz::LostContextProvider:
void OnContextLost() override { run_loop_.Quit(); }
const raw_ptr<viz::RasterContextProvider> context_provider_;
base::RunLoop run_loop_;
};
class ContextTestBase : public content::ContentBrowserTest {
public:
void SetUpOnMainThread() override {
// This may leave the provider_ null in some cases, so tests need to early
// out.
if (!content::GpuDataManager::GetInstance()->GpuAccessAllowed(nullptr))
return;
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host =
content::GpuBrowsertestEstablishGpuChannelSyncRunLoop();
CHECK(gpu_channel_host);
provider_ = content::GpuBrowsertestCreateContext(
std::move(gpu_channel_host), /*wants_raster_interface=*/false);
auto result = provider_->BindToCurrentSequence();
CHECK_EQ(result, gpu::ContextResult::kSuccess);
gl_ = provider_->ContextGL();
context_support_ = provider_->ContextSupport();
ContentBrowserTest::SetUpOnMainThread();
}
void TearDownOnMainThread() override {
// Must delete the context first.
gl_ = nullptr;
context_support_ = nullptr;
provider_ = nullptr;
ContentBrowserTest::TearDownOnMainThread();
}
protected:
scoped_refptr<viz::ContextProviderCommandBuffer> provider_;
raw_ptr<gpu::ContextSupport> context_support_ = nullptr;
raw_ptr<gpu::gles2::GLES2Interface> gl_ = nullptr;
};
class TestGpuHostImplDelegate
: public viz::GpuHostImplTestApi::HookDelegateBase {
public:
TestGpuHostImplDelegate() = default;
~TestGpuHostImplDelegate() override = default;
TestGpuHostImplDelegate(const TestGpuHostImplDelegate&) = delete;
TestGpuHostImplDelegate& operator=(const TestGpuHostImplDelegate&) = delete;
// viz::GpuHostImpl::Delegate
bool GpuAccessAllowed() const override { return false; }
};
} // namespace
// Include the shared tests.
#define CONTEXT_TEST_F IN_PROC_BROWSER_TEST_F
#include "base/functional/bind.h"
#include "base/task/single_thread_task_runner.h"
#include "build/chromeos_buildflags.h"
#include "content/public/browser/browser_thread.h"
#include "gpu/ipc/client/gpu_context_tests.h"
namespace content {
class BrowserGpuChannelHostFactoryTest : public ContentBrowserTest {
public:
void SetUpOnMainThread() override {
if (!GpuDataManager::GetInstance()->GpuAccessAllowed(nullptr))
return;
CHECK(GetFactory());
ContentBrowserTest::SetUpOnMainThread();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
// Start all tests without a gpu channel so that the tests exercise a
// consistent codepath.
command_line->AppendSwitch(switches::kDisableGpuEarlyInit);
}
void Signal(bool* event,
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host) {
CHECK_EQ(*event, false);
*event = true;
gpu_channel_host_ = std::move(gpu_channel_host);
}
void SignalAndQuitLoop(bool* event,
base::RunLoop* run_loop,
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host) {
Signal(event, std::move(gpu_channel_host));
run_loop->Quit();
}
protected:
gpu::GpuChannelEstablishFactory* GetFactory() {
return BrowserMainLoop::GetInstance()->gpu_channel_establish_factory();
}
bool IsChannelEstablished() {
return gpu_channel_host_ && !gpu_channel_host_->IsLost();
}
void EstablishAndWait() {
gpu_channel_host_ = content::GpuBrowsertestEstablishGpuChannelSyncRunLoop();
}
gpu::GpuChannelHost* GetGpuChannel() { return gpu_channel_host_.get(); }
scoped_refptr<gpu::GpuChannelHost> gpu_channel_host_;
};
// Test fails on Chromeos + Mac, flaky on Windows because UI Compositor
// establishes a GPU channel.
// TODO(crbug.com/40118868): Revisit the macro expression once build flag switch
// of lacros-chrome is complete.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_Basic Basic
#else
#define MAYBE_Basic DISABLED_Basic
#endif
IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest, MAYBE_Basic) {
DCHECK(!IsChannelEstablished());
EstablishAndWait();
EXPECT_TRUE(GetGpuChannel() != nullptr);
}
#if !BUILDFLAG(IS_ANDROID)
// Test fails on Chromeos + Mac, flaky on Windows because UI Compositor
// establishes a GPU channel.
// TODO(crbug.com/40118868): Revisit the macro expression once build flag switch
// of lacros-chrome is complete.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_AlreadyEstablished AlreadyEstablished
#else
#define MAYBE_AlreadyEstablished DISABLED_AlreadyEstablished
#endif
IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest,
MAYBE_AlreadyEstablished) {
DCHECK(!IsChannelEstablished());
scoped_refptr<gpu::GpuChannelHost> gpu_channel =
GetFactory()->EstablishGpuChannelSync();
// Expect established callback immediately.
bool event = false;
GetFactory()->EstablishGpuChannel(
base::BindOnce(&BrowserGpuChannelHostFactoryTest::Signal,
base::Unretained(this), &event));
EXPECT_TRUE(event);
EXPECT_EQ(gpu_channel.get(), GetGpuChannel());
}
#endif
// Test fails on Chromeos + Mac, flaky on Windows because UI Compositor
// establishes a GPU channel.
// TODO(crbug.com/40118868): Revisit the macro expression once build flag switch
// of lacros-chrome is complete.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_CrashAndRecover CrashAndRecover
#else
#define MAYBE_CrashAndRecover DISABLED_CrashAndRecover
#endif
IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest,
MAYBE_CrashAndRecover) {
DCHECK(!IsChannelEstablished());
EstablishAndWait();
scoped_refptr<gpu::GpuChannelHost> host = GetGpuChannel();
scoped_refptr<viz::ContextProviderCommandBuffer> provider =
content::GpuBrowsertestCreateContext(GetGpuChannel());
ContextLostRunLoop run_loop(provider.get());
ASSERT_EQ(provider->BindToCurrentSequence(), gpu::ContextResult::kSuccess);
GpuProcessHost::CallOnUI(FROM_HERE, GPU_PROCESS_KIND_SANDBOXED,
false /* force_create */,
base::BindOnce([](GpuProcessHost* host) {
if (host)
host->gpu_service()->Crash();
}));
run_loop.RunUntilContextLost();
EXPECT_FALSE(IsChannelEstablished());
EstablishAndWait();
EXPECT_TRUE(IsChannelEstablished());
}
// Disabled outside linux like other tests here sadface.
// crbug.com/1224892: the test if flaky on linux and lacros.
// TODO(crbug.com/40118868): Revisit the macro expression once build flag switch
// of lacros-chrome is complete.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest,
DISABLED_CreateTransferBuffer) {
DCHECK(!IsChannelEstablished());
EstablishAndWait();
gpu::ContextCreationAttribs attributes;
attributes.bind_generates_resource = false;
auto impl = std::make_unique<gpu::CommandBufferProxyImpl>(
GetGpuChannel(), content::kGpuStreamIdDefault,
base::SingleThreadTaskRunner::GetCurrentDefault());
ASSERT_EQ(
impl->Initialize(gpu::kNullSurfaceHandle, nullptr,
content::kGpuStreamPriorityDefault, attributes, GURL()),
gpu::ContextResult::kSuccess);
// Creating a transfer buffer works normally.
int32_t id = -1;
scoped_refptr<gpu::Buffer> buffer = impl->CreateTransferBuffer(100, &id);
EXPECT_TRUE(buffer);
EXPECT_GE(id, 0);
// If the context is lost, creating a transfer buffer still works. This is
// important for initializing a client side context. If it is lost for some
// transient reason, we don't want that to be confused with a fatal error,
// like failing to make a transfer buffer.
// Lose the connection to the gpu to lose the context.
GetGpuChannel()->DestroyChannel();
// It's not visible until we run the task queue.
EXPECT_EQ(impl->GetLastState().error, gpu::error::kNoError);
// Wait to see the error occur. The DestroyChannel() will destroy the IPC
// channel on the IO thread, which then notifies the main thread about the
// error state.
base::RunLoop wait_for_io_run_loop;
GetIOThreadTaskRunner({})->PostTask(FROM_HERE,
wait_for_io_run_loop.QuitClosure());
// Waits for the IO thread to run.
wait_for_io_run_loop.Run();
// Waits for the main thread to run.
base::RunLoop().RunUntilIdle();
// The error has become visible on the main thread now.
EXPECT_NE(impl->GetLastState().error, gpu::error::kNoError);
// Creating a transfer buffer still works.
id = -1;
buffer = impl->CreateTransferBuffer(100, &id);
EXPECT_TRUE(buffer);
EXPECT_GE(id, 0);
}
#endif
IN_PROC_BROWSER_TEST_F(BrowserGpuChannelHostFactoryTest,
CallbackOnSynchronousFailure) {
// Ensure that there is no pending establish request.
EstablishAndWait();
viz::GpuHostImplTestApi test_api(GpuProcessHost::Get()->gpu_host());
// This delegate disallows GPU access, which will cause EstablishGpuChannel()
// to fail synchronously.
test_api.HookDelegate(std::make_unique<TestGpuHostImplDelegate>());
bool event = false;
GetFactory()->EstablishGpuChannel(
base::BindOnce(&BrowserGpuChannelHostFactoryTest::Signal,
base::Unretained(this), &event));
// Expect that the callback has been called.
EXPECT_TRUE(event);
}
} // namespace content