| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // 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_unittest_base.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "base/command_line.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_split.h" |
| #include "components/viz/common/resources/resource_format_utils.h" |
| #include "gpu/command_buffer/common/gles2_cmd_utils.h" |
| #include "gpu/command_buffer/common/raster_cmd_format.h" |
| #include "gpu/command_buffer/service/context_group.h" |
| #include "gpu/command_buffer/service/copy_texture_chromium_mock.h" |
| #include "gpu/command_buffer/service/gpu_switches.h" |
| #include "gpu/command_buffer/service/logger.h" |
| #include "gpu/command_buffer/service/mailbox_manager.h" |
| #include "gpu/command_buffer/service/program_manager.h" |
| #include "gpu/command_buffer/service/raster_decoder_context_state.h" |
| #include "gpu/command_buffer/service/service_utils.h" |
| #include "gpu/command_buffer/service/test_helper.h" |
| #include "gpu/command_buffer/service/vertex_attrib_manager.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gl/gl_mock.h" |
| #include "ui/gl/init/gl_factory.h" |
| #include "ui/gl/test/gl_surface_test_support.h" |
| |
| using ::gl::MockGLInterface; |
| using ::testing::_; |
| using ::testing::AnyNumber; |
| using ::testing::AtMost; |
| using ::testing::Between; |
| using ::testing::InSequence; |
| using ::testing::Ne; |
| using ::testing::Pointee; |
| using ::testing::Return; |
| using ::testing::SetArgPointee; |
| using ::testing::StrictMock; |
| |
| namespace gpu { |
| namespace raster { |
| |
| RasterDecoderTestBase::InitState::InitState() = default; |
| RasterDecoderTestBase::InitState::~InitState() = default; |
| |
| RasterDecoderTestBase::RasterDecoderTestBase() |
| : surface_(nullptr), |
| context_(nullptr), |
| client_texture_id_(106), |
| shared_memory_id_(0), |
| shared_memory_offset_(0), |
| shared_memory_address_(nullptr), |
| shared_memory_base_(nullptr), |
| ignore_cached_state_for_test_(GetParam()), |
| shader_translator_cache_(gpu_preferences_), |
| copy_texture_manager_(nullptr) { |
| memset(immediate_buffer_, 0xEE, sizeof(immediate_buffer_)); |
| } |
| |
| RasterDecoderTestBase::~RasterDecoderTestBase() = default; |
| |
| void RasterDecoderTestBase::OnConsoleMessage(int32_t id, |
| const std::string& message) {} |
| void RasterDecoderTestBase::CacheShader(const std::string& key, |
| const std::string& shader) {} |
| void RasterDecoderTestBase::OnFenceSyncRelease(uint64_t release) {} |
| void RasterDecoderTestBase::OnDescheduleUntilFinished() {} |
| void RasterDecoderTestBase::OnRescheduleAfterFinished() {} |
| void RasterDecoderTestBase::OnSwapBuffers(uint64_t swap_id, uint32_t flags) {} |
| |
| void RasterDecoderTestBase::SetUp() { |
| InitDecoder(InitState()); |
| } |
| |
| void RasterDecoderTestBase::AddExpectationsForRestoreAttribState( |
| GLuint attrib) { |
| EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, _)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| |
| EXPECT_CALL(*gl_, VertexAttribPointer(attrib, _, _, _, _, _)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| |
| EXPECT_CALL(*gl_, VertexAttribDivisorANGLE(attrib, _)) |
| .Times(testing::AtMost(1)) |
| .RetiresOnSaturation(); |
| |
| EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, _)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| |
| if (attrib != 0 || group_->feature_info()->gl_version_info().is_es) { |
| // TODO(bajones): Not sure if I can tell which of these will be called |
| EXPECT_CALL(*gl_, EnableVertexAttribArray(attrib)) |
| .Times(testing::AtMost(1)) |
| .RetiresOnSaturation(); |
| |
| EXPECT_CALL(*gl_, DisableVertexAttribArray(attrib)) |
| .Times(testing::AtMost(1)) |
| .RetiresOnSaturation(); |
| } |
| } |
| |
| void RasterDecoderTestBase::SetupInitStateManualExpectations(bool es3_capable) { |
| if (es3_capable) { |
| EXPECT_CALL(*gl_, PixelStorei(GL_PACK_ROW_LENGTH, 0)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*gl_, PixelStorei(GL_UNPACK_ROW_LENGTH, 0)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*gl_, PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| if (group_->feature_info()->feature_flags().ext_window_rectangles) { |
| EXPECT_CALL(*gl_, WindowRectanglesEXT(GL_EXCLUSIVE_EXT, 0, nullptr)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| } |
| } |
| } |
| |
| void RasterDecoderTestBase::SetupInitStateManualExpectationsForDoLineWidth( |
| GLfloat width) { |
| EXPECT_CALL(*gl_, LineWidth(width)).Times(1).RetiresOnSaturation(); |
| } |
| |
| void RasterDecoderTestBase::ExpectEnableDisable(GLenum cap, bool enable) { |
| if (enable) { |
| EXPECT_CALL(*gl_, Enable(cap)).Times(1).RetiresOnSaturation(); |
| } else { |
| EXPECT_CALL(*gl_, Disable(cap)).Times(1).RetiresOnSaturation(); |
| } |
| } |
| |
| void RasterDecoderTestBase::CreateFakeTexture( |
| GLuint client_id, |
| GLuint service_id, |
| viz::ResourceFormat resource_format, |
| GLsizei width, |
| GLsizei height, |
| bool cleared) { |
| // Create texture and temporary ref. |
| const GLuint kTempClientId = 271828; |
| auto* temp_ref = |
| group_->texture_manager()->CreateTexture(kTempClientId, service_id); |
| group_->texture_manager()->SetTarget(temp_ref, GL_TEXTURE_2D); |
| group_->texture_manager()->SetLevelInfo( |
| temp_ref, GL_TEXTURE_2D, 0, viz::GLInternalFormat(resource_format), |
| /*width=*/width, /*height=*/height, 1, 0, |
| viz::GLDataFormat(resource_format), viz::GLDataType(resource_format), |
| cleared ? gfx::Rect(width, height) : gfx::Rect()); |
| gpu::Mailbox mailbox = gpu::Mailbox::Generate(); |
| group_->mailbox_manager()->ProduceTexture(mailbox, temp_ref->texture()); |
| |
| // Consume texture to hold a permanent ref. |
| cmds::CreateAndConsumeTextureINTERNALImmediate& cmd = |
| *GetImmediateAs<cmds::CreateAndConsumeTextureINTERNALImmediate>(); |
| cmd.Init(client_id, false /* use_buffer */, gfx::BufferUsage::GPU_READ, |
| resource_format, mailbox.name); |
| EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailbox.name))); |
| |
| // Check that client_texture_id has appropriate attributes. |
| auto* texture_ref = group().texture_manager()->GetTexture(client_id); |
| ASSERT_NE(texture_ref, nullptr); |
| auto* texture = texture_ref->texture(); |
| EXPECT_EQ(service_id, texture->service_id()); |
| |
| // Release temporary ref. |
| group_->texture_manager()->RemoveTexture(kTempClientId); |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| } |
| |
| void RasterDecoderTestBase::InitDecoder(const InitState& init) { |
| std::string all_extensions; |
| for (const std::string& extension : init.extensions) { |
| all_extensions += extension + " "; |
| } |
| const bool bind_generates_resource(false); |
| const ContextType context_type(CONTEXT_TYPE_OPENGLES2); |
| |
| // For easier substring/extension matching |
| gl::SetGLGetProcAddressProc(gl::MockGLInterface::GetGLProcAddress); |
| gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings(); |
| |
| gl_.reset(new StrictMock<MockGLInterface>()); |
| ::gl::MockGLInterface::SetGLInterface(gl_.get()); |
| |
| GpuFeatureInfo gpu_feature_info; |
| scoped_refptr<gles2::FeatureInfo> feature_info = |
| new gles2::FeatureInfo(init.workarounds, gpu_feature_info); |
| |
| group_ = scoped_refptr<gles2::ContextGroup>(new gles2::ContextGroup( |
| gpu_preferences_, false, &mailbox_manager_, nullptr /* memory_tracker */, |
| &shader_translator_cache_, &framebuffer_completeness_cache_, feature_info, |
| bind_generates_resource, &image_manager_, nullptr /* image_factory */, |
| nullptr /* progress_reporter */, gpu_feature_info, &discardable_manager_, |
| nullptr /* passthrough_discardable_manager */, &shared_image_manager_)); |
| |
| InSequence sequence; |
| |
| surface_ = new gl::GLSurfaceStub; |
| |
| // Context needs to be created before initializing ContextGroup, which willxo |
| // in turn initialize FeatureInfo, which needs a context to determine |
| // extension support. |
| context_ = new StrictMock<GLContextMock>(); |
| context_->SetExtensionsString(all_extensions.c_str()); |
| context_->SetGLVersionString(init.gl_version.c_str()); |
| |
| context_->GLContextStub::MakeCurrent(surface_.get()); |
| |
| gles2::TestHelper::SetupContextGroupInitExpectations( |
| gl_.get(), gles2::DisallowedFeatures(), all_extensions.c_str(), |
| init.gl_version.c_str(), context_type, bind_generates_resource); |
| |
| // We initialize the ContextGroup with a MockRasterDecoder so that |
| // we can use the ContextGroup to figure out how the real RasterDecoder |
| // will initialize itself. |
| command_buffer_service_.reset(new FakeCommandBufferServiceBase()); |
| command_buffer_service_for_mock_decoder_.reset( |
| new FakeCommandBufferServiceBase()); |
| mock_decoder_.reset( |
| new MockRasterDecoder(command_buffer_service_for_mock_decoder_.get())); |
| |
| EXPECT_EQ(group_->Initialize(mock_decoder_.get(), context_type, |
| gles2::DisallowedFeatures()), |
| gpu::ContextResult::kSuccess); |
| |
| scoped_refptr<gpu::Buffer> buffer = |
| command_buffer_service_->CreateTransferBufferHelper(kSharedBufferSize, |
| &shared_memory_id_); |
| shared_memory_offset_ = kSharedMemoryOffset; |
| shared_memory_address_ = |
| static_cast<int8_t*>(buffer->memory()) + shared_memory_offset_; |
| shared_memory_base_ = buffer->memory(); |
| ClearSharedMemory(); |
| |
| ContextCreationAttribs attribs; |
| attribs.lose_context_when_out_of_memory = |
| init.lose_context_when_out_of_memory; |
| attribs.context_type = context_type; |
| |
| // Setup expections for RasterDecoderContextState::InitializeGL() |
| // It will initialize a FeatureInfo and |
| gles2::TestHelper::SetupFeatureInfoInitExpectationsWithGLVersion( |
| gl_.get(), all_extensions.c_str(), "" /* gl_renderer */, |
| init.gl_version.c_str(), CONTEXT_TYPE_OPENGLES2); |
| EXPECT_CALL(*gl_, GetIntegerv(GL_MAX_VERTEX_ATTRIBS, _)) |
| .WillOnce(SetArgPointee<1>(8u)) |
| .RetiresOnSaturation(); |
| SetupInitCapabilitiesExpectations(group_->feature_info()->IsES3Capable()); |
| SetupInitStateExpectations(group_->feature_info()->IsES3Capable()); |
| |
| raster_decoder_context_state_ = new raster::RasterDecoderContextState( |
| new gl::GLShareGroup(), surface_, context_, |
| feature_info->workarounds().use_virtualized_gl_contexts, |
| base::DoNothing()); |
| |
| raster_decoder_context_state_->InitializeGL(init.workarounds, |
| gpu_feature_info); |
| |
| decoder_.reset(RasterDecoder::Create(this, command_buffer_service_.get(), |
| &outputter_, group_.get(), |
| raster_decoder_context_state_)); |
| decoder_->SetIgnoreCachedStateForTest(ignore_cached_state_for_test_); |
| decoder_->GetLogger()->set_log_synthesized_gl_errors(false); |
| |
| copy_texture_manager_ = new gles2::MockCopyTextureResourceManager(); |
| decoder_->SetCopyTextureResourceManagerForTest(copy_texture_manager_); |
| |
| ASSERT_EQ( |
| decoder_->Initialize(surface_, raster_decoder_context_state_->context(), |
| true, gles2::DisallowedFeatures(), attribs), |
| gpu::ContextResult::kSuccess); |
| |
| EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(true)); |
| if (context_->WasAllocatedUsingRobustnessExtension()) { |
| EXPECT_CALL(*gl_, GetGraphicsResetStatusARB()) |
| .WillOnce(Return(GL_NO_ERROR)); |
| } |
| decoder_->MakeCurrent(); |
| decoder_->BeginDecoding(); |
| |
| CreateFakeTexture(client_texture_id_, kServiceTextureId, |
| viz::ResourceFormat::RGBA_8888, /*width=*/2, |
| /*height=*/2, /*cleared=*/false); |
| } |
| |
| void RasterDecoderTestBase::ResetDecoder() { |
| if (!decoder_.get()) |
| return; |
| // All Tests should have read all their GLErrors before getting here. |
| EXPECT_EQ(GL_NO_ERROR, GetGLError()); |
| |
| decoder_->EndDecoding(); |
| |
| if (!decoder_->WasContextLost()) { |
| EXPECT_CALL(*copy_texture_manager_, Destroy()) |
| .Times(1) |
| .RetiresOnSaturation(); |
| copy_texture_manager_ = nullptr; |
| } |
| |
| decoder_->Destroy(!decoder_->WasContextLost()); |
| decoder_.reset(); |
| group_->Destroy(mock_decoder_.get(), false); |
| command_buffer_service_.reset(); |
| command_buffer_service_for_mock_decoder_.reset(); |
| ::gl::MockGLInterface::SetGLInterface(nullptr); |
| gl_.reset(); |
| gl::init::ShutdownGL(false); |
| } |
| |
| void RasterDecoderTestBase::TearDown() { |
| ResetDecoder(); |
| } |
| |
| GLint RasterDecoderTestBase::GetGLError() { |
| EXPECT_CALL(*gl_, GetError()) |
| .WillOnce(Return(GL_NO_ERROR)) |
| .RetiresOnSaturation(); |
| cmds::GetError cmd; |
| cmd.Init(shared_memory_id_, shared_memory_offset_); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); |
| return static_cast<GLint>(*GetSharedMemoryAs<GLenum*>()); |
| } |
| |
| void RasterDecoderTestBase::SetBucketData(uint32_t bucket_id, |
| const void* data, |
| uint32_t data_size) { |
| DCHECK(data || data_size == 0); |
| cmd::SetBucketSize cmd1; |
| cmd1.Init(bucket_id, data_size); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd1)); |
| if (data) { |
| memcpy(shared_memory_address_, data, data_size); |
| cmd::SetBucketData cmd2; |
| cmd2.Init(bucket_id, 0, data_size, shared_memory_id_, kSharedMemoryOffset); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); |
| ClearSharedMemory(); |
| } |
| } |
| |
| void RasterDecoderTestBase::SetBucketAsCString(uint32_t bucket_id, |
| const char* str) { |
| SetBucketData(bucket_id, str, str ? (strlen(str) + 1) : 0); |
| } |
| |
| void RasterDecoderTestBase::SetBucketAsCStrings(uint32_t bucket_id, |
| GLsizei count, |
| const char** str, |
| GLsizei count_in_header, |
| char str_end) { |
| uint32_t header_size = sizeof(GLint) * (count + 1); |
| uint32_t total_size = header_size; |
| std::unique_ptr<GLint[]> header(new GLint[count + 1]); |
| header[0] = static_cast<GLint>(count_in_header); |
| for (GLsizei ii = 0; ii < count; ++ii) { |
| header[ii + 1] = str && str[ii] ? strlen(str[ii]) : 0; |
| total_size += header[ii + 1] + 1; |
| } |
| cmd::SetBucketSize cmd1; |
| cmd1.Init(bucket_id, total_size); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd1)); |
| memcpy(shared_memory_address_, header.get(), header_size); |
| uint32_t offset = header_size; |
| for (GLsizei ii = 0; ii < count; ++ii) { |
| if (str && str[ii]) { |
| size_t str_len = strlen(str[ii]); |
| memcpy(static_cast<char*>(shared_memory_address_) + offset, str[ii], |
| str_len); |
| offset += str_len; |
| } |
| memcpy(static_cast<char*>(shared_memory_address_) + offset, &str_end, 1); |
| offset += 1; |
| } |
| cmd::SetBucketData cmd2; |
| cmd2.Init(bucket_id, 0, total_size, shared_memory_id_, kSharedMemoryOffset); |
| EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2)); |
| ClearSharedMemory(); |
| } |
| |
| void RasterDecoderTestBase::DoDeleteTexture(GLuint client_id, |
| GLuint service_id) { |
| { |
| InSequence s; |
| |
| // Calling DoDeleteTexture will unbind the texture from any texture units |
| // it's currently bound to. |
| EXPECT_CALL(*gl_, BindTexture(_, 0)).Times(AnyNumber()); |
| |
| EXPECT_CALL(*gl_, DeleteTextures(1, Pointee(service_id))) |
| .Times(1) |
| .RetiresOnSaturation(); |
| |
| GenHelper<cmds::DeleteTexturesImmediate>(client_id); |
| } |
| } |
| |
| void RasterDecoderTestBase::SetScopedTextureBinderExpectations(GLenum target) { |
| // ScopedTextureBinder |
| EXPECT_CALL(*gl_, ActiveTexture(_)) |
| .Times(Between(1, 2)) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*gl_, BindTexture(target, Ne(0U))).Times(1).RetiresOnSaturation(); |
| if (!raster_decoder_context_state_->need_context_state_reset) |
| EXPECT_CALL(*gl_, BindTexture(target, 0)).Times(1).RetiresOnSaturation(); |
| } |
| |
| void RasterDecoderTestBase::SetupClearTextureExpectations( |
| GLuint service_id, |
| GLuint old_service_id, |
| GLenum bind_target, |
| GLenum target, |
| GLint level, |
| GLenum format, |
| GLenum type, |
| GLint xoffset, |
| GLint yoffset, |
| GLsizei width, |
| GLsizei height, |
| GLuint bound_pixel_unpack_buffer) { |
| EXPECT_CALL(*gl_, BindTexture(bind_target, service_id)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*gl_, PixelStorei(GL_UNPACK_ALIGNMENT, _)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| if (bound_pixel_unpack_buffer) { |
| EXPECT_CALL(*gl_, BindBuffer(GL_PIXEL_UNPACK_BUFFER, _)) |
| .Times(2) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*gl_, PixelStorei(GL_UNPACK_ROW_LENGTH, _)) |
| .Times(2) |
| .RetiresOnSaturation(); |
| EXPECT_CALL(*gl_, PixelStorei(GL_UNPACK_IMAGE_HEIGHT, _)) |
| .Times(2) |
| .RetiresOnSaturation(); |
| } |
| EXPECT_CALL(*gl_, TexSubImage2D(target, level, xoffset, yoffset, width, |
| height, format, type, _)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| #if DCHECK_IS_ON() |
| EXPECT_CALL(*gl_, GetError()) |
| .WillOnce(Return(GL_NO_ERROR)) |
| .RetiresOnSaturation(); |
| #endif |
| } |
| |
| // Include the auto-generated part of this file. We split this because it means |
| // we can easily edit the non-auto generated parts right here in this file |
| // instead of having to edit some template or the code generator. |
| #include "gpu/command_buffer/service/raster_decoder_unittest_0_autogen.h" |
| |
| // GCC requires these declarations, but MSVC requires they not be present |
| #ifndef COMPILER_MSVC |
| const GLint RasterDecoderTestBase::kMaxTextureSize; |
| const GLint RasterDecoderTestBase::kNumTextureUnits; |
| |
| const GLint RasterDecoderTestBase::kViewportX; |
| const GLint RasterDecoderTestBase::kViewportY; |
| const GLint RasterDecoderTestBase::kViewportWidth; |
| const GLint RasterDecoderTestBase::kViewportHeight; |
| |
| const GLuint RasterDecoderTestBase::kServiceBufferId; |
| const GLuint RasterDecoderTestBase::kServiceTextureId; |
| const GLuint RasterDecoderTestBase::kServiceVertexArrayId; |
| |
| const size_t RasterDecoderTestBase::kSharedBufferSize; |
| const uint32_t RasterDecoderTestBase::kSharedMemoryOffset; |
| const int32_t RasterDecoderTestBase::kInvalidSharedMemoryId; |
| const uint32_t RasterDecoderTestBase::kInvalidSharedMemoryOffset; |
| const uint32_t RasterDecoderTestBase::kInitialResult; |
| const uint8_t RasterDecoderTestBase::kInitialMemoryValue; |
| |
| const uint32_t RasterDecoderTestBase::kNewClientId; |
| const uint32_t RasterDecoderTestBase::kNewServiceId; |
| const uint32_t RasterDecoderTestBase::kInvalidClientId; |
| #endif |
| |
| } // namespace raster |
| } // namespace gpu |