| // Copyright 2013 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 "cc/raster/zero_copy_raster_buffer_provider.h" |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| |
| #include "base/macros.h" |
| #include "base/trace_event/trace_event.h" |
| #include "base/trace_event/traced_value.h" |
| #include "cc/resources/resource_pool.h" |
| #include "components/viz/client/client_resource_provider.h" |
| #include "components/viz/common/gpu/context_provider.h" |
| #include "components/viz/common/resources/platform_color.h" |
| #include "components/viz/common/resources/resource_format_utils.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h" |
| #include "gpu/command_buffer/common/gpu_memory_buffer_support.h" |
| #include "ui/gfx/buffer_format_util.h" |
| #include "ui/gfx/gpu_memory_buffer.h" |
| |
| namespace cc { |
| namespace { |
| |
| constexpr static auto kBufferUsage = gfx::BufferUsage::GPU_READ_CPU_READ_WRITE; |
| |
| // Subclass for InUsePoolResource that holds ownership of a zero-copy backing |
| // and does cleanup of the backing when destroyed. |
| class ZeroCopyGpuBacking : public ResourcePool::GpuBacking { |
| public: |
| ~ZeroCopyGpuBacking() override { |
| gpu::gles2::GLES2Interface* gl = compositor_context_provider->ContextGL(); |
| if (returned_sync_token.HasData()) |
| gl->WaitSyncTokenCHROMIUM(returned_sync_token.GetConstData()); |
| if (texture_id) |
| gl->DeleteTextures(1, &texture_id); |
| if (image_id) |
| gl->DestroyImageCHROMIUM(image_id); |
| } |
| |
| void OnMemoryDump( |
| base::trace_event::ProcessMemoryDump* pmd, |
| const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid, |
| uint64_t tracing_process_id, |
| int importance) const override { |
| if (!gpu_memory_buffer) |
| return; |
| gpu_memory_buffer->OnMemoryDump(pmd, buffer_dump_guid, tracing_process_id, |
| importance); |
| } |
| |
| // The ContextProvider used to clean up the texture and image ids. |
| viz::ContextProvider* compositor_context_provider = nullptr; |
| // The backing for zero-copy gpu resources. The |texture_id| is bound to |
| // this. |
| std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer; |
| // The texture id bound to the GpuMemoryBuffer. |
| uint32_t texture_id = 0; |
| // The image id that associates the |gpu_memory_buffer| and the |
| // |texture_id|. |
| uint32_t image_id = 0; |
| }; |
| |
| // RasterBuffer for the zero copy upload, which is given to the raster worker |
| // threads for raster/upload. |
| class ZeroCopyRasterBufferImpl : public RasterBuffer { |
| public: |
| ZeroCopyRasterBufferImpl( |
| viz::ContextProvider* context_provider, |
| gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, |
| const ResourcePool::InUsePoolResource& in_use_resource, |
| ZeroCopyGpuBacking* backing) |
| : backing_(backing), |
| gpu_memory_buffer_manager_(gpu_memory_buffer_manager), |
| resource_size_(in_use_resource.size()), |
| resource_format_(in_use_resource.format()), |
| resource_color_space_(in_use_resource.color_space()), |
| gpu_memory_buffer_(std::move(backing_->gpu_memory_buffer)) {} |
| |
| ~ZeroCopyRasterBufferImpl() override { |
| // If GpuMemoryBuffer allocation failed (https://crbug.com/554541), then |
| // we don't have anything to give to the display compositor, so we report a |
| // zero mailbox that will result in checkerboarding. |
| if (!gpu_memory_buffer_) { |
| DCHECK(backing_->mailbox.IsZero()); |
| return; |
| } |
| |
| // This is destroyed on the compositor thread when raster is complete, but |
| // before the backing is prepared for export to the display compositor. So |
| // we can set up the texture and SyncToken here. |
| // TODO(danakj): This could be done with the worker context in Playback. Do |
| // we need to do things in IsResourceReadyToDraw() and OrderingBarrier then? |
| gpu::gles2::GLES2Interface* gl = |
| backing_->compositor_context_provider->ContextGL(); |
| const gpu::Capabilities& caps = |
| backing_->compositor_context_provider->ContextCapabilities(); |
| |
| if (backing_->returned_sync_token.HasData()) { |
| gl->WaitSyncTokenCHROMIUM(backing_->returned_sync_token.GetConstData()); |
| backing_->returned_sync_token = gpu::SyncToken(); |
| } |
| |
| if (!backing_->texture_id) { |
| // Make a texture and a mailbox for export of the GpuMemoryBuffer to the |
| // display compositor. |
| gl->GenTextures(1, &backing_->texture_id); |
| backing_->texture_target = gpu::GetBufferTextureTarget( |
| kBufferUsage, viz::BufferFormat(resource_format_), caps); |
| gl->ProduceTextureDirectCHROMIUM(backing_->texture_id, |
| backing_->mailbox.name); |
| backing_->overlay_candidate = true; |
| // This RasterBufferProvider will modify the resource outside of the |
| // GL command stream. So resources should not become available for reuse |
| // until they are not in use by the gpu anymore, which a fence is used to |
| // determine. |
| backing_->wait_on_fence_required = true; |
| |
| gl->BindTexture(backing_->texture_target, backing_->texture_id); |
| gl->TexParameteri(backing_->texture_target, GL_TEXTURE_MIN_FILTER, |
| GL_LINEAR); |
| gl->TexParameteri(backing_->texture_target, GL_TEXTURE_MAG_FILTER, |
| GL_LINEAR); |
| gl->TexParameteri(backing_->texture_target, GL_TEXTURE_WRAP_S, |
| GL_CLAMP_TO_EDGE); |
| gl->TexParameteri(backing_->texture_target, GL_TEXTURE_WRAP_T, |
| GL_CLAMP_TO_EDGE); |
| } else { |
| gl->BindTexture(backing_->texture_target, backing_->texture_id); |
| } |
| |
| if (!backing_->image_id) { |
| backing_->image_id = gl->CreateImageCHROMIUM( |
| gpu_memory_buffer_->AsClientBuffer(), resource_size_.width(), |
| resource_size_.height(), viz::GLInternalFormat(resource_format_)); |
| gl->BindTexImage2DCHROMIUM(backing_->texture_target, backing_->image_id); |
| } else { |
| gl->ReleaseTexImage2DCHROMIUM(backing_->texture_target, |
| backing_->image_id); |
| gl->BindTexImage2DCHROMIUM(backing_->texture_target, backing_->image_id); |
| } |
| if (backing_->image_id && resource_color_space_.IsValid()) { |
| gl->SetColorSpaceMetadataCHROMIUM( |
| backing_->texture_id, |
| reinterpret_cast<GLColorSpace>(&resource_color_space_)); |
| } |
| gl->BindTexture(backing_->texture_target, 0); |
| |
| backing_->mailbox_sync_token = |
| viz::ClientResourceProvider::GenerateSyncTokenHelper(gl); |
| backing_->gpu_memory_buffer = std::move(gpu_memory_buffer_); |
| } |
| |
| // Overridden from RasterBuffer: |
| void Playback(const RasterSource* raster_source, |
| const gfx::Rect& raster_full_rect, |
| const gfx::Rect& raster_dirty_rect, |
| uint64_t new_content_id, |
| const gfx::AxisTransform2d& transform, |
| const RasterSource::PlaybackSettings& playback_settings, |
| const GURL& url) override { |
| TRACE_EVENT0("cc", "ZeroCopyRasterBuffer::Playback"); |
| |
| if (!gpu_memory_buffer_) { |
| gpu_memory_buffer_ = gpu_memory_buffer_manager_->CreateGpuMemoryBuffer( |
| resource_size_, viz::BufferFormat(resource_format_), kBufferUsage, |
| gpu::kNullSurfaceHandle); |
| // Note that GpuMemoryBuffer allocation can fail. |
| // https://crbug.com/554541 |
| if (!gpu_memory_buffer_) |
| return; |
| } |
| |
| DCHECK_EQ(1u, gfx::NumberOfPlanesForBufferFormat( |
| gpu_memory_buffer_->GetFormat())); |
| bool rv = gpu_memory_buffer_->Map(); |
| DCHECK(rv); |
| DCHECK(gpu_memory_buffer_->memory(0)); |
| // RasterBufferProvider::PlaybackToMemory only supports unsigned strides. |
| DCHECK_GE(gpu_memory_buffer_->stride(0), 0); |
| |
| // TODO(danakj): Implement partial raster with raster_dirty_rect. |
| RasterBufferProvider::PlaybackToMemory( |
| gpu_memory_buffer_->memory(0), resource_format_, resource_size_, |
| gpu_memory_buffer_->stride(0), raster_source, raster_full_rect, |
| raster_full_rect, transform, resource_color_space_, |
| /*gpu_compositing=*/true, playback_settings); |
| gpu_memory_buffer_->Unmap(); |
| } |
| |
| private: |
| // This field may only be used on the compositor thread. |
| ZeroCopyGpuBacking* backing_; |
| |
| // These fields are for use on the worker thread. |
| gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_; |
| gfx::Size resource_size_; |
| viz::ResourceFormat resource_format_; |
| gfx::ColorSpace resource_color_space_; |
| std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ZeroCopyRasterBufferImpl); |
| }; |
| |
| } // namespace |
| |
| ZeroCopyRasterBufferProvider::ZeroCopyRasterBufferProvider( |
| gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, |
| viz::ContextProvider* compositor_context_provider, |
| viz::ResourceFormat tile_format) |
| : gpu_memory_buffer_manager_(gpu_memory_buffer_manager), |
| compositor_context_provider_(compositor_context_provider), |
| tile_format_(tile_format) {} |
| |
| ZeroCopyRasterBufferProvider::~ZeroCopyRasterBufferProvider() = default; |
| |
| std::unique_ptr<RasterBuffer> |
| ZeroCopyRasterBufferProvider::AcquireBufferForRaster( |
| const ResourcePool::InUsePoolResource& resource, |
| uint64_t resource_content_id, |
| uint64_t previous_content_id) { |
| if (!resource.gpu_backing()) { |
| auto backing = std::make_unique<ZeroCopyGpuBacking>(); |
| backing->compositor_context_provider = compositor_context_provider_; |
| resource.set_gpu_backing(std::move(backing)); |
| } |
| ZeroCopyGpuBacking* backing = |
| static_cast<ZeroCopyGpuBacking*>(resource.gpu_backing()); |
| |
| return std::make_unique<ZeroCopyRasterBufferImpl>( |
| compositor_context_provider_, gpu_memory_buffer_manager_, resource, |
| backing); |
| } |
| |
| void ZeroCopyRasterBufferProvider::Flush() {} |
| |
| viz::ResourceFormat ZeroCopyRasterBufferProvider::GetResourceFormat() const { |
| return tile_format_; |
| } |
| |
| bool ZeroCopyRasterBufferProvider::IsResourceSwizzleRequired() const { |
| return !viz::PlatformColor::SameComponentOrder(GetResourceFormat()); |
| } |
| |
| bool ZeroCopyRasterBufferProvider::IsResourcePremultiplied() const { |
| return true; |
| } |
| |
| bool ZeroCopyRasterBufferProvider::CanPartialRasterIntoProvidedResource() |
| const { |
| return false; |
| } |
| |
| bool ZeroCopyRasterBufferProvider::IsResourceReadyToDraw( |
| const ResourcePool::InUsePoolResource& resource) const { |
| // Zero-copy resources are immediately ready to draw. |
| return true; |
| } |
| |
| uint64_t ZeroCopyRasterBufferProvider::SetReadyToDrawCallback( |
| const std::vector<const ResourcePool::InUsePoolResource*>& resources, |
| const base::Closure& callback, |
| uint64_t pending_callback_id) const { |
| // Zero-copy resources are immediately ready to draw. |
| return 0; |
| } |
| |
| void ZeroCopyRasterBufferProvider::Shutdown() {} |
| |
| bool ZeroCopyRasterBufferProvider::CheckRasterFinishedQueries() { |
| return false; |
| } |
| |
| } // namespace cc |