910e62b5创建于 1月15日历史提交
// 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