#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()) {
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() {
backing_->can_access_shared_image_on_compositor_thread = true;
backing_->mailbox_sync_token = after_raster_sync_token_;
if (after_raster_sync_token_.HasData()) {
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");
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 {
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) {
return std::make_unique<RasterBufferImpl>(this, resource,
previous_content_id);
}
void OneCopyRasterBufferProvider::Flush() {
compositor_context_provider_->ContextSupport()->FlushPendingWork();
}
bool OneCopyRasterBufferProvider::CanPartialRasterIntoProvidedResource() const {
return false;
}
bool OneCopyRasterBufferProvider::IsResourceReadyToDraw(
const ResourcePool::InUsePoolResource& resource) {
FlushIfNeeded();
const gpu::SyncToken& sync_token = resource.backing()->mailbox_sync_token;
DCHECK(sync_token.HasData());
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 (callback_id != pending_callback_id) {
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()) {
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) {
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?";
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()) {
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");
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,
false);
std::unique_ptr<gpu::RasterScopedAccess> src_ri_access =
staging_buffer->client_shared_image->BeginRasterAccess(
ri, sii_->GenUnverifiedSyncToken(), true);
GLenum query_target = GL_NONE;
if (worker_context_provider_->ContextCapabilities().sync_query) {
query_target = GL_COMMANDS_COMPLETED_CHROMIUM;
}
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();
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(), 0,
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);
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;
}
}