blob: 93a557da307ac637fb292dd6216634a18ffad4c1 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/raster/one_copy_raster_buffer_provider.h"
#include <stdint.h>
#include <algorithm>
#include <limits>
#include <utility>
#include "base/debug/alias.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/sequenced_task_runner.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "cc/base/features.h"
#include "cc/base/histograms.h"
#include "cc/base/math_util.h"
#include "components/viz/common/gpu/raster_context_provider.h"
#include "components/viz/common/resources/platform_color.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "gpu/command_buffer/client/context_support.h"
#include "gpu/command_buffer/client/raster_interface.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/shared_image_trace_utils.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "third_party/skia/include/core/SkAlphaType.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColorType.h"
#include "ui/gl/trace_util.h"
namespace cc {
OneCopyRasterBufferProvider::RasterBufferImpl::RasterBufferImpl(
OneCopyRasterBufferProvider* client,
const ResourcePool::InUsePoolResource& in_use_resource,
uint64_t previous_content_id)
: client_(client),
previous_content_id_(previous_content_id) {
if (!in_use_resource.backing()) {
auto backing = std::make_unique<ResourcePool::Backing>(
in_use_resource.size(), in_use_resource.format(),
in_use_resource.color_space());
in_use_resource.set_backing(std::move(backing));
}
backing_ = in_use_resource.backing();
if (!backing_->shared_image()) {
// The backing's SharedImage will be created on a worker thread during the
// execution of this raster; to avoid data races during taking of memory
// dumps on the compositor thread, mark the backing's SharedImage as
// unavailable for access on the compositor thread for the duration of the
// raster.
backing_->can_access_shared_image_on_compositor_thread = false;
}
before_raster_sync_token_ = backing_->returned_sync_token;
mailbox_texture_is_overlay_candidate_ = client_->tile_overlay_candidate_;
}
OneCopyRasterBufferProvider::RasterBufferImpl::~RasterBufferImpl() {
// This raster task is complete, so if the backing's SharedImage was created
// on a worker thread during the raster work that has now happened.
backing_->can_access_shared_image_on_compositor_thread = true;
// This SyncToken was created on the worker context after uploading the
// texture content.
backing_->mailbox_sync_token = after_raster_sync_token_;
if (after_raster_sync_token_.HasData()) {
// The returned SyncToken was waited on in Playback. We know Playback
// happened if the |after_raster_sync_token_| was set.
backing_->returned_sync_token = gpu::SyncToken();
}
if (should_destroy_shared_image_ && backing_->shared_image()) {
backing_->shared_image()->UpdateDestructionSyncToken(
before_raster_sync_token_);
backing_->clear_shared_image();
}
}
void OneCopyRasterBufferProvider::RasterBufferImpl::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) {
TRACE_EVENT0("cc", "OneCopyRasterBuffer::Playback");
// The |before_raster_sync_token_| passed in here was created on the
// compositor thread, or given back with the texture for reuse. This call
// returns another SyncToken generated on the worker thread to synchronize
// with after the raster is complete.
after_raster_sync_token_ = client_->PlaybackAndCopyOnWorkerThread(
backing_, mailbox_texture_is_overlay_candidate_,
before_raster_sync_token_, raster_source, raster_full_rect,
raster_dirty_rect, transform, playback_settings, previous_content_id_,
new_content_id, should_destroy_shared_image_);
}
bool OneCopyRasterBufferProvider::RasterBufferImpl::
SupportsBackgroundThreadPriority() const {
// Playback() should not run at background thread priority because it acquires
// the GpuChannelHost lock, which is acquired at normal thread priority by
// other code. Acquiring it at background thread priority can cause a priority
// inversion. https://crbug.com/1072756
return false;
}
OneCopyRasterBufferProvider::OneCopyRasterBufferProvider(
scoped_refptr<gpu::SharedImageInterface> sii,
scoped_refptr<base::SequencedTaskRunner> task_runner,
viz::RasterContextProvider* compositor_context_provider,
viz::RasterContextProvider* worker_context_provider,
bool use_partial_raster,
int max_staging_buffer_usage_in_bytes,
bool is_overlay_candidate)
: sii_(sii),
compositor_context_provider_(compositor_context_provider),
worker_context_provider_(worker_context_provider),
use_partial_raster_(use_partial_raster),
tile_overlay_candidate_(is_overlay_candidate),
staging_pool_(std::move(task_runner),
worker_context_provider,
use_partial_raster,
max_staging_buffer_usage_in_bytes) {
DCHECK(compositor_context_provider);
DCHECK(worker_context_provider);
}
OneCopyRasterBufferProvider::~OneCopyRasterBufferProvider() = default;
std::unique_ptr<RasterBuffer>
OneCopyRasterBufferProvider::AcquireBufferForRaster(
const ResourcePool::InUsePoolResource& resource,
uint64_t resource_content_id,
uint64_t previous_content_id) {
// TODO(danakj): If resource_content_id != 0, we only need to copy/upload
// the dirty rect.
return std::make_unique<RasterBufferImpl>(this, resource,
previous_content_id);
}
void OneCopyRasterBufferProvider::Flush() {
// This flush on the compositor context flushes queued work on all contexts,
// including the raster worker. Tile raster inserted a SyncToken which is
// waited for in order to tell if a tile is ready for draw, but a flush
// is needed to ensure the work is sent for those queries to get the right
// answer.
compositor_context_provider_->ContextSupport()->FlushPendingWork();
}
bool OneCopyRasterBufferProvider::CanPartialRasterIntoProvidedResource() const {
// While OneCopyRasterBufferProvider has an internal partial raster
// implementation, it cannot directly partial raster into the externally
// owned resource provided in AcquireBufferForRaster.
return false;
}
bool OneCopyRasterBufferProvider::IsResourceReadyToDraw(
const ResourcePool::InUsePoolResource& resource) {
FlushIfNeeded();
const gpu::SyncToken& sync_token = resource.backing()->mailbox_sync_token;
// This SyncToken() should have been set by calling OrderingBarrier() before
// calling this.
DCHECK(sync_token.HasData());
// IsSyncTokenSignaled is thread-safe, no need for worker context lock.
return worker_context_provider_->ContextSupport()->IsSyncTokenSignaled(
sync_token);
}
uint64_t OneCopyRasterBufferProvider::SetReadyToDrawCallback(
const std::vector<const ResourcePool::InUsePoolResource*>& resources,
base::OnceClosure callback,
uint64_t pending_callback_id) {
FlushIfNeeded();
gpu::SyncToken latest_sync_token;
for (const auto* in_use : resources) {
const gpu::SyncToken& sync_token = in_use->backing()->mailbox_sync_token;
if (sync_token.release_count() > latest_sync_token.release_count())
latest_sync_token = sync_token;
}
uint64_t callback_id = latest_sync_token.release_count();
DCHECK_NE(callback_id, 0u);
// If the callback is different from the one the caller is already waiting on,
// pass the callback through to SignalSyncToken. Otherwise the request is
// redundant.
if (callback_id != pending_callback_id) {
// Use the compositor context because we want this callback on the
// compositor thread.
compositor_context_provider_->ContextSupport()->SignalSyncToken(
latest_sync_token, std::move(callback));
}
return callback_id;
}
void OneCopyRasterBufferProvider::Shutdown() {
staging_pool_.Shutdown();
}
gpu::SyncToken OneCopyRasterBufferProvider::PlaybackAndCopyOnWorkerThread(
ResourcePool::Backing* backing,
bool mailbox_texture_is_overlay_candidate,
const gpu::SyncToken& sync_token,
const RasterSource* raster_source,
const gfx::Rect& raster_full_rect,
const gfx::Rect& raster_dirty_rect,
const gfx::AxisTransform2d& transform,
const RasterSource::PlaybackSettings& playback_settings,
uint64_t previous_content_id,
uint64_t new_content_id,
bool& should_destroy_shared_image) {
std::unique_ptr<StagingBuffer> staging_buffer =
staging_pool_.AcquireStagingBuffer(backing->size(), backing->format(),
previous_content_id);
DCHECK(staging_buffer->size.width() >= raster_full_rect.width() &&
staging_buffer->size.height() >= raster_full_rect.height());
bool put_data_in_staging_buffer = PlaybackToStagingBuffer(
staging_buffer.get(), raster_source, raster_full_rect, raster_dirty_rect,
transform, backing->format(), backing->color_space(), playback_settings,
previous_content_id, new_content_id);
gpu::SyncToken sync_token_after_upload;
if (put_data_in_staging_buffer) {
sync_token_after_upload = CopyOnWorkerThread(
staging_buffer.get(), raster_source, raster_full_rect, backing,
mailbox_texture_is_overlay_candidate, sync_token);
} else if (backing->shared_image()) {
// If we failed to put data in the staging buffer
// (https://crbug.com/554541), then we don't have anything to give to copy
// into the resource. We report a zero mailbox that will result in
// checkerboarding, and be treated as OOM which should retry.
should_destroy_shared_image = true;
}
staging_pool_.ReleaseStagingBuffer(std::move(staging_buffer));
return sync_token_after_upload;
}
bool OneCopyRasterBufferProvider::PlaybackToStagingBuffer(
StagingBuffer* staging_buffer,
const RasterSource* raster_source,
const gfx::Rect& raster_full_rect,
const gfx::Rect& raster_dirty_rect,
const gfx::AxisTransform2d& transform,
viz::SharedImageFormat format,
const gfx::ColorSpace& dst_color_space,
const RasterSource::PlaybackSettings& playback_settings,
uint64_t previous_content_id,
uint64_t new_content_id) {
gfx::Rect playback_rect = raster_full_rect;
if (use_partial_raster_ && previous_content_id) {
// Reduce playback rect to dirty region if the content id of the staging
// buffer matches the previous content id.
if (previous_content_id == staging_buffer->content_id) {
playback_rect.Intersect(raster_dirty_rect);
}
}
DCHECK(!playback_rect.IsEmpty())
<< "Why are we rastering a tile that's not dirty?";
// Allocate mappable SharedImage if necessary.
if (!staging_buffer->client_shared_image) {
staging_buffer->client_shared_image = sii_->CreateSharedImage(
{format, staging_buffer->size, dst_color_space,
gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY |
gpu::SHARED_IMAGE_USAGE_RASTER_COPY_SOURCE,
"OneCopyRasterStaging"},
gpu::kNullSurfaceHandle, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE);
if (!staging_buffer->client_shared_image) {
LOG(ERROR) << "Creation of StagingBuffer's SharedImage failed.";
return false;
}
}
auto mapping = staging_buffer->client_shared_image->Map();
if (!mapping) {
LOG(ERROR) << "MapSharedImage Failed.";
return false;
}
staging_buffer->is_shared_memory = mapping->IsSharedMemory();
RasterBufferProvider::PlaybackToMemory(
mapping->GetMemoryForPlane(0).data(), format, staging_buffer->size,
mapping->Stride(0), raster_source, raster_full_rect, playback_rect,
transform, dst_color_space, playback_settings);
staging_buffer->content_id = new_content_id;
return true;
}
gpu::SyncToken OneCopyRasterBufferProvider::CopyOnWorkerThread(
StagingBuffer* staging_buffer,
const RasterSource* raster_source,
const gfx::Rect& rect_to_copy,
ResourcePool::Backing* backing,
bool mailbox_texture_is_overlay_candidate,
const gpu::SyncToken& sync_token) {
const gfx::Size& resource_size = backing->size();
DCHECK(sii_);
CHECK(staging_buffer->client_shared_image);
bool needs_clear = false;
if (!backing->shared_image()) {
// This SharedImage will have the contents of raster operations copied into
// it via the raster interface before being sent off to the display
// compositor.
gpu::SharedImageUsageSet usage = gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
gpu::SHARED_IMAGE_USAGE_RASTER_WRITE;
if (mailbox_texture_is_overlay_candidate)
usage |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
backing->CreateSharedImage(sii_.get(), usage, "OneCopyRasterTile");
// Clear the resource if we're not going to initialize it fully from the
// copy due to non-exact resource reuse. See https://crbug.com/1313091
needs_clear = rect_to_copy.size() != resource_size;
}
sii_->UpdateSharedImage(staging_buffer->sync_token,
staging_buffer->client_shared_image->mailbox());
viz::RasterContextProvider::ScopedRasterContextLock scoped_context(
worker_context_provider_);
gpu::raster::RasterInterface* ri = scoped_context.RasterInterface();
DCHECK(ri);
std::unique_ptr<gpu::RasterScopedAccess> dst_ri_access =
backing->shared_image()->BeginRasterAccess(ri, sync_token,
/*readonly=*/false);
std::unique_ptr<gpu::RasterScopedAccess> src_ri_access =
staging_buffer->client_shared_image->BeginRasterAccess(
ri, sii_->GenUnverifiedSyncToken(), /*readonly=*/true);
// Do not use queries unless COMMANDS_COMPLETED queries are supported, or
// COMMANDS_ISSUED queries are sufficient.
GLenum query_target = GL_NONE;
if (worker_context_provider_->ContextCapabilities().sync_query) {
// Use GL_COMMANDS_COMPLETED_CHROMIUM when supported because native
// GpuMemoryBuffers can be accessed by the GPU after commands are issued
// until GPU reads are done.
query_target = GL_COMMANDS_COMPLETED_CHROMIUM;
}
// COMMANDS_ISSUED is sufficient for shared memory resources.
if (staging_buffer->is_shared_memory) {
query_target = GL_COMMANDS_ISSUED_CHROMIUM;
}
if (query_target != GL_NONE) {
if (!staging_buffer->query_id)
ri->GenQueriesEXT(1, &staging_buffer->query_id);
ri->BeginQueryEXT(query_target, staging_buffer->query_id);
}
uint32_t texture_target = backing->shared_image()->GetTextureTarget();
// Clear to ensure the resource is fully initialized and BeginAccess succeeds.
if (needs_clear) {
SkImageInfo dst_info = SkImageInfo::Make(
{resource_size.width(), resource_size.height()},
ToClosestSkColorType(backing->format()), kPremul_SkAlphaType);
SkBitmap bitmap;
if (bitmap.tryAllocPixels(dst_info)) {
bitmap.eraseColor(raster_source->background_color());
ri->WritePixels(backing->shared_image()->mailbox(), /*dst_x_offset=*/0,
/*dst_y_offset=*/0, texture_target, bitmap.pixmap());
}
}
ri->CopySharedImage(staging_buffer->client_shared_image->mailbox(),
backing->shared_image()->mailbox(), 0, 0, 0, 0,
rect_to_copy.width(), rect_to_copy.height());
if (query_target != GL_NONE)
ri->EndQueryEXT(query_target);
// Generate sync token on the worker context that will be sent to and waited
// for by the display compositor before using the content generated here.
// The same sync token is used to synchronize operations on the staging
// buffer. Note, the query completion is generally enough to guarantee
// ordering, but there are some paths (e.g.
// StagingBufferPool::ReduceMemoryUsage) that may destroy the staging buffer
// without waiting for the query completion.
gpu::RasterScopedAccess::EndAccess(std::move(dst_ri_access));
gpu::SyncToken out_sync_token =
gpu::RasterScopedAccess::EndAccess(std::move(src_ri_access));
staging_buffer->sync_token = out_sync_token;
return out_sync_token;
}
} // namespace cc