blob: 2fd2579e068a3d9b8068b7b78ebe2d7684e0d039 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include <array>
#include <memory>
#include "base/memory/scoped_refptr.h"
#include "base/test/task_environment.h"
#include "gpu/command_buffer/client/client_test_helper.h"
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/framebuffer_completeness_cache.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/gpu_tracer.h"
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/command_buffer/service/shader_translator_cache.h"
#include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "gpu/config/gpu_feature_info.h"
#include "gpu/config/gpu_preferences.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_context_stub.h"
#include "ui/gl/gl_mock.h"
#include "ui/gl/gl_surface_stub.h"
#include "ui/gl/test/gl_surface_test_support.h"
using ::gl::MockGLInterface;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::NiceMock;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::SetArrayArgument;
namespace gpu {
namespace gles2 {
namespace {
constexpr GLuint kServiceTextureId = 101;
constexpr GLuint kServiceFramebufferId = 102;
constexpr GLint kMaxSize = 2048;
// Tests for GLES2DecoderImpl initialized with |offscreen| = true. The shared
// GLES2DecoderTestBase scripts a strict, ordered set of GL calls for onscreen
// initialization only, so this fixture uses a NiceMock with reasonable
// defaults and only sets expectations on the calls under test.
class GLES2DecoderOffscreenTest : public testing::Test {
protected:
void SetUp() override {
gl::SetGLGetProcAddressProc(MockGLInterface::GetGLProcAddress);
display_ = gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings();
gl_ = std::make_unique<NiceMock<MockGLInterface>>();
MockGLInterface::SetGLInterface(gl_.get());
ON_CALL(*gl_, GetString(_))
.WillByDefault(Return(reinterpret_cast<const GLubyte*>("")));
ON_CALL(*gl_, GetString(GL_VERSION))
.WillByDefault(
Return(reinterpret_cast<const GLubyte*>("OpenGL ES 2.0")));
ON_CALL(*gl_, GetIntegerv(_, _))
.WillByDefault([](GLenum pname, GLint* params) {
switch (pname) {
case GL_MAX_TEXTURE_SIZE:
case GL_MAX_CUBE_MAP_TEXTURE_SIZE:
case GL_MAX_RENDERBUFFER_SIZE:
*params = kMaxSize;
break;
case GL_MAX_VERTEX_UNIFORM_VECTORS:
*params = 256;
break;
case GL_MAX_VERTEX_ATTRIBS:
case GL_MAX_TEXTURE_IMAGE_UNITS:
case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
case GL_MAX_FRAGMENT_UNIFORM_VECTORS:
case GL_MAX_VARYING_VECTORS:
*params = 16;
break;
default:
// Bindings and anything else not needed by these tests.
*params = 0;
break;
}
});
// GL_MAX_VIEWPORT_DIMS returns two values. Declared after the generic
// default above so that it takes precedence.
static constexpr std::array<GLint, 2> kMaxViewportDims = {kMaxSize,
kMaxSize};
ON_CALL(*gl_, GetIntegerv(GL_MAX_VIEWPORT_DIMS, _))
.WillByDefault(SetArrayArgument<1>(kMaxViewportDims.begin(),
kMaxViewportDims.end()));
ON_CALL(*gl_, GetError()).WillByDefault(Return(GL_NO_ERROR));
ON_CALL(*gl_, GenTextures(1, _))
.WillByDefault(SetArgPointee<1>(kServiceTextureId));
ON_CALL(*gl_, GenFramebuffersEXT(1, _))
.WillByDefault(SetArgPointee<1>(kServiceFramebufferId));
ON_CALL(*gl_, CheckFramebufferStatusEXT(_))
.WillByDefault(Return(GL_FRAMEBUFFER_COMPLETE));
surface_ = base::MakeRefCounted<gl::GLSurfaceStub>();
context_ = base::MakeRefCounted<gl::GLContextStub>();
context_->SetGLVersionString("OpenGL ES 2.0");
context_->SetExtensionsString("");
context_->MakeCurrent(surface_.get());
}
void TearDown() override {
if (decoder_) {
decoder_->Destroy(/*have_context=*/true);
decoder_.reset();
}
group_ = nullptr;
context_ = nullptr;
surface_ = nullptr;
MockGLInterface::SetGLInterface(nullptr);
gl_.reset();
gl::GLSurfaceTestSupport::ShutdownGL(display_);
}
ContextResult InitializeOffscreenDecoder(
const GpuDriverBugWorkarounds& workarounds) {
auto feature_info =
base::MakeRefCounted<FeatureInfo>(workarounds, GpuFeatureInfo());
group_ = base::MakeRefCounted<ContextGroup>(
gpu_preferences_, /*memory_tracker=*/nullptr, &shader_translator_cache_,
&framebuffer_completeness_cache_, feature_info,
/*progress_reporter=*/nullptr, GpuFeatureInfo(),
&shared_image_manager_);
decoder_ = GLES2Decoder::Create(&client_, &command_buffer_service_,
&outputter_, group_.get());
return decoder_->Initialize(surface_, context_, /*offscreen=*/true,
CONTEXT_TYPE_OPENGLES2,
/*lose_context_when_out_of_memory=*/false);
}
base::test::SingleThreadTaskEnvironment task_environment_;
raw_ptr<gl::GLDisplay> display_ = nullptr;
std::unique_ptr<NiceMock<MockGLInterface>> gl_;
scoped_refptr<gl::GLSurfaceStub> surface_;
scoped_refptr<gl::GLContextStub> context_;
GpuPreferences gpu_preferences_;
ShaderTranslatorCache shader_translator_cache_{gpu_preferences_};
FramebufferCompletenessCache framebuffer_completeness_cache_;
SharedImageManager shared_image_manager_;
FakeCommandBufferServiceBase command_buffer_service_;
FakeDecoderClient client_;
TraceOutputter outputter_;
scoped_refptr<ContextGroup> group_;
std::unique_ptr<GLES2Decoder> decoder_;
};
// The offscreen backbuffer must be allocated with defined (zeroed) contents
// rather than relying on glClear, which is unreliable on drivers with the
// gl_clear_broken workaround. See crbug.com/562221093.
TEST_F(GLES2DecoderOffscreenTest, BackbufferStorageIsZeroInitialized) {
GpuDriverBugWorkarounds workarounds;
// With gl_clear_broken the backbuffer can't rely on the native glClear in
// ResizeOffscreenFramebuffer to initialize its contents.
workarounds.gl_clear_broken = true;
// Initialization issues other TexImage2D calls (e.g. default textures).
EXPECT_CALL(*gl_, TexImage2D(_, _, _, _, _, _, _, _, _)).Times(AnyNumber());
// The offscreen backbuffer is created at 64x64 in GL_RGB. It must be uploaded
// with data (BackTexture::AllocateStorage zero-fills it via
// base::HeapArray::WithSize) rather than nullptr, which leaves the storage
// uninitialized.
constexpr GLsizei kWidth = 64;
constexpr GLsizei kHeight = 64;
EXPECT_CALL(*gl_, TexImage2D(GL_TEXTURE_2D, 0, GL_RGB, kWidth, kHeight, 0,
GL_RGB, GL_UNSIGNED_BYTE, NotNull()))
.Times(1);
ASSERT_EQ(InitializeOffscreenDecoder(workarounds), ContextResult::kSuccess);
}
} // namespace
} // namespace gles2
} // namespace gpu