blob: 82b8acd12c23d6776756e4522233615d171d34c1 [file] [log] [blame]
// Copyright 2018 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/service/raster_decoder.h"
#include <limits>
#include <memory>
#include <string>
#include <utility>
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/raster_cmd_format.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/query_manager.h"
#include "gpu/command_buffer/service/raster_decoder_unittest_base.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory.h"
#include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
#include "gpu/command_buffer/service/test_helper.h"
#include "gpu/config/gpu_preferences.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_mock.h"
#include "ui/gl/gl_surface_stub.h"
#include "ui/gl/init/gl_factory.h"
#include "ui/gl/test/gl_surface_test_support.h"
using ::testing::_;
using ::testing::Return;
using ::testing::SetArgPointee;
namespace gpu {
namespace raster {
namespace {
void CopyMailboxes(GLbyte (&output)[sizeof(Mailbox) * 2],
const Mailbox& source,
const Mailbox& dest) {
UNSAFE_TODO(memcpy(output, source.name, sizeof(source.name)));
UNSAFE_TODO(
memcpy(output + sizeof(source.name), dest.name, sizeof(dest.name)));
}
} // anonymous namespace
class RasterDecoderTest : public RasterDecoderTestBase {
public:
RasterDecoderTest() = default;
};
INSTANTIATE_TEST_SUITE_P(Service, RasterDecoderTest, ::testing::Bool());
INSTANTIATE_TEST_SUITE_P(Service,
RasterDecoderManualInitTest,
::testing::Bool());
const GLsync kGlSync = reinterpret_cast<GLsync>(0xdeadbeef);
TEST_P(RasterDecoderTest, BeginEndQueryEXTCommandsCompletedCHROMIUM) {
GenHelper<cmds::GenQueriesEXTImmediate>(kNewClientId);
cmds::BeginQueryEXT begin_cmd;
begin_cmd.Init(GL_COMMANDS_COMPLETED_CHROMIUM, kNewClientId,
shared_memory_id_, kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
QueryManager* query_manager = decoder_->GetQueryManager();
ASSERT_TRUE(query_manager != nullptr);
QueryManager::Query* query = query_manager->GetQuery(kNewClientId);
ASSERT_TRUE(query != nullptr);
EXPECT_FALSE(query->IsPending());
EXPECT_TRUE(query->IsActive());
EXPECT_CALL(*gl_, Flush()).RetiresOnSaturation();
EXPECT_CALL(*gl_, FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0))
.WillOnce(Return(kGlSync))
.RetiresOnSaturation();
#if DCHECK_IS_ON()
EXPECT_CALL(*gl_, IsSync(kGlSync))
.WillOnce(Return(GL_TRUE))
.RetiresOnSaturation();
#endif
cmds::EndQueryEXT end_cmd;
end_cmd.Init(GL_COMMANDS_COMPLETED_CHROMIUM, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_TRUE(query->IsPending());
EXPECT_FALSE(query->IsActive());
#if DCHECK_IS_ON()
EXPECT_CALL(*gl_, IsSync(kGlSync))
.WillOnce(Return(GL_TRUE))
.RetiresOnSaturation();
#endif
EXPECT_CALL(*gl_, ClientWaitSync(kGlSync, _, _))
.WillOnce(Return(GL_TIMEOUT_EXPIRED))
.RetiresOnSaturation();
query_manager->ProcessPendingQueries(false);
EXPECT_TRUE(query->IsPending());
#if DCHECK_IS_ON()
EXPECT_CALL(*gl_, IsSync(kGlSync))
.WillOnce(Return(GL_TRUE))
.RetiresOnSaturation();
#endif
EXPECT_CALL(*gl_, ClientWaitSync(kGlSync, _, _))
.WillOnce(Return(GL_ALREADY_SIGNALED))
.RetiresOnSaturation();
query_manager->ProcessPendingQueries(false);
EXPECT_FALSE(query->IsPending());
#if DCHECK_IS_ON()
EXPECT_CALL(*gl_, IsSync(kGlSync))
.WillOnce(Return(GL_TRUE))
.RetiresOnSaturation();
#endif
EXPECT_CALL(*gl_, DeleteSync(kGlSync)).Times(1).RetiresOnSaturation();
ResetDecoder();
}
TEST_P(RasterDecoderTest, BeginEndQueryEXTCommandsIssuedCHROMIUM) {
cmds::BeginQueryEXT begin_cmd;
GenHelper<cmds::GenQueriesEXTImmediate>(kNewClientId);
// Test valid parameters work.
begin_cmd.Init(GL_COMMANDS_ISSUED_CHROMIUM, kNewClientId, shared_memory_id_,
kSharedMemoryOffset);
EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
QueryManager* query_manager = decoder_->GetQueryManager();
ASSERT_TRUE(query_manager != nullptr);
QueryManager::Query* query = query_manager->GetQuery(kNewClientId);
ASSERT_TRUE(query != nullptr);
EXPECT_FALSE(query->IsPending());
EXPECT_TRUE(query->IsActive());
// Test end succeeds.
cmds::EndQueryEXT end_cmd;
end_cmd.Init(GL_COMMANDS_ISSUED_CHROMIUM, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_FALSE(query->IsPending());
EXPECT_FALSE(query->IsActive());
}
TEST_P(RasterDecoderTest, QueryCounterEXTCommandsIssuedTimestampCHROMIUM) {
GenHelper<cmds::GenQueriesEXTImmediate>(kNewClientId);
cmds::QueryCounterEXT query_counter_cmd;
query_counter_cmd.Init(kNewClientId, GL_COMMANDS_ISSUED_TIMESTAMP_CHROMIUM,
shared_memory_id_, kSharedMemoryOffset, 1);
EXPECT_EQ(error::kNoError, ExecuteCmd(query_counter_cmd));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
QueryManager* query_manager = decoder_->GetQueryManager();
ASSERT_TRUE(query_manager != nullptr);
QueryManager::Query* query = query_manager->GetQuery(kNewClientId);
ASSERT_TRUE(query != nullptr);
EXPECT_FALSE(query->IsPending());
EXPECT_FALSE(query->IsActive());
}
TEST_P(RasterDecoderManualInitTest, GetCapabilitiesHalfFloatLinear) {
InitState init;
init.extensions.push_back("GL_OES_texture_half_float_linear");
InitDecoder(init);
AddExpectationsForGetCapabilities();
const auto& caps = decoder_->GetCapabilities();
EXPECT_TRUE(caps.texture_half_float_linear);
}
TEST_P(RasterDecoderManualInitTest, GetCapabilitiesNorm16) {
// R16 requires an ES3 context plus the extension to be available.
InitState init;
init.context_type = CONTEXT_TYPE_OPENGLES3;
init.gl_version = "OpenGL ES 3.0";
init.extensions.push_back("GL_EXT_texture_norm16");
InitDecoder(init);
AddExpectationsForGetCapabilities();
const auto& caps = decoder_->GetCapabilities();
EXPECT_TRUE(caps.texture_norm16);
}
class RasterDecoderOOPTest : public testing::Test, DecoderClient {
public:
void SetUp() override {
display_ = gl::GLSurfaceTestSupport::InitializeOneOff();
gpu::GpuDriverBugWorkarounds workarounds;
scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup();
scoped_refptr<gl::GLSurface> surface =
gl::init::CreateOffscreenGLSurface(display_, gfx::Size());
scoped_refptr<gl::GLContext> context = gl::init::CreateGLContext(
share_group.get(), surface.get(), gl::GLContextAttribs());
ASSERT_TRUE(context->MakeCurrent(surface.get()));
gpu_feature_info_.status_values[GPU_FEATURE_TYPE_GPU_TILE_RASTERIZATION] =
kGpuFeatureStatusEnabled;
auto feature_info = base::MakeRefCounted<gles2::FeatureInfo>(
workarounds, gpu_feature_info_);
context_state_ = base::MakeRefCounted<SharedContextState>(
std::move(share_group), std::move(surface), std::move(context),
false /* use_virtualized_gl_contexts */, base::DoNothing(),
GpuPreferences().gr_context_type);
context_state_->InitializeSkia(GpuPreferences(), workarounds);
context_state_->InitializeGL(GpuPreferences(), feature_info);
decoder_ = CreateDecoder();
scoped_refptr<gpu::Buffer> buffer =
command_buffer_service_->CreateTransferBufferHelper(kSharedBufferSize,
&shared_memory_id_);
shared_memory_offset_ = kSharedMemoryOffset;
shared_memory_address_ = UNSAFE_TODO(
static_cast<int8_t*>(buffer->memory()) + shared_memory_offset_);
workarounds.webgl_or_caps_max_texture_size = INT_MAX - 1;
shared_image_factory_ = std::make_unique<SharedImageFactory>(
GpuPreferences(), workarounds, GpuFeatureInfo(), context_state_.get(),
&shared_image_manager_, nullptr,
/*is_for_display_compositor=*/false);
client_texture_mailbox_ =
CreateMailbox(viz::SinglePlaneFormat::kRGBA_8888, /*width=*/2,
/*height=*/2, /*cleared=*/false);
// When creating the mailbox, we create a WrappedSkImage shared image which
// sets this flag to true. Some tests expect this flag to be false when
// testing so we reset it back here to false.
context_state_->set_need_context_state_reset(/*reset=*/false);
}
void TearDown() override {
context_state_->MakeCurrent(nullptr);
decoder_->EndDecoding();
decoder_->Destroy(!decoder_->WasContextLost());
decoder_.reset();
command_buffer_service_.reset();
shared_image_factory_->DestroyAllSharedImages(true);
shared_image_factory_.reset();
context_state_.reset();
context_state_ = nullptr;
gl::GLSurfaceTestSupport::ShutdownGL(display_);
}
RasterDecoderOOPTest() : memory_tracker_(nullptr) {
UNSAFE_TODO(memset(immediate_buffer_, 0xEE, sizeof(immediate_buffer_)));
}
// DecoderClient implementation.
void OnConsoleMessage(int32_t id, const std::string& message) override {}
void CacheBlob(gpu::GpuDiskCacheType type,
const std::string& key,
const std::string& blob) override {}
void OnFenceSyncRelease(uint64_t release) override {}
void OnDescheduleUntilFinished() override {}
void OnRescheduleAfterFinished() override {}
void ScheduleGrContextCleanup() override {}
void HandleReturnData(base::span<const uint8_t> data) override {}
bool ShouldYield() override { return false; }
std::unique_ptr<RasterDecoder> CreateDecoder() {
command_buffer_service_ = std::make_unique<FakeCommandBufferServiceBase>();
auto decoder = base::WrapUnique(RasterDecoder::Create(
this, command_buffer_service_.get(), &outputter_, gpu_feature_info_,
GpuPreferences(), nullptr /* memory_tracker */, &shared_image_manager_,
context_state_, true /* is_privileged */));
ContextCreationAttribs attribs;
attribs.enable_gpu_rasterization = true;
attribs.enable_raster_interface = true;
CHECK_EQ(decoder->Initialize(context_state_->surface(),
context_state_->context(), true,
gles2::DisallowedFeatures(), attribs),
ContextResult::kSuccess);
return decoder;
}
gpu::Mailbox CreateMailbox(viz::SharedImageFormat format,
GLsizei width,
GLsizei height,
bool cleared) {
gpu::Mailbox mailbox = gpu::Mailbox::Generate();
gfx::Size size(width, height);
auto color_space = gfx::ColorSpace::CreateSRGB();
// Via this function, this test creates mailboxes that are used as both the
// sources of reads and destinations of writes via the raster interface.
shared_image_factory_->CreateSharedImage(
mailbox, format, size, color_space, kTopLeft_GrSurfaceOrigin,
kPremul_SkAlphaType, gpu::kNullSurfaceHandle,
SHARED_IMAGE_USAGE_RASTER_READ | SHARED_IMAGE_USAGE_RASTER_WRITE,
"TestLabel");
if (cleared) {
SharedImageRepresentationFactory repr_factory(shared_image_manager(),
nullptr);
auto representation =
repr_factory.ProduceSkia(mailbox, context_state_.get());
representation->SetCleared();
}
return mailbox;
}
template <typename T>
T* GetImmediateAs() {
return reinterpret_cast<T*>(immediate_buffer_);
}
template <typename T>
error::Error ExecuteCmd(RasterDecoder* decoder, const T& cmd) {
static_assert(T::kArgFlags == cmd::kFixed,
"T::kArgFlags should equal cmd::kFixed");
int entries_processed = 0;
return decoder->DoCommands(1, reinterpret_cast<const void*>(&cmd),
ComputeNumEntries(sizeof(cmd)),
&entries_processed);
}
template <typename T>
error::Error ExecuteImmediateCmd(const T& cmd, size_t data_size) {
static_assert(T::kArgFlags == cmd::kAtLeastN,
"T::kArgFlags should equal cmd::kAtLeastN");
int entries_processed = 0;
return decoder_->DoCommands(1, reinterpret_cast<const void*>(&cmd),
ComputeNumEntries(sizeof(cmd) + data_size),
&entries_processed);
}
template <typename T>
T GetSharedMemoryAs() {
return reinterpret_cast<T>(shared_memory_address_.get());
}
GLint GetGLError() {
cmds::GetError cmd;
cmd.Init(shared_memory_id_, shared_memory_offset_);
EXPECT_EQ(error::kNoError, ExecuteCmd(decoder_.get(), cmd));
return static_cast<GLint>(*GetSharedMemoryAs<GLenum*>());
}
SharedImageManager* shared_image_manager() { return &shared_image_manager_; }
protected:
GpuFeatureInfo gpu_feature_info_;
gles2::TraceOutputter outputter_;
std::unique_ptr<FakeCommandBufferServiceBase> command_buffer_service_;
MemoryTypeTracker memory_tracker_;
scoped_refptr<SharedContextState> context_state_;
gpu::Mailbox client_texture_mailbox_;
std::unique_ptr<RasterDecoder> decoder_;
int32_t shared_memory_id_ = 0;
uint32_t shared_memory_offset_ = 0;
raw_ptr<void> shared_memory_address_ = nullptr;
const size_t kSharedBufferSize = 2048;
const uint32_t kSharedMemoryOffset = 132;
uint32_t immediate_buffer_[64];
std::unique_ptr<SharedImageFactory> shared_image_factory_;
SharedImageManager shared_image_manager_;
raw_ptr<gl::GLDisplay> display_ = nullptr;
};
TEST_F(RasterDecoderOOPTest, CopyTexSubImage2DSizeMismatch) {
context_state_->set_need_context_state_reset(true);
// Create uninitialized source texture mailbox.
gpu::Mailbox source_texture_mailbox =
CreateMailbox(viz::SinglePlaneFormat::kRGBA_8888,
/*width=*/1, /*height=*/1,
/*cleared=*/true);
GLbyte mailboxes[sizeof(gpu::Mailbox) * 2];
CopyMailboxes(mailboxes, source_texture_mailbox, client_texture_mailbox_);
SharedImageRepresentationFactory repr_factory(shared_image_manager(),
nullptr);
auto representation =
repr_factory.ProduceSkia(client_texture_mailbox_, context_state_.get());
{
// This will initialize the bottom right corner of destination.
auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>();
cmd.Init(1, 1, 0, 0, 1, 1, mailboxes);
EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
EXPECT_EQ(GL_NO_ERROR, GetGLError());
EXPECT_EQ(representation->ClearedRect(), gfx::Rect(1, 1, 1, 1));
}
{
// Dest rect outside of dest bounds
auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>();
cmd.Init(2, 2, 0, 0, 1, 1, mailboxes);
EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
EXPECT_EQ(representation->ClearedRect(), gfx::Rect(1, 1, 1, 1));
}
{
// Source rect outside of source bounds
auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>();
cmd.Init(0, 0, 0, 0, 2, 2, mailboxes);
EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
EXPECT_EQ(representation->ClearedRect(), gfx::Rect(1, 1, 1, 1));
}
}
TEST_F(RasterDecoderOOPTest, CopyTexSubImage2DTwiceClearsUnclearedTexture) {
context_state_->set_need_context_state_reset(true);
// Create uninitialized source texture mailbox.
gpu::Mailbox source_texture_mailbox =
CreateMailbox(viz::SinglePlaneFormat::kRGBA_8888,
/*width=*/2, /*height=*/2,
/*cleared=*/true);
GLbyte mailboxes[sizeof(gpu::Mailbox) * 2];
CopyMailboxes(mailboxes, source_texture_mailbox, client_texture_mailbox_);
SharedImageRepresentationFactory repr_factory(shared_image_manager(),
nullptr);
auto representation =
repr_factory.ProduceSkia(client_texture_mailbox_, context_state_.get());
EXPECT_FALSE(representation->IsCleared());
// This will initialize the top half of destination.
{
auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>();
cmd.Init(0, 0, 0, 0, 2, 1, mailboxes);
EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
}
EXPECT_EQ(gfx::Rect(0, 0, 2, 1), representation->ClearedRect());
EXPECT_FALSE(representation->IsCleared());
// This will initialize bottom half of the destination.
{
auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>();
cmd.Init(0, 1, 0, 0, 2, 1, mailboxes);
EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
}
EXPECT_TRUE(representation->IsCleared());
}
// Unlike the GLES2 version, RasterInterface's CopySharedImage does not allow
// initializing a texture in parts *unless* the rectangles being cleared
// can be trivially combined into a larger rectangle.
TEST_F(RasterDecoderOOPTest, CopyTexSubImage2DPartialFailsWithUnalignedRect) {
context_state_->set_need_context_state_reset(true);
// Create uninitialized source texture mailbox.
gpu::Mailbox source_texture_mailbox =
CreateMailbox(viz::SinglePlaneFormat::kRGBA_8888,
/*width=*/2, /*height=*/2,
/*cleared=*/true);
GLbyte mailboxes[sizeof(gpu::Mailbox) * 2];
CopyMailboxes(mailboxes, source_texture_mailbox, client_texture_mailbox_);
SharedImageRepresentationFactory repr_factory(shared_image_manager(),
nullptr);
auto representation =
repr_factory.ProduceSkia(client_texture_mailbox_, context_state_.get());
EXPECT_FALSE(representation->IsCleared());
// This will initialize the top half of destination.
{
auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>();
cmd.Init(0, 0, 0, 0, 2, 1, mailboxes);
EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
}
EXPECT_EQ(gfx::Rect(0, 0, 2, 1), representation->ClearedRect());
EXPECT_FALSE(representation->IsCleared());
// This will attempt to initialize the bottom corner of the destination. As
// the new rect cannot be trivially combined with the previous cleared rect,
// this will fail.
{
auto& cmd = *GetImmediateAs<cmds::CopySharedImageINTERNALImmediate>();
cmd.Init(1, 1, 0, 0, 1, 1, mailboxes);
EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
}
EXPECT_EQ(gfx::Rect(0, 0, 2, 1), representation->ClearedRect());
EXPECT_FALSE(representation->IsCleared());
}
TEST_F(RasterDecoderOOPTest, StateRestoreAcrossDecoders) {
// First decoder receives a skia command requiring context state reset.
auto decoder1 = CreateDecoder();
EXPECT_FALSE(context_state_->need_context_state_reset());
decoder1->MakeCurrent();
decoder1->SetUpForRasterCHROMIUMForTest();
cmds::EndRasterCHROMIUM end_raster_cmd;
end_raster_cmd.Init();
EXPECT_FALSE(error::IsError(ExecuteCmd(decoder1.get(), end_raster_cmd)));
EXPECT_TRUE(context_state_->need_context_state_reset());
// Another decoder receives a command which does not require consistent state,
// it should be processed without state restoration.
auto decoder2 = CreateDecoder();
decoder2->MakeCurrent();
decoder2->SetUpForRasterCHROMIUMForTest();
EXPECT_FALSE(error::IsError(ExecuteCmd(decoder2.get(), end_raster_cmd)));
EXPECT_TRUE(context_state_->need_context_state_reset());
decoder1->Destroy(true);
context_state_->MakeCurrent(nullptr);
decoder2->Destroy(true);
// Make sure the context is preserved across decoders.
EXPECT_FALSE(context_state_->gr_context()->abandoned());
}
} // namespace raster
} // namespace gpu