blob: b32cea3f02d812f568b26f54088b171c228795a1 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/command_buffer/tests/gl_manager.h"
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <GLES2/gl2extchromium.h>
#include <stddef.h>
#include <stdint.h>
#include <vector>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted_memory.h"
#include "build/build_config.h"
#include "gpu/command_buffer/client/gles2_cmd_helper.h"
#include "gpu/command_buffer/client/gles2_implementation.h"
#include "gpu/command_buffer/client/gles2_lib.h"
#include "gpu/command_buffer/client/transfer_buffer.h"
#include "gpu/command_buffer/common/constants.h"
#include "gpu/command_buffer/common/context_creation_attribs.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "gpu/command_buffer/service/command_buffer_direct.h"
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/gl_context_virtual.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/command_buffer/service/service_utils.h"
#include "gpu/command_buffer/service/transfer_buffer_manager.h"
#include "gpu/ipc/common/gpu_client_ids.h"
#include "gpu/ipc/in_process_command_buffer.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_share_group.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gl_utils.h"
#include "ui/gl/init/gl_factory.h"
#if BUILDFLAG(IS_MAC)
#include "ui/gfx/mac/io_surface.h"
#endif
namespace gpu {
namespace {
void InitializeGpuPreferencesForTestingFromCommandLine(
const base::CommandLine& command_line,
GpuPreferences* preferences) {
// Only initialize specific GpuPreferences members used for testing.
preferences->use_passthrough_cmd_decoder =
gles2::UsePassthroughCommandDecoder(&command_line);
preferences->enable_gpu_service_logging_gpu =
command_line.HasSwitch(switches::kEnableGPUServiceLoggingGPU);
}
class CommandBufferCheckLostContext : public CommandBufferDirect {
public:
explicit CommandBufferCheckLostContext(bool context_lost_allowed)
: context_lost_allowed_(context_lost_allowed) {}
~CommandBufferCheckLostContext() override = default;
void Flush(int32_t put_offset) override {
CommandBufferDirect::Flush(put_offset);
::gpu::CommandBuffer::State state = GetLastState();
if (!context_lost_allowed_) {
ASSERT_EQ(::gpu::error::kNoError, state.error);
}
}
private:
bool context_lost_allowed_;
};
} // namespace
int GLManager::use_count_;
scoped_refptr<gl::GLShareGroup>* GLManager::base_share_group_;
scoped_refptr<gl::GLSurface>* GLManager::base_surface_;
scoped_refptr<gl::GLContext>* GLManager::base_context_;
// static
GpuFeatureInfo GLManager::g_gpu_feature_info;
GLManager::Options::Options() = default;
GLManager::GLManager() {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
InitializeGpuPreferencesForTestingFromCommandLine(command_line,
&gpu_preferences_);
SetupBaseContext();
}
GLManager::~GLManager() {
--use_count_;
if (!use_count_) {
if (base_share_group_) {
delete base_share_group_;
base_share_group_ = nullptr;
}
if (base_surface_) {
delete base_surface_;
base_surface_ = nullptr;
}
if (base_context_) {
delete base_context_;
base_context_ = nullptr;
}
}
}
void GLManager::Initialize(const GLManager::Options& options) {
GpuDriverBugWorkarounds platform_workarounds(
g_gpu_feature_info.enabled_gpu_driver_bug_workarounds);
InitializeWithWorkaroundsImpl(options, platform_workarounds);
}
void GLManager::InitializeWithWorkarounds(
const GLManager::Options& options,
const GpuDriverBugWorkarounds& workarounds) {
GpuDriverBugWorkarounds combined_workarounds(
g_gpu_feature_info.enabled_gpu_driver_bug_workarounds);
combined_workarounds.Append(workarounds);
InitializeWithWorkaroundsImpl(options, combined_workarounds);
}
void GLManager::InitializeWithWorkaroundsImpl(
const GLManager::Options& options,
const GpuDriverBugWorkarounds& workarounds) {
const SharedMemoryLimits limits = options.shared_memory_limits;
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
DCHECK(!command_line.HasSwitch(switches::kDisableGLExtensions));
context_type_ = options.context_type;
gl::GLShareGroup* share_group = nullptr;
if (options.share_group_manager) {
share_group = options.share_group_manager->share_group();
}
gles2::ContextGroup* context_group = nullptr;
scoped_refptr<gles2::ShareGroup> client_share_group;
if (options.share_group_manager) {
context_group = options.share_group_manager->decoder_->GetContextGroup();
client_share_group =
options.share_group_manager->gles2_implementation()->share_group();
}
gl::GLContext* real_gl_context = nullptr;
if (options.virtual_manager &&
!gpu_preferences_.use_passthrough_cmd_decoder) {
real_gl_context = options.virtual_manager->context();
}
share_group_ = share_group ? share_group : new gl::GLShareGroup;
translator_cache_ =
std::make_unique<gles2::ShaderTranslatorCache>(gpu_preferences_);
discardable_manager_ =
std::make_unique<ServiceDiscardableManager>(gpu_preferences_);
passthrough_discardable_manager_ =
std::make_unique<PassthroughDiscardableManager>(gpu_preferences_);
if (!context_group) {
GpuFeatureInfo gpu_feature_info;
scoped_refptr<gles2::FeatureInfo> feature_info =
new gles2::FeatureInfo(workarounds, gpu_feature_info);
// Always mark the passthrough command decoder as supported so that tests do
// not unexpectedly use the wrong command decoder
context_group = new gles2::ContextGroup(
gpu_preferences_, /*memory_tracker=*/nullptr, translator_cache_.get(),
&completeness_cache_, feature_info,
/*progress_reporter=*/nullptr, gpu_feature_info,
discardable_manager_.get(), passthrough_discardable_manager_.get(),
&shared_image_manager_);
}
command_buffer_.reset(
new CommandBufferCheckLostContext(options.context_lost_allowed));
decoder_ = ::gpu::gles2::GLES2Decoder::Create(command_buffer_.get(),
command_buffer_->service(),
&outputter_, context_group);
if (options.force_shader_name_hashing) {
decoder_->SetForceShaderNameHashingForTest(true);
}
command_buffer_->set_handler(decoder_.get());
auto surface = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplayEGL(),
gfx::Size());
ASSERT_TRUE(surface.get() != nullptr) << "could not create offscreen surface";
if (base_context_) {
context_ = scoped_refptr<gl::GLContext>(new gpu::GLContextVirtual(
share_group_.get(), base_context_->get(), decoder_->AsWeakPtr()));
ASSERT_TRUE(context_->Initialize(
surface.get(), GenerateGLContextAttribsForDecoder(
options.context_type, gl::GpuPreference::kLowPower,
context_group)));
} else {
if (real_gl_context) {
context_ = scoped_refptr<gl::GLContext>(new gpu::GLContextVirtual(
share_group_.get(), real_gl_context, decoder_->AsWeakPtr()));
ASSERT_TRUE(context_->Initialize(
surface.get(), GenerateGLContextAttribsForDecoder(
options.context_type, gl::GpuPreference::kLowPower,
context_group)));
} else {
context_ = gl::init::CreateGLContext(
share_group_.get(), surface.get(),
GenerateGLContextAttribsForDecoder(options.context_type,
gl::GpuPreference::kLowPower,
context_group));
g_gpu_feature_info.ApplyToGLContext(context_.get());
}
}
ASSERT_TRUE(context_.get() != nullptr) << "could not create GL context";
ASSERT_TRUE(context_->default_surface() == surface.get());
ASSERT_TRUE(context_->MakeCurrentDefault());
// if (gpu_preferences_.use_passthrough_cmd_decoder) {
// auto* apit = g_current_gl_context;
// api->glRequestExtensionANGLEFn("GL_EXT_read_format_bgra");
// api->glRequestExtensionANGLEFn("GL_EXT_texture_format_BGRA8888");
// }
auto result = decoder_->Initialize(
context_->default_surface(), context_.get(), /*offscreen=*/true,
options.context_type, /*lose_context_when_out_of_memory=*/false);
if (result != gpu::ContextResult::kSuccess)
return;
// Client side Capabilities queries return reference, service side return
// value. Here two sides are joined together.
capabilities_ = decoder_->GetCapabilities();
gl_capabilities_ = decoder_->GetGLCapabilities();
// Create the GLES2 helper, which writes the command buffer protocol.
gles2_helper_.reset(new gles2::GLES2CmdHelper(command_buffer_.get()));
ASSERT_EQ(gles2_helper_->Initialize(limits.command_buffer_size),
gpu::ContextResult::kSuccess);
// Create a transfer buffer.
transfer_buffer_.reset(new TransferBuffer(gles2_helper_.get()));
// Create the object exposing the OpenGL API.
gles2_implementation_.reset(new gles2::GLES2Implementation(
gles2_helper_.get(), std::move(client_share_group),
transfer_buffer_.get(), options.lose_context_when_out_of_memory, this));
ASSERT_EQ(gles2_implementation_->Initialize(limits),
gpu::ContextResult::kSuccess)
<< "Could not init GLES2Implementation";
MakeCurrent();
// Initialize FBO for drawing
if (!options.size.IsEmpty()) {
GLuint color, depth_stencil;
gles2_implementation_->GenTextures(1, &color);
gles2_implementation_->BindTexture(GL_TEXTURE_2D, color);
gles2_implementation_->TexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA, options.size.width(), options.size.height(),
0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
gles2_implementation_->BindTexture(GL_TEXTURE_2D, 0);
gles2_implementation_->GenRenderbuffers(1, &depth_stencil);
gles2_implementation_->BindRenderbuffer(GL_RENDERBUFFER, depth_stencil);
gles2_implementation_->RenderbufferStorage(
GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, options.size.width(),
options.size.height());
gles2_implementation_->BindRenderbuffer(GL_RENDERBUFFER, 0);
gles2_implementation_->GenFramebuffers(1, &fbo_);
gles2_implementation_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
gles2_implementation_->FramebufferTexture2D(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
EXPECT_TRUE(glGetError() == GL_NONE);
// WebGL requires GL_DEPTH_STENCIL_ATTACHMENT
if (context_type_ == CONTEXT_TYPE_WEBGL1 ||
context_type_ == CONTEXT_TYPE_WEBGL2) {
gles2_implementation_->FramebufferRenderbuffer(
GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depth_stencil);
} else {
gles2_implementation_->FramebufferRenderbuffer(
GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_stencil);
gles2_implementation_->FramebufferRenderbuffer(
GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depth_stencil);
}
gles2_implementation_->Viewport(0, 0, options.size.width(),
options.size.height());
gles2_implementation_->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
GL_STENCIL_BUFFER_BIT);
}
EXPECT_TRUE(glGetError() == GL_NONE);
}
void GLManager::BindOffscreenFramebuffer(GLenum target) {
gles2_implementation_->BindFramebuffer(target, fbo_);
}
size_t GLManager::GetSharedMemoryBytesAllocated() const {
return command_buffer_->service()->GetSharedMemoryBytesAllocated();
}
void GLManager::SetupBaseContext() {
if (!use_count_) {
#if BUILDFLAG(IS_ANDROID)
// Virtual contexts is not necessary with passthrough.
if (!gpu_preferences_.use_passthrough_cmd_decoder) {
base_share_group_ =
new scoped_refptr<gl::GLShareGroup>(new gl::GLShareGroup);
gfx::Size size(4, 4);
base_surface_ = new scoped_refptr<gl::GLSurface>(
gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplay(), size));
base_context_ =
new scoped_refptr<gl::GLContext>(gl::init::CreateGLContext(
base_share_group_->get(), base_surface_->get(),
gl::GLContextAttribs()));
g_gpu_feature_info.ApplyToGLContext(base_context_->get());
}
#endif
}
++use_count_;
}
void GLManager::MakeCurrent() {
::gles2::SetGLContext(gles2_implementation_.get());
if (!decoder_->MakeCurrent())
command_buffer_->service()->SetParseError(error::kLostContext);
}
void GLManager::SetSurface(gl::GLSurface* surface) {
decoder_->SetSurface(surface);
MakeCurrent();
}
void GLManager::PerformIdleWork() {
decoder_->PerformIdleWork();
}
void GLManager::Destroy() {
if (gles2_implementation_.get()) {
MakeCurrent();
EXPECT_TRUE(glGetError() == GL_NONE);
gles2_implementation_->Flush();
gles2_implementation_.reset();
}
transfer_buffer_.reset();
gles2_helper_.reset();
if (decoder_.get()) {
bool have_context =
decoder_->GetGLContext() &&
decoder_->GetGLContext()->MakeCurrent(context_->default_surface());
decoder_->Destroy(have_context);
decoder_.reset();
}
command_buffer_.reset();
context_ = nullptr;
}
const GpuDriverBugWorkarounds& GLManager::workarounds() const {
return decoder_->GetContextGroup()->feature_info()->workarounds();
}
void GLManager::SetGpuControlClient(GpuControlClient*) {
// The client is not currently called, so don't store it.
}
const Capabilities& GLManager::GetCapabilities() const {
return capabilities_;
}
const GLCapabilities& GLManager::GetGLCapabilities() const {
return gl_capabilities_;
}
void GLManager::SignalQuery(uint32_t query, base::OnceClosure callback) {
NOTREACHED();
}
void GLManager::CancelAllQueries() {
NOTREACHED();
}
void GLManager::CreateGpuFence(uint32_t gpu_fence_id, ClientGpuFence source) {
NOTREACHED();
}
void GLManager::GetGpuFence(
uint32_t gpu_fence_id,
base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) {
NOTREACHED();
}
void GLManager::SetLock(base::Lock*) {
NOTREACHED();
}
void GLManager::EnsureWorkVisible() {
NOTREACHED();
}
gpu::CommandBufferNamespace GLManager::GetNamespaceID() const {
return CommandBufferNamespace::INVALID;
}
CommandBufferId GLManager::GetCommandBufferID() const {
return CommandBufferId();
}
void GLManager::FlushPendingWork() {
NOTREACHED();
}
uint64_t GLManager::GenerateFenceSyncRelease() {
NOTREACHED();
}
bool GLManager::IsFenceSyncReleased(uint64_t release) {
NOTREACHED();
}
void GLManager::SignalSyncToken(const gpu::SyncToken& sync_token,
base::OnceClosure callback) {
NOTREACHED();
}
void GLManager::WaitSyncToken(const gpu::SyncToken& sync_token) {
NOTREACHED();
}
bool GLManager::CanWaitUnverifiedSyncToken(const gpu::SyncToken& sync_token) {
NOTREACHED();
}
ContextType GLManager::GetContextType() const {
return context_type_;
}
void GLManager::Reset() {
Destroy();
SetupBaseContext();
}
} // namespace gpu