blob: 96728876d19430cacd2505cd395c8f2b9bd7c975 [file] [log] [blame]
// 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/shared_image_backing_factory_gl_texture.h"
#include <thread>
#include "base/bind_helpers.h"
#include "base/optional.h"
#include "build/build_config.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/common/resources/resource_sizes.h"
#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/mailbox_manager_impl.h"
#include "gpu/command_buffer/service/service_utils.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image_backing.h"
#include "gpu/command_buffer/service/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image_manager.h"
#include "gpu/command_buffer/service/shared_image_representation.h"
#include "gpu/command_buffer/service/shared_image_test_utils.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "gpu/command_buffer/tests/texture_image_factory.h"
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "gpu/config/gpu_feature_info.h"
#include "gpu/config/gpu_preferences.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkPromiseImageTexture.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/color_space.h"
#include "ui/gl/buffer_format_utils.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_image_shared_memory.h"
#include "ui/gl/gl_image_stub.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/init/gl_factory.h"
namespace gpu {
namespace {
void CreateSharedContext(const GpuDriverBugWorkarounds& workarounds,
scoped_refptr<gl::GLSurface>& surface,
scoped_refptr<gl::GLContext>& context,
scoped_refptr<SharedContextState>& context_state,
scoped_refptr<gles2::FeatureInfo>& feature_info) {
surface = gl::init::CreateOffscreenGLSurface(gfx::Size());
ASSERT_TRUE(surface);
context =
gl::init::CreateGLContext(nullptr, surface.get(), gl::GLContextAttribs());
ASSERT_TRUE(context);
bool result = context->MakeCurrent(surface.get());
ASSERT_TRUE(result);
scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup();
feature_info =
base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo());
context_state = base::MakeRefCounted<SharedContextState>(
std::move(share_group), surface, context,
false /* use_virtualized_gl_contexts */, base::DoNothing());
context_state->InitializeGrContext(GpuPreferences(), workarounds, nullptr);
context_state->InitializeGL(GpuPreferences(), feature_info);
}
bool IsAndroid() {
#if defined(OS_ANDROID)
return true;
#else
return false;
#endif
}
class SharedImageBackingFactoryGLTextureTestBase
: public testing::TestWithParam<bool> {
public:
SharedImageBackingFactoryGLTextureTestBase(bool is_thread_safe)
: shared_image_manager_(
std::make_unique<SharedImageManager>(is_thread_safe)) {}
~SharedImageBackingFactoryGLTextureTestBase() {
// |context_state_| must be destroyed on its own context.
context_state_->MakeCurrent(surface_.get(), true /* needs_gl */);
}
void SetUpBase(const GpuDriverBugWorkarounds& workarounds,
ImageFactory* factory) {
scoped_refptr<gles2::FeatureInfo> feature_info;
CreateSharedContext(workarounds, surface_, context_, context_state_,
feature_info);
supports_etc1_ =
feature_info->validators()->compressed_texture_format.IsValid(
GL_ETC1_RGB8_OES);
GpuPreferences preferences;
preferences.use_passthrough_cmd_decoder = use_passthrough();
backing_factory_ = std::make_unique<SharedImageBackingFactoryGLTexture>(
preferences, workarounds, GpuFeatureInfo(), factory,
shared_image_manager_->batch_access_manager());
memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr);
shared_image_representation_factory_ =
std::make_unique<SharedImageRepresentationFactory>(
shared_image_manager_.get(), nullptr);
}
bool use_passthrough() {
return GetParam() && gles2::PassthroughCommandDecoderSupported();
}
bool supports_etc1() { return supports_etc1_; }
GrContext* gr_context() { return context_state_->gr_context(); }
protected:
scoped_refptr<gl::GLSurface> surface_;
scoped_refptr<gl::GLContext> context_;
scoped_refptr<SharedContextState> context_state_;
std::unique_ptr<SharedImageBackingFactoryGLTexture> backing_factory_;
gles2::MailboxManagerImpl mailbox_manager_;
std::unique_ptr<SharedImageManager> shared_image_manager_;
std::unique_ptr<MemoryTypeTracker> memory_type_tracker_;
std::unique_ptr<SharedImageRepresentationFactory>
shared_image_representation_factory_;
bool supports_etc1_ = false;
};
class SharedImageBackingFactoryGLTextureTest
: public SharedImageBackingFactoryGLTextureTestBase {
public:
SharedImageBackingFactoryGLTextureTest()
: SharedImageBackingFactoryGLTextureTestBase(false) {}
void SetUp() override {
GpuDriverBugWorkarounds workarounds;
workarounds.max_texture_size = INT_MAX - 1;
SetUpBase(workarounds, &image_factory_);
}
protected:
TextureImageFactory image_factory_;
};
class SharedImageBackingFactoryGLTextureThreadSafeTest
: public SharedImageBackingFactoryGLTextureTestBase {
public:
SharedImageBackingFactoryGLTextureThreadSafeTest()
: SharedImageBackingFactoryGLTextureTestBase(true) {}
~SharedImageBackingFactoryGLTextureThreadSafeTest() {
// |context_state2_| must be destroyed on its own context.
context_state2_->MakeCurrent(surface2_.get(), true /* needs_gl */);
}
void SetUp() override {
GpuDriverBugWorkarounds workarounds;
workarounds.max_texture_size = INT_MAX - 1;
SetUpBase(workarounds, &image_factory_);
// Create 2nd context/context_state which are not part of same shared group.
scoped_refptr<gles2::FeatureInfo> feature_info;
CreateSharedContext(workarounds, surface2_, context2_, context_state2_,
feature_info);
feature_info.reset();
}
protected:
scoped_refptr<gl::GLSurface> surface2_;
scoped_refptr<gl::GLContext> context2_;
scoped_refptr<SharedContextState> context_state2_;
TextureImageFactory image_factory_;
};
class CreateAndValidateSharedImageRepresentations {
public:
CreateAndValidateSharedImageRepresentations(
SharedImageBackingFactoryGLTexture* backing_factory,
bool is_thread_safe,
gles2::MailboxManagerImpl* mailbox_manager,
SharedImageManager* shared_image_manager,
MemoryTypeTracker* memory_type_tracker,
SharedImageRepresentationFactory* shared_image_representation_factory,
SharedContextState* context_state);
~CreateAndValidateSharedImageRepresentations();
gfx::Size size() { return size_; }
Mailbox mailbox() { return mailbox_; }
private:
gles2::MailboxManagerImpl* mailbox_manager_;
gfx::Size size_;
Mailbox mailbox_;
std::unique_ptr<SharedImageBacking> backing_;
std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image_;
};
TEST_P(SharedImageBackingFactoryGLTextureTest, Basic) {
auto mailbox = Mailbox::GenerateForSharedImage();
auto format = viz::ResourceFormat::RGBA_8888;
gfx::Size size(256, 256);
auto color_space = gfx::ColorSpace::CreateSRGB();
uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
auto backing = backing_factory_->CreateSharedImage(
mailbox, format, size, color_space, usage, false /* is_thread_safe */);
EXPECT_TRUE(backing);
// Check clearing.
if (!backing->IsCleared()) {
backing->SetCleared();
EXPECT_TRUE(backing->IsCleared());
}
// First, validate via a legacy mailbox.
EXPECT_TRUE(backing->ProduceLegacyMailbox(&mailbox_manager_));
TextureBase* texture_base = mailbox_manager_.ConsumeTexture(mailbox);
ASSERT_TRUE(texture_base);
GLenum expected_target = GL_TEXTURE_2D;
EXPECT_EQ(texture_base->target(), expected_target);
if (!use_passthrough()) {
auto* texture = static_cast<gles2::Texture*>(texture_base);
EXPECT_TRUE(texture->IsImmutable());
int width, height, depth;
bool has_level =
texture->GetLevelSize(GL_TEXTURE_2D, 0, &width, &height, &depth);
EXPECT_TRUE(has_level);
EXPECT_EQ(width, size.width());
EXPECT_EQ(height, size.height());
}
// Next, validate via a SharedImageRepresentationGLTexture.
std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
shared_image_manager_->Register(std::move(backing),
memory_type_tracker_.get());
EXPECT_TRUE(shared_image);
if (!use_passthrough()) {
auto gl_representation =
shared_image_representation_factory_->ProduceGLTexture(mailbox);
EXPECT_TRUE(gl_representation);
EXPECT_TRUE(gl_representation->GetTexture()->service_id());
EXPECT_EQ(expected_target, gl_representation->GetTexture()->target());
EXPECT_EQ(size, gl_representation->size());
EXPECT_EQ(format, gl_representation->format());
EXPECT_EQ(color_space, gl_representation->color_space());
EXPECT_EQ(usage, gl_representation->usage());
gl_representation.reset();
}
// Next, validate a SharedImageRepresentationGLTexturePassthrough.
if (use_passthrough()) {
auto gl_representation =
shared_image_representation_factory_->ProduceGLTexturePassthrough(
mailbox);
EXPECT_TRUE(gl_representation);
EXPECT_TRUE(gl_representation->GetTexturePassthrough()->service_id());
EXPECT_EQ(expected_target,
gl_representation->GetTexturePassthrough()->target());
EXPECT_EQ(size, gl_representation->size());
EXPECT_EQ(format, gl_representation->format());
EXPECT_EQ(color_space, gl_representation->color_space());
EXPECT_EQ(usage, gl_representation->usage());
gl_representation.reset();
}
// Finally, validate a SharedImageRepresentationSkia.
auto skia_representation = shared_image_representation_factory_->ProduceSkia(
mailbox, context_state_.get());
EXPECT_TRUE(skia_representation);
std::vector<GrBackendSemaphore> begin_semaphores;
std::vector<GrBackendSemaphore> end_semaphores;
std::unique_ptr<SharedImageRepresentationSkia::ScopedWriteAccess>
scoped_write_access;
scoped_write_access = skia_representation->BeginScopedWriteAccess(
&begin_semaphores, &end_semaphores,
SharedImageRepresentation::AllowUnclearedAccess::kYes);
auto* surface = scoped_write_access->surface();
EXPECT_TRUE(surface);
EXPECT_EQ(size.width(), surface->width());
EXPECT_EQ(size.height(), surface->height());
EXPECT_TRUE(begin_semaphores.empty());
EXPECT_TRUE(end_semaphores.empty());
scoped_write_access.reset();
std::unique_ptr<SharedImageRepresentationSkia::ScopedReadAccess>
scoped_read_access;
scoped_read_access = skia_representation->BeginScopedReadAccess(
&begin_semaphores, &end_semaphores);
auto* promise_texture = scoped_read_access->promise_image_texture();
EXPECT_TRUE(promise_texture);
EXPECT_TRUE(begin_semaphores.empty());
EXPECT_TRUE(end_semaphores.empty());
GrBackendTexture backend_texture = promise_texture->backendTexture();
EXPECT_TRUE(backend_texture.isValid());
EXPECT_EQ(size.width(), backend_texture.width());
EXPECT_EQ(size.height(), backend_texture.height());
scoped_read_access.reset();
skia_representation.reset();
shared_image.reset();
EXPECT_FALSE(mailbox_manager_.ConsumeTexture(mailbox));
}
TEST_P(SharedImageBackingFactoryGLTextureTest, Image) {
auto mailbox = Mailbox::GenerateForSharedImage();
auto format = viz::ResourceFormat::RGBA_8888;
gfx::Size size(256, 256);
auto color_space = gfx::ColorSpace::CreateSRGB();
uint32_t usage = SHARED_IMAGE_USAGE_SCANOUT;
auto backing = backing_factory_->CreateSharedImage(
mailbox, format, size, color_space, usage, false /* is_thread_safe */);
EXPECT_TRUE(backing);
// Check clearing.
if (!backing->IsCleared()) {
backing->SetCleared();
EXPECT_TRUE(backing->IsCleared());
}
// First, validate via a legacy mailbox.
EXPECT_TRUE(backing->ProduceLegacyMailbox(&mailbox_manager_));
TextureBase* texture_base = mailbox_manager_.ConsumeTexture(mailbox);
ASSERT_TRUE(texture_base);
GLenum target = texture_base->target();
scoped_refptr<gl::GLImage> image;
if (use_passthrough()) {
auto* texture = static_cast<gles2::TexturePassthrough*>(texture_base);
image = texture->GetLevelImage(target, 0);
} else {
auto* texture = static_cast<gles2::Texture*>(texture_base);
image = texture->GetLevelImage(target, 0);
}
ASSERT_TRUE(image);
EXPECT_EQ(size, image->GetSize());
// Next, validate via a SharedImageRepresentationGLTexture.
std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
shared_image_manager_->Register(std::move(backing),
memory_type_tracker_.get());
EXPECT_TRUE(shared_image);
if (!use_passthrough()) {
auto gl_representation =
shared_image_representation_factory_->ProduceGLTexture(mailbox);
EXPECT_TRUE(gl_representation);
EXPECT_TRUE(gl_representation->GetTexture()->service_id());
EXPECT_EQ(size, gl_representation->size());
EXPECT_EQ(format, gl_representation->format());
EXPECT_EQ(color_space, gl_representation->color_space());
EXPECT_EQ(usage, gl_representation->usage());
gl_representation.reset();
auto gl_representation_rgb =
shared_image_representation_factory_->ProduceRGBEmulationGLTexture(
mailbox);
EXPECT_TRUE(gl_representation_rgb);
EXPECT_TRUE(gl_representation_rgb->GetTexture()->service_id());
EXPECT_EQ(size, gl_representation_rgb->size());
EXPECT_EQ(format, gl_representation_rgb->format());
EXPECT_EQ(color_space, gl_representation_rgb->color_space());
EXPECT_EQ(usage, gl_representation_rgb->usage());
gl_representation_rgb.reset();
}
// Next, validate a SharedImageRepresentationGLTexturePassthrough.
if (use_passthrough()) {
auto gl_representation =
shared_image_representation_factory_->ProduceGLTexturePassthrough(
mailbox);
EXPECT_TRUE(gl_representation);
EXPECT_TRUE(gl_representation->GetTexturePassthrough()->service_id());
EXPECT_EQ(size, gl_representation->size());
EXPECT_EQ(format, gl_representation->format());
EXPECT_EQ(color_space, gl_representation->color_space());
EXPECT_EQ(usage, gl_representation->usage());
gl_representation.reset();
}
// Finally, validate a SharedImageRepresentationSkia.
auto skia_representation = shared_image_representation_factory_->ProduceSkia(
mailbox, context_state_.get());
EXPECT_TRUE(skia_representation);
std::vector<GrBackendSemaphore> begin_semaphores;
std::vector<GrBackendSemaphore> end_semaphores;
std::unique_ptr<SharedImageRepresentationSkia::ScopedWriteAccess>
scoped_write_access;
scoped_write_access = skia_representation->BeginScopedWriteAccess(
&begin_semaphores, &end_semaphores,
SharedImageRepresentation::AllowUnclearedAccess::kYes);
auto* surface = scoped_write_access->surface();
EXPECT_TRUE(surface);
EXPECT_EQ(size.width(), surface->width());
EXPECT_EQ(size.height(), surface->height());
scoped_write_access.reset();
std::unique_ptr<SharedImageRepresentationSkia::ScopedReadAccess>
scoped_read_access;
scoped_read_access = skia_representation->BeginScopedReadAccess(
&begin_semaphores, &end_semaphores);
auto* promise_texture = scoped_read_access->promise_image_texture();
EXPECT_TRUE(promise_texture);
EXPECT_TRUE(begin_semaphores.empty());
EXPECT_TRUE(end_semaphores.empty());
if (promise_texture) {
GrBackendTexture backend_texture = promise_texture->backendTexture();
EXPECT_TRUE(backend_texture.isValid());
EXPECT_EQ(size.width(), backend_texture.width());
EXPECT_EQ(size.height(), backend_texture.height());
}
scoped_read_access.reset();
skia_representation.reset();
shared_image.reset();
EXPECT_FALSE(mailbox_manager_.ConsumeTexture(mailbox));
if (!use_passthrough() &&
context_state_->feature_info()->feature_flags().ext_texture_rg) {
// Create a R-8 image texture, and check that the internal_format is that of
// the image (GL_RGBA for TextureImageFactory). This only matters for the
// validating decoder.
auto format = viz::ResourceFormat::RED_8;
backing = backing_factory_->CreateSharedImage(
mailbox, format, size, color_space, usage, false /* is_thread_safe */);
EXPECT_TRUE(backing);
shared_image = shared_image_manager_->Register(std::move(backing),
memory_type_tracker_.get());
auto gl_representation =
shared_image_representation_factory_->ProduceGLTexture(mailbox);
ASSERT_TRUE(gl_representation);
gles2::Texture* texture = gl_representation->GetTexture();
ASSERT_TRUE(texture);
GLenum type = 0;
GLenum internal_format = 0;
EXPECT_TRUE(texture->GetLevelType(target, 0, &type, &internal_format));
EXPECT_EQ(internal_format, static_cast<GLenum>(GL_RGBA));
gl_representation.reset();
shared_image.reset();
}
}
TEST_P(SharedImageBackingFactoryGLTextureTest, InitialData) {
for (auto format :
{viz::ResourceFormat::RGBA_8888, viz::ResourceFormat::ETC1}) {
if (format == viz::ResourceFormat::ETC1 && !supports_etc1())
continue;
auto mailbox = Mailbox::GenerateForSharedImage();
gfx::Size size(256, 256);
auto color_space = gfx::ColorSpace::CreateSRGB();
uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
std::vector<uint8_t> initial_data(
viz::ResourceSizes::CheckedSizeInBytes<unsigned int>(size, format));
auto backing = backing_factory_->CreateSharedImage(
mailbox, format, size, color_space, usage, initial_data);
EXPECT_TRUE(backing);
EXPECT_TRUE(backing->IsCleared());
// Validate via a SharedImageRepresentationGLTexture(Passthrough).
std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
shared_image_manager_->Register(std::move(backing),
memory_type_tracker_.get());
EXPECT_TRUE(shared_image);
GLenum expected_target = GL_TEXTURE_2D;
if (!use_passthrough()) {
auto gl_representation =
shared_image_representation_factory_->ProduceGLTexture(mailbox);
EXPECT_TRUE(gl_representation);
EXPECT_TRUE(gl_representation->GetTexture()->service_id());
EXPECT_EQ(expected_target, gl_representation->GetTexture()->target());
EXPECT_EQ(size, gl_representation->size());
EXPECT_EQ(format, gl_representation->format());
EXPECT_EQ(color_space, gl_representation->color_space());
EXPECT_EQ(usage, gl_representation->usage());
gl_representation.reset();
} else {
auto gl_representation =
shared_image_representation_factory_->ProduceGLTexturePassthrough(
mailbox);
EXPECT_TRUE(gl_representation);
EXPECT_TRUE(gl_representation->GetTexturePassthrough()->service_id());
EXPECT_EQ(expected_target,
gl_representation->GetTexturePassthrough()->target());
EXPECT_EQ(size, gl_representation->size());
EXPECT_EQ(format, gl_representation->format());
EXPECT_EQ(color_space, gl_representation->color_space());
EXPECT_EQ(usage, gl_representation->usage());
gl_representation.reset();
}
shared_image.reset();
EXPECT_FALSE(mailbox_manager_.ConsumeTexture(mailbox));
}
}
TEST_P(SharedImageBackingFactoryGLTextureTest, InitialDataImage) {
auto mailbox = Mailbox::GenerateForSharedImage();
auto format = viz::ResourceFormat::RGBA_8888;
gfx::Size size(256, 256);
auto color_space = gfx::ColorSpace::CreateSRGB();
uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_SCANOUT;
std::vector<uint8_t> initial_data(256 * 256 * 4);
auto backing = backing_factory_->CreateSharedImage(
mailbox, format, size, color_space, usage, initial_data);
// Validate via a SharedImageRepresentationGLTexture(Passthrough).
std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
shared_image_manager_->Register(std::move(backing),
memory_type_tracker_.get());
EXPECT_TRUE(shared_image);
if (!use_passthrough()) {
auto gl_representation =
shared_image_representation_factory_->ProduceGLTexture(mailbox);
EXPECT_TRUE(gl_representation);
EXPECT_TRUE(gl_representation->GetTexture()->service_id());
EXPECT_EQ(size, gl_representation->size());
EXPECT_EQ(format, gl_representation->format());
EXPECT_EQ(color_space, gl_representation->color_space());
EXPECT_EQ(usage, gl_representation->usage());
gl_representation.reset();
} else {
auto gl_representation =
shared_image_representation_factory_->ProduceGLTexturePassthrough(
mailbox);
EXPECT_TRUE(gl_representation);
EXPECT_TRUE(gl_representation->GetTexturePassthrough()->service_id());
EXPECT_EQ(size, gl_representation->size());
EXPECT_EQ(format, gl_representation->format());
EXPECT_EQ(color_space, gl_representation->color_space());
EXPECT_EQ(usage, gl_representation->usage());
gl_representation.reset();
}
}
TEST_P(SharedImageBackingFactoryGLTextureTest, InitialDataWrongSize) {
auto mailbox = Mailbox::GenerateForSharedImage();
auto format = viz::ResourceFormat::RGBA_8888;
gfx::Size size(256, 256);
auto color_space = gfx::ColorSpace::CreateSRGB();
uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
std::vector<uint8_t> initial_data_small(256 * 128 * 4);
std::vector<uint8_t> initial_data_large(256 * 512 * 4);
auto backing = backing_factory_->CreateSharedImage(
mailbox, format, size, color_space, usage, initial_data_small);
EXPECT_FALSE(backing);
backing = backing_factory_->CreateSharedImage(
mailbox, format, size, color_space, usage, initial_data_large);
EXPECT_FALSE(backing);
}
TEST_P(SharedImageBackingFactoryGLTextureTest, InvalidFormat) {
auto mailbox = Mailbox::GenerateForSharedImage();
auto format = viz::ResourceFormat::YUV_420_BIPLANAR;
gfx::Size size(256, 256);
auto color_space = gfx::ColorSpace::CreateSRGB();
uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
auto backing = backing_factory_->CreateSharedImage(
mailbox, format, size, color_space, usage, false /* is_thread_safe */);
EXPECT_FALSE(backing);
}
TEST_P(SharedImageBackingFactoryGLTextureTest, InvalidSize) {
auto mailbox = Mailbox::GenerateForSharedImage();
auto format = viz::ResourceFormat::RGBA_8888;
gfx::Size size(0, 0);
auto color_space = gfx::ColorSpace::CreateSRGB();
uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
auto backing = backing_factory_->CreateSharedImage(
mailbox, format, size, color_space, usage, false /* is_thread_safe */);
EXPECT_FALSE(backing);
size = gfx::Size(INT_MAX, INT_MAX);
backing = backing_factory_->CreateSharedImage(
mailbox, format, size, color_space, usage, false /* is_thread_safe */);
EXPECT_FALSE(backing);
}
TEST_P(SharedImageBackingFactoryGLTextureTest, EstimatedSize) {
auto mailbox = Mailbox::GenerateForSharedImage();
auto format = viz::ResourceFormat::RGBA_8888;
gfx::Size size(256, 256);
auto color_space = gfx::ColorSpace::CreateSRGB();
uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
auto backing = backing_factory_->CreateSharedImage(
mailbox, format, size, color_space, usage, false /* is_thread_safe */);
EXPECT_TRUE(backing);
size_t backing_estimated_size = backing->estimated_size();
EXPECT_GT(backing_estimated_size, 0u);
std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
shared_image_manager_->Register(std::move(backing),
memory_type_tracker_.get());
EXPECT_EQ(backing_estimated_size, memory_type_tracker_->GetMemRepresented());
shared_image.reset();
}
// Ensures that the various conversion functions used w/ TexStorage2D match
// their TexImage2D equivalents, allowing us to minimize the amount of parallel
// data tracked in the SharedImageFactoryGLTexture.
TEST_P(SharedImageBackingFactoryGLTextureTest, TexImageTexStorageEquivalence) {
scoped_refptr<gles2::FeatureInfo> feature_info =
new gles2::FeatureInfo(GpuDriverBugWorkarounds(), GpuFeatureInfo());
feature_info->Initialize(ContextType::CONTEXT_TYPE_OPENGLES2,
use_passthrough(), gles2::DisallowedFeatures());
const gles2::Validators* validators = feature_info->validators();
for (int i = 0; i <= viz::RESOURCE_FORMAT_MAX; ++i) {
auto format = static_cast<viz::ResourceFormat>(i);
if (!viz::GLSupportsFormat(format) ||
viz::IsResourceFormatCompressed(format))
continue;
int storage_format = viz::TextureStorageFormat(format);
int image_gl_format = viz::GLDataFormat(format);
int storage_gl_format =
gles2::TextureManager::ExtractFormatFromStorageFormat(storage_format);
EXPECT_EQ(image_gl_format, storage_gl_format);
int image_gl_type = viz::GLDataType(format);
int storage_gl_type =
gles2::TextureManager::ExtractTypeFromStorageFormat(storage_format);
// Ignore the HALF_FLOAT / HALF_FLOAT_OES discrepancy for now.
// TODO(ericrk): Figure out if we need additional action to support
// HALF_FLOAT.
if (!(image_gl_type == GL_HALF_FLOAT_OES &&
storage_gl_type == GL_HALF_FLOAT)) {
EXPECT_EQ(image_gl_type, storage_gl_type);
}
// confirm that we support TexStorage2D only if we support TexImage2D:
int image_internal_format = viz::GLInternalFormat(format);
bool supports_tex_image =
validators->texture_internal_format.IsValid(image_internal_format) &&
validators->texture_format.IsValid(image_gl_format) &&
validators->pixel_type.IsValid(image_gl_type);
bool supports_tex_storage =
validators->texture_internal_format_storage.IsValid(storage_format);
if (supports_tex_storage)
EXPECT_TRUE(supports_tex_image);
}
}
class StubImage : public gl::GLImageStub {
public:
StubImage(const gfx::Size& size, gfx::BufferFormat format)
: size_(size), format_(format) {}
gfx::Size GetSize() override { return size_; }
unsigned GetInternalFormat() override {
return gl::BufferFormatToGLInternalFormat(format_);
}
unsigned GetDataType() override {
return gl::BufferFormatToGLDataType(format_);
}
BindOrCopy ShouldBindOrCopy() override { return BIND; }
bool BindTexImage(unsigned target) override {
if (!bound_) {
bound_ = true;
++update_counter_;
}
return true;
}
bool BindTexImageWithInternalformat(unsigned target,
unsigned internal_format) override {
internal_format_ = internal_format;
if (!bound_) {
bound_ = true;
++update_counter_;
}
return true;
}
void ReleaseTexImage(unsigned target) override { bound_ = false; }
bool bound() const { return bound_; }
int update_counter() const { return update_counter_; }
unsigned internal_format() const { return internal_format_; }
private:
~StubImage() override = default;
gfx::Size size_;
gfx::BufferFormat format_;
bool bound_ = false;
int update_counter_ = 0;
unsigned internal_format_ = GL_RGBA;
};
class SharedImageBackingFactoryGLTextureWithGMBTest
: public SharedImageBackingFactoryGLTextureTestBase,
public gpu::ImageFactory {
public:
SharedImageBackingFactoryGLTextureWithGMBTest()
: SharedImageBackingFactoryGLTextureTestBase(false) {}
void SetUp() override { SetUpBase(GpuDriverBugWorkarounds(), this); }
scoped_refptr<gl::GLImage> GetImageFromMailbox(Mailbox mailbox) {
if (!use_passthrough()) {
auto representation =
shared_image_representation_factory_->ProduceGLTexture(mailbox);
DCHECK(representation);
return representation->GetTexture()->GetLevelImage(GL_TEXTURE_2D, 0);
} else {
auto representation =
shared_image_representation_factory_->ProduceGLTexturePassthrough(
mailbox);
DCHECK(representation);
return representation->GetTexturePassthrough()->GetLevelImage(
GL_TEXTURE_2D, 0);
}
}
protected:
// gpu::ImageFactory implementation.
scoped_refptr<gl::GLImage> CreateImageForGpuMemoryBuffer(
gfx::GpuMemoryBufferHandle handle,
const gfx::Size& size,
gfx::BufferFormat format,
int client_id,
gpu::SurfaceHandle surface_handle) override {
// pretend to handle NATIVE_PIXMAP types.
if (handle.type != gfx::NATIVE_PIXMAP)
return nullptr;
if (client_id != kClientId)
return nullptr;
return base::MakeRefCounted<StubImage>(size, format);
}
static constexpr int kClientId = 3;
};
TEST_P(SharedImageBackingFactoryGLTextureWithGMBTest,
GpuMemoryBufferImportEmpty) {
auto mailbox = Mailbox::GenerateForSharedImage();
gfx::Size size(256, 256);
gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888;
auto color_space = gfx::ColorSpace::CreateSRGB();
uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
gfx::GpuMemoryBufferHandle handle;
auto backing = backing_factory_->CreateSharedImage(
mailbox, kClientId, std::move(handle), format, kNullSurfaceHandle, size,
color_space, usage);
EXPECT_FALSE(backing);
}
TEST_P(SharedImageBackingFactoryGLTextureWithGMBTest,
GpuMemoryBufferImportNative) {
auto mailbox = Mailbox::GenerateForSharedImage();
gfx::Size size(256, 256);
gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888;
auto color_space = gfx::ColorSpace::CreateSRGB();
uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
gfx::GpuMemoryBufferHandle handle;
handle.type = gfx::NATIVE_PIXMAP;
auto backing = backing_factory_->CreateSharedImage(
mailbox, kClientId, std::move(handle), format, kNullSurfaceHandle, size,
color_space, usage);
ASSERT_TRUE(backing);
std::unique_ptr<SharedImageRepresentationFactoryRef> ref =
shared_image_manager_->Register(std::move(backing),
memory_type_tracker_.get());
scoped_refptr<gl::GLImage> image = GetImageFromMailbox(mailbox);
ASSERT_EQ(image->GetType(), gl::GLImage::Type::NONE);
auto* stub_image = static_cast<StubImage*>(image.get());
EXPECT_TRUE(stub_image->bound());
int update_counter = stub_image->update_counter();
ref->Update(nullptr);
EXPECT_TRUE(stub_image->bound());
EXPECT_GT(stub_image->update_counter(), update_counter);
}
TEST_P(SharedImageBackingFactoryGLTextureWithGMBTest,
GpuMemoryBufferImportSharedMemory) {
auto mailbox = Mailbox::GenerateForSharedImage();
gfx::Size size(256, 256);
gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888;
auto color_space = gfx::ColorSpace::CreateSRGB();
uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
size_t shm_size = 0u;
ASSERT_TRUE(gfx::BufferSizeForBufferFormatChecked(size, format, &shm_size));
gfx::GpuMemoryBufferHandle handle;
handle.type = gfx::SHARED_MEMORY_BUFFER;
handle.region = base::UnsafeSharedMemoryRegion::Create(shm_size);
ASSERT_TRUE(handle.region.IsValid());
handle.offset = 0;
handle.stride = static_cast<int32_t>(
gfx::RowSizeForBufferFormat(size.width(), format, 0));
auto backing = backing_factory_->CreateSharedImage(
mailbox, kClientId, std::move(handle), format, kNullSurfaceHandle, size,
color_space, usage);
ASSERT_TRUE(backing);
std::unique_ptr<SharedImageRepresentationFactoryRef> ref =
shared_image_manager_->Register(std::move(backing),
memory_type_tracker_.get());
scoped_refptr<gl::GLImage> image = GetImageFromMailbox(mailbox);
ASSERT_EQ(image->GetType(), gl::GLImage::Type::MEMORY);
auto* shm_image = static_cast<gl::GLImageSharedMemory*>(image.get());
EXPECT_EQ(size, shm_image->GetSize());
EXPECT_EQ(format, shm_image->format());
}
TEST_P(SharedImageBackingFactoryGLTextureWithGMBTest,
GpuMemoryBufferImportNative_WithRGBEmulation) {
if (use_passthrough())
return;
auto mailbox = Mailbox::GenerateForSharedImage();
gfx::Size size(256, 256);
gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888;
auto color_space = gfx::ColorSpace::CreateSRGB();
uint32_t usage = SHARED_IMAGE_USAGE_GLES2;
gfx::GpuMemoryBufferHandle handle;
handle.type = gfx::NATIVE_PIXMAP;
auto backing = backing_factory_->CreateSharedImage(
mailbox, kClientId, std::move(handle), format, kNullSurfaceHandle, size,
color_space, usage);
ASSERT_TRUE(backing);
std::unique_ptr<SharedImageRepresentationFactoryRef> ref =
shared_image_manager_->Register(std::move(backing),
memory_type_tracker_.get());
auto representation =
shared_image_representation_factory_->ProduceRGBEmulationGLTexture(
mailbox);
EXPECT_TRUE(representation);
EXPECT_TRUE(representation->GetTexture()->service_id());
EXPECT_EQ(size, representation->size());
EXPECT_EQ(viz::ResourceFormat::RGBA_8888, representation->format());
EXPECT_EQ(color_space, representation->color_space());
EXPECT_EQ(usage, representation->usage());
scoped_refptr<gl::GLImage> image =
representation->GetTexture()->GetLevelImage(GL_TEXTURE_2D, 0);
ASSERT_EQ(image->GetType(), gl::GLImage::Type::NONE);
auto* stub_image = static_cast<StubImage*>(image.get());
EXPECT_EQ(stub_image->internal_format(), (unsigned)GL_RGB);
EXPECT_TRUE(stub_image->bound());
EXPECT_EQ(stub_image->update_counter(), 1);
}
// Intent of this test is to create at thread safe backing and test if all
// representations are working.
TEST_P(SharedImageBackingFactoryGLTextureThreadSafeTest, BasicThreadSafe) {
// SharedImageBackingFactoryGLTextureThreadSafeTest tests are only meant for
// android platform.
if (!IsAndroid())
return;
CreateAndValidateSharedImageRepresentations shared_image(
backing_factory_.get(), true /* is_thread_safe */, &mailbox_manager_,
shared_image_manager_.get(), memory_type_tracker_.get(),
shared_image_representation_factory_.get(), context_state_.get());
}
// Intent of this test is to use the shared image mailbox system by 2 different
// threads each running their own GL context which are not part of same shared
// group. One thread will be writing to the backing and other thread will be
// reading from it.
TEST_P(SharedImageBackingFactoryGLTextureThreadSafeTest, OneWriterOneReader) {
if (!IsAndroid())
return;
// Create it on 1st SharedContextState |context_state_|.
CreateAndValidateSharedImageRepresentations shared_image(
backing_factory_.get(), true /* is_thread_safe */, &mailbox_manager_,
shared_image_manager_.get(), memory_type_tracker_.get(),
shared_image_representation_factory_.get(), context_state_.get());
auto mailbox = shared_image.mailbox();
auto size = shared_image.size();
// Writer will write to the backing. We will create a GLTexture representation
// and write green color to it.
auto gl_representation =
shared_image_representation_factory_->ProduceGLTexture(mailbox);
EXPECT_TRUE(gl_representation);
// Begin writing to the underlying texture of the backing via ScopedAccess.
std::unique_ptr<SharedImageRepresentationGLTexture::ScopedAccess>
writer_scoped_access = gl_representation->BeginScopedAccess(
GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
SharedImageRepresentation::AllowUnclearedAccess::kNo);
DCHECK(writer_scoped_access);
// Create an FBO.
GLuint fbo = 0;
gl::GLApi* api = gl::g_current_gl_context;
api->glGenFramebuffersEXTFn(1, &fbo);
api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, fbo);
// Attach the texture to FBO.
api->glFramebufferTexture2DEXTFn(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
gl_representation->GetTexture()->target(),
gl_representation->GetTexture()->service_id(), 0);
// Set the clear color to green.
api->glClearColorFn(0.0f, 1.0f, 0.0f, 1.0f);
api->glClearFn(GL_COLOR_BUFFER_BIT);
gl_representation->GetTexture()->SetLevelCleared(
gl_representation->GetTexture()->target(), 0, true);
// End writing.
writer_scoped_access.reset();
gl_representation.reset();
// Read from the backing in a separate thread. Read is done via
// SkiaGLRepresentation. ReadPixels() creates/produces a SkiaGLRepresentation
// which in turn wraps a GLTextureRepresentation when for GL mode. Hence
// testing reading via SkiaGLRepresentation is equivalent to testing via
// GLTextureRepresentation.
std::vector<uint8_t> dst_pixels;
// Launch 2nd thread.
std::thread second_thread([&]() {
// Do ReadPixels() on 2nd SharedContextState |context_state2_|.
dst_pixels = ReadPixels(mailbox, size, context_state2_.get(),
shared_image_representation_factory_.get());
});
// Wait for this thread to be done.
second_thread.join();
// Compare the pixel values.
EXPECT_EQ(dst_pixels[0], 0);
EXPECT_EQ(dst_pixels[1], 255);
EXPECT_EQ(dst_pixels[2], 0);
EXPECT_EQ(dst_pixels[3], 255);
}
CreateAndValidateSharedImageRepresentations::
CreateAndValidateSharedImageRepresentations(
SharedImageBackingFactoryGLTexture* backing_factory,
bool is_thread_safe,
gles2::MailboxManagerImpl* mailbox_manager,
SharedImageManager* shared_image_manager,
MemoryTypeTracker* memory_type_tracker,
SharedImageRepresentationFactory* shared_image_representation_factory,
SharedContextState* context_state)
: mailbox_manager_(mailbox_manager), size_(256, 256) {
// Make the context current.
DCHECK(context_state);
EXPECT_TRUE(
context_state->MakeCurrent(context_state->surface(), true /* needs_gl*/));
mailbox_ = Mailbox::GenerateForSharedImage();
auto format = viz::ResourceFormat::RGBA_8888;
auto color_space = gfx::ColorSpace::CreateSRGB();
// SHARED_IMAGE_USAGE_DISPLAY for skia read and SHARED_IMAGE_USAGE_RASTER for
// skia write.
uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_RASTER;
if (!is_thread_safe)
usage |= SHARED_IMAGE_USAGE_DISPLAY;
backing_ = backing_factory->CreateSharedImage(
mailbox_, format, size_, color_space, usage, is_thread_safe);
EXPECT_TRUE(backing_);
// Check clearing.
if (!backing_->IsCleared()) {
backing_->SetCleared();
EXPECT_TRUE(backing_->IsCleared());
}
GLenum expected_target = GL_TEXTURE_2D;
shared_image_ =
shared_image_manager->Register(std::move(backing_), memory_type_tracker);
// Create and validate GLTexture representation.
auto gl_representation =
shared_image_representation_factory->ProduceGLTexture(mailbox_);
EXPECT_TRUE(gl_representation);
EXPECT_TRUE(gl_representation->GetTexture()->service_id());
EXPECT_EQ(expected_target, gl_representation->GetTexture()->target());
EXPECT_EQ(size_, gl_representation->size());
EXPECT_EQ(format, gl_representation->format());
EXPECT_EQ(color_space, gl_representation->color_space());
EXPECT_EQ(usage, gl_representation->usage());
gl_representation.reset();
// Create and Validate Skia Representations.
auto skia_representation =
shared_image_representation_factory->ProduceSkia(mailbox_, context_state);
EXPECT_TRUE(skia_representation);
std::vector<GrBackendSemaphore> begin_semaphores;
std::vector<GrBackendSemaphore> end_semaphores;
std::unique_ptr<SharedImageRepresentationSkia::ScopedWriteAccess>
scoped_write_access;
scoped_write_access = skia_representation->BeginScopedWriteAccess(
&begin_semaphores, &end_semaphores,
SharedImageRepresentation::AllowUnclearedAccess::kNo);
auto* surface = scoped_write_access->surface();
EXPECT_TRUE(surface);
EXPECT_EQ(size_.width(), surface->width());
EXPECT_EQ(size_.height(), surface->height());
EXPECT_TRUE(begin_semaphores.empty());
EXPECT_TRUE(end_semaphores.empty());
scoped_write_access.reset();
std::unique_ptr<SharedImageRepresentationSkia::ScopedReadAccess>
scoped_read_access;
scoped_read_access = skia_representation->BeginScopedReadAccess(
&begin_semaphores, &end_semaphores);
auto* promise_texture = scoped_read_access->promise_image_texture();
EXPECT_TRUE(promise_texture);
EXPECT_TRUE(begin_semaphores.empty());
EXPECT_TRUE(end_semaphores.empty());
GrBackendTexture backend_texture = promise_texture->backendTexture();
EXPECT_TRUE(backend_texture.isValid());
EXPECT_EQ(size_.width(), backend_texture.width());
EXPECT_EQ(size_.height(), backend_texture.height());
scoped_read_access.reset();
skia_representation.reset();
}
CreateAndValidateSharedImageRepresentations::
~CreateAndValidateSharedImageRepresentations() {
shared_image_.reset();
EXPECT_FALSE(mailbox_manager_->ConsumeTexture(mailbox_));
}
INSTANTIATE_TEST_SUITE_P(Service,
SharedImageBackingFactoryGLTextureTest,
::testing::Bool());
INSTANTIATE_TEST_SUITE_P(Service,
SharedImageBackingFactoryGLTextureThreadSafeTest,
::testing::Bool());
INSTANTIATE_TEST_SUITE_P(Service,
SharedImageBackingFactoryGLTextureWithGMBTest,
::testing::Bool());
} // anonymous namespace
} // namespace gpu