| // Copyright 2021 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/shared_image/gl_image_backing_factory.h" |
| |
| #include <list> |
| #include <utility> |
| |
| #include "base/containers/contains.h" |
| #include "build/build_config.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/image_factory.h" |
| #include "gpu/command_buffer/service/shared_image/gl_image_backing.h" |
| #include "gpu/command_buffer/service/shared_image/shared_image_factory.h" |
| #include "gpu/config/gpu_preferences.h" |
| #include "ui/gfx/buffer_format_util.h" |
| #include "ui/gfx/color_space.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gl/buffer_format_utils.h" |
| #include "ui/gl/gl_implementation.h" |
| #include "ui/gl/progress_reporter.h" |
| |
| namespace gpu { |
| |
| namespace { |
| |
| using InitializeGLTextureParams = |
| GLTextureImageBackingHelper::InitializeGLTextureParams; |
| |
| } // anonymous namespace |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // GLImageBackingFactory |
| |
| GLImageBackingFactory::GLImageBackingFactory( |
| const GpuPreferences& gpu_preferences, |
| const GpuDriverBugWorkarounds& workarounds, |
| const gles2::FeatureInfo* feature_info, |
| ImageFactory* image_factory, |
| gl::ProgressReporter* progress_reporter) |
| : GLCommonImageBackingFactory(gpu_preferences, |
| workarounds, |
| feature_info, |
| progress_reporter), |
| image_factory_(image_factory) { |
| gpu_memory_buffer_formats_ = |
| feature_info->feature_flags().gpu_memory_buffer_formats; |
| // Return if scanout images are not supported |
| if (!(image_factory_ && image_factory_->SupportsCreateAnonymousImage())) { |
| return; |
| } |
| for (int i = 0; i <= viz::RESOURCE_FORMAT_MAX; ++i) { |
| auto format = static_cast<viz::ResourceFormat>(i); |
| FormatInfo& info = format_info_[i]; |
| BufferFormatInfo& buffer_format_info = buffer_format_info_[i]; |
| if (!info.enabled || !IsGpuMemoryBufferFormatSupported(format)) { |
| continue; |
| } |
| const gfx::BufferFormat buffer_format = viz::BufferFormat(format); |
| switch (buffer_format) { |
| case gfx::BufferFormat::RGBA_8888: |
| case gfx::BufferFormat::RGBX_8888: |
| case gfx::BufferFormat::BGRA_8888: |
| case gfx::BufferFormat::BGRX_8888: |
| case gfx::BufferFormat::RGBA_F16: |
| case gfx::BufferFormat::R_8: |
| case gfx::BufferFormat::BGRA_1010102: |
| case gfx::BufferFormat::RGBA_1010102: |
| break; |
| default: |
| continue; |
| } |
| if (!gpu_memory_buffer_formats_.Has(buffer_format)) |
| continue; |
| buffer_format_info.allow_scanout = true; |
| buffer_format_info.buffer_format = buffer_format; |
| DCHECK_EQ(info.image_internal_format, |
| gl::BufferFormatToGLInternalFormat(buffer_format)); |
| if (base::Contains(gpu_preferences.texture_target_exception_list, |
| gfx::BufferUsageAndFormat(gfx::BufferUsage::SCANOUT, |
| buffer_format))) { |
| buffer_format_info.target_for_scanout = |
| gpu::GetPlatformSpecificTextureTarget(); |
| } |
| } |
| } |
| |
| GLImageBackingFactory::~GLImageBackingFactory() = default; |
| |
| std::unique_ptr<SharedImageBacking> GLImageBackingFactory::CreateSharedImage( |
| const Mailbox& mailbox, |
| viz::ResourceFormat format, |
| SurfaceHandle surface_handle, |
| const gfx::Size& size, |
| const gfx::ColorSpace& color_space, |
| GrSurfaceOrigin surface_origin, |
| SkAlphaType alpha_type, |
| uint32_t usage, |
| bool is_thread_safe) { |
| DCHECK(!is_thread_safe); |
| return CreateSharedImageInternal(mailbox, format, surface_handle, size, |
| color_space, surface_origin, alpha_type, |
| usage, base::span<const uint8_t>()); |
| } |
| |
| std::unique_ptr<SharedImageBacking> GLImageBackingFactory::CreateSharedImage( |
| const Mailbox& mailbox, |
| viz::ResourceFormat format, |
| const gfx::Size& size, |
| const gfx::ColorSpace& color_space, |
| GrSurfaceOrigin surface_origin, |
| SkAlphaType alpha_type, |
| uint32_t usage, |
| base::span<const uint8_t> pixel_data) { |
| return CreateSharedImageInternal(mailbox, format, kNullSurfaceHandle, size, |
| color_space, surface_origin, alpha_type, |
| usage, pixel_data); |
| } |
| |
| std::unique_ptr<SharedImageBacking> GLImageBackingFactory::CreateSharedImage( |
| const Mailbox& mailbox, |
| int client_id, |
| gfx::GpuMemoryBufferHandle handle, |
| gfx::BufferFormat buffer_format, |
| gfx::BufferPlane plane, |
| SurfaceHandle surface_handle, |
| const gfx::Size& size, |
| const gfx::ColorSpace& color_space, |
| GrSurfaceOrigin surface_origin, |
| SkAlphaType alpha_type, |
| uint32_t usage) { |
| if (!gpu_memory_buffer_formats_.Has(buffer_format)) { |
| LOG(ERROR) << "CreateSharedImage: unsupported buffer format " |
| << gfx::BufferFormatToString(buffer_format); |
| return nullptr; |
| } |
| |
| if (!gpu::IsPlaneValidForGpuMemoryBufferFormat(plane, buffer_format)) { |
| LOG(ERROR) << "Invalid plane " << gfx::BufferPlaneToString(plane) << " for " |
| << gfx::BufferFormatToString(buffer_format); |
| return nullptr; |
| } |
| |
| if (!gpu::IsImageSizeValidForGpuMemoryBufferFormat(size, buffer_format)) { |
| LOG(ERROR) << "Invalid image size " << size.ToString() << " for " |
| << gfx::BufferFormatToString(buffer_format); |
| return nullptr; |
| } |
| |
| GLenum target = |
| !NativeBufferNeedsPlatformSpecificTextureTarget(buffer_format, plane) |
| ? GL_TEXTURE_2D |
| : gpu::GetPlatformSpecificTextureTarget(); |
| scoped_refptr<gl::GLImage> image = |
| MakeGLImage(client_id, std::move(handle), buffer_format, color_space, |
| plane, surface_handle, size); |
| if (!image) { |
| LOG(ERROR) << "Failed to create image."; |
| return nullptr; |
| } |
| // If we decide to use GL_TEXTURE_2D at the target for a native buffer, we |
| // would like to verify that it will actually work. If the image expects to be |
| // copied, there is no way to do this verification here, because copying is |
| // done lazily after the SharedImage is created, so require that the image is |
| // bindable. Currently NativeBufferNeedsPlatformSpecificTextureTarget can |
| // only return false on Chrome OS where GLImageNativePixmap is used which is |
| // always bindable. |
| #if DCHECK_IS_ON() |
| bool texture_2d_support = false; |
| #if BUILDFLAG(IS_MAC) |
| // If the PlatformSpecificTextureTarget on Mac is GL_TEXTURE_2D, this is |
| // supported. |
| texture_2d_support = |
| (gpu::GetPlatformSpecificTextureTarget() == GL_TEXTURE_2D); |
| #endif // BUILDFLAG(IS_MAC) |
| DCHECK(target != GL_TEXTURE_2D || texture_2d_support || |
| image->ShouldBindOrCopy() == gl::GLImage::BIND); |
| #endif // DCHECK_IS_ON() |
| if (usage & SHARED_IMAGE_USAGE_MACOS_VIDEO_TOOLBOX) |
| image->DisableInUseByWindowServer(); |
| |
| const viz::ResourceFormat plane_format = |
| viz::GetResourceFormat(GetPlaneBufferFormat(plane, buffer_format)); |
| |
| const gfx::Size plane_size = gpu::GetPlaneSize(plane, size); |
| DCHECK_EQ(image->GetSize(), plane_size); |
| |
| const bool for_framebuffer_attachment = |
| (usage & (SHARED_IMAGE_USAGE_RASTER | |
| SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT)) != 0; |
| |
| InitializeGLTextureParams params; |
| params.target = target; |
| params.internal_format = image->GetInternalFormat(); |
| params.format = image->GetDataFormat(); |
| params.type = image->GetDataType(); |
| params.is_cleared = true; |
| params.framebuffer_attachment_angle = |
| for_framebuffer_attachment && texture_usage_angle_; |
| return std::make_unique<GLImageBacking>( |
| image, mailbox, plane_format, plane_size, color_space, surface_origin, |
| alpha_type, usage, params, use_passthrough_); |
| } |
| |
| scoped_refptr<gl::GLImage> GLImageBackingFactory::MakeGLImage( |
| int client_id, |
| gfx::GpuMemoryBufferHandle handle, |
| gfx::BufferFormat format, |
| const gfx::ColorSpace& color_space, |
| gfx::BufferPlane plane, |
| SurfaceHandle surface_handle, |
| const gfx::Size& size) { |
| if (!image_factory_) |
| return nullptr; |
| |
| return image_factory_->CreateImageForGpuMemoryBuffer( |
| std::move(handle), size, format, color_space, plane, client_id, |
| surface_handle); |
| } |
| |
| bool GLImageBackingFactory::IsSupported(uint32_t usage, |
| viz::ResourceFormat format, |
| const gfx::Size& size, |
| bool thread_safe, |
| gfx::GpuMemoryBufferType gmb_type, |
| GrContextType gr_context_type, |
| base::span<const uint8_t> pixel_data) { |
| if (!pixel_data.empty() && gr_context_type != GrContextType::kGL) { |
| return false; |
| } |
| if (thread_safe) { |
| return false; |
| } |
| // Never used with shared memory GMBs. |
| if (gmb_type == gfx::SHARED_MEMORY_BUFFER) { |
| return false; |
| } |
| if (usage & SHARED_IMAGE_USAGE_CPU_UPLOAD) { |
| return false; |
| } |
| #if BUILDFLAG(IS_MAC) |
| // On macOS, there is no separate interop factory. Any GpuMemoryBuffer-backed |
| // image can be used with both OpenGL and Metal |
| |
| // In certain modes on Mac, Angle needs the image to be released when ending a |
| // write. To avoid that release resulting in the GLES2 command decoders |
| // needing to perform on-demand binding, we disallow concurrent read/write in |
| // these modes. See GLImageBacking::GLTextureImageRepresentationEndAccess() |
| // for further details. |
| // TODO(https://anglebug.com/7626): Adjust the Metal-related conditions here |
| // if/as they are adjusted in |
| // GLImageBacking::GLTextureImageRepresentationEndAccess(). |
| if (use_passthrough_ && |
| (gl::GetANGLEImplementation() == gl::ANGLEImplementation::kSwiftShader || |
| gl::GetANGLEImplementation() == gl::ANGLEImplementation::kMetal)) { |
| if (usage & SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE) { |
| return false; |
| } |
| } |
| |
| return true; |
| #else |
| // Doesn't support contexts other than GL for OOPR Canvas |
| if (gr_context_type != GrContextType::kGL && |
| ((usage & SHARED_IMAGE_USAGE_DISPLAY_READ) || |
| (usage & SHARED_IMAGE_USAGE_RASTER))) { |
| return false; |
| } |
| if ((usage & SHARED_IMAGE_USAGE_WEBGPU) || |
| (usage & SHARED_IMAGE_USAGE_VIDEO_DECODE)) { |
| // return false if it needs interop factory |
| return false; |
| } |
| return true; |
| #endif |
| } |
| |
| std::unique_ptr<SharedImageBacking> |
| GLImageBackingFactory::CreateSharedImageInternal( |
| const Mailbox& mailbox, |
| viz::ResourceFormat format, |
| SurfaceHandle surface_handle, |
| const gfx::Size& size, |
| const gfx::ColorSpace& color_space, |
| GrSurfaceOrigin surface_origin, |
| SkAlphaType alpha_type, |
| uint32_t usage, |
| base::span<const uint8_t> pixel_data) { |
| const FormatInfo& format_info = format_info_[format]; |
| const BufferFormatInfo& buffer_format_info = buffer_format_info_[format]; |
| GLenum target = buffer_format_info.target_for_scanout; |
| |
| if (!buffer_format_info.allow_scanout) { |
| LOG(ERROR) << "CreateSharedImage: SCANOUT shared images unavailable. " |
| "Buffer format= " |
| << gfx::BufferFormatToString(buffer_format_info.buffer_format); |
| return nullptr; |
| } |
| |
| if (!CanCreateSharedImage(size, pixel_data, format_info, target)) { |
| return nullptr; |
| } |
| |
| const bool for_framebuffer_attachment = |
| (usage & (SHARED_IMAGE_USAGE_RASTER | |
| SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT)) != 0; |
| |
| scoped_refptr<gl::GLImage> image; |
| |
| // TODO(piman): We pretend the texture was created in an ES2 context, so that |
| // it can be used in other ES2 contexts, and so we have to pass gl_format as |
| // the internal format in the LevelInfo. https://crbug.com/628064 |
| GLuint level_info_internal_format = format_info.gl_format; |
| bool is_cleared = false; |
| |
| // |scoped_progress_reporter| will notify |progress_reporter_| upon |
| // construction and destruction. We limit the scope so that progress is |
| // reported immediately after allocation/upload and before other GL |
| // operations. |
| { |
| gl::ScopedProgressReporter scoped_progress_reporter(progress_reporter_); |
| image = image_factory_->CreateAnonymousImage( |
| size, buffer_format_info.buffer_format, gfx::BufferUsage::SCANOUT, |
| surface_handle, &is_cleared); |
| } |
| // Scanout images have different constraints than GL images and might fail |
| // to allocate even if GL images can be created. |
| if (!image) { |
| gl::ScopedProgressReporter scoped_progress_reporter(progress_reporter_); |
| // TODO(dcastagna): Use BufferUsage::GPU_READ_WRITE instead |
| // BufferUsage::GPU_READ once we add it. |
| image = image_factory_->CreateAnonymousImage( |
| size, buffer_format_info.buffer_format, gfx::BufferUsage::GPU_READ, |
| surface_handle, &is_cleared); |
| } |
| // The allocated image should not require copy. |
| if (!image || image->ShouldBindOrCopy() != gl::GLImage::BIND) { |
| LOG(ERROR) << "CreateSharedImage: Failed to create bindable image"; |
| return nullptr; |
| } |
| level_info_internal_format = image->GetInternalFormat(); |
| if (color_space.IsValid()) |
| image->SetColorSpace(color_space); |
| if (usage & SHARED_IMAGE_USAGE_MACOS_VIDEO_TOOLBOX) |
| image->DisableInUseByWindowServer(); |
| |
| InitializeGLTextureParams params; |
| params.target = target; |
| params.internal_format = level_info_internal_format; |
| params.format = format_info.gl_format; |
| params.type = format_info.gl_type; |
| params.is_cleared = pixel_data.empty() ? is_cleared : true; |
| params.has_immutable_storage = !image && format_info.supports_storage; |
| params.framebuffer_attachment_angle = |
| for_framebuffer_attachment && texture_usage_angle_; |
| |
| DCHECK(!format_info.swizzle); |
| auto result = std::make_unique<GLImageBacking>( |
| image, mailbox, format, size, color_space, surface_origin, alpha_type, |
| usage, params, use_passthrough_); |
| if (!pixel_data.empty()) { |
| gl::ScopedProgressReporter scoped_progress_reporter(progress_reporter_); |
| result->InitializePixels(format_info.adjusted_format, format_info.gl_type, |
| pixel_data.data()); |
| } |
| return std::move(result); |
| } |
| |
| } // namespace gpu |