blob: 49bffdb9ecd6c8534b74da3f5ff324907d232c1b [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/gpu_raster_buffer_provider.h"
#include <stdint.h>
#include <algorithm>
#include <bit>
#include <memory>
#include <utility>
#include <vector>
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/rand_util.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event.h"
#include "build/chromeos_buildflags.h"
#include "cc/base/features.h"
#include "cc/base/histograms.h"
#include "cc/paint/display_item_list.h"
#include "cc/paint/paint_canvas.h"
#include "cc/paint/paint_recorder.h"
#include "cc/raster/raster_source.h"
#include "components/viz/client/client_resource_provider.h"
#include "components/viz/common/features.h"
#include "components/viz/common/gpu/raster_context_provider.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 "skia/ext/legacy_display_globals.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/geometry/axis_transform2d.h"
#include "url/gurl.h"
namespace cc {
// Subclass for InUsePoolResource that holds ownership of a gpu-rastered backing
// and does cleanup of the backing when destroyed.
class GpuRasterBufferProvider::GpuRasterBacking
: public ResourcePool::GpuBacking {
public:
~GpuRasterBacking() override {
if (!shared_image) {
return;
}
auto* sii = worker_context_provider->SharedImageInterface();
if (returned_sync_token.HasData())
sii->DestroySharedImage(returned_sync_token, std::move(shared_image));
else if (mailbox_sync_token.HasData())
sii->DestroySharedImage(mailbox_sync_token, std::move(shared_image));
}
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 (!shared_image) {
return;
}
auto tracing_guid = shared_image->GetGUIDForTracing();
pmd->CreateSharedGlobalAllocatorDump(tracing_guid);
pmd->AddOwnershipEdge(buffer_dump_guid, tracing_guid, importance);
}
// The context used to clean up the mailbox
raw_ptr<viz::RasterContextProvider> worker_context_provider = nullptr;
};
GpuRasterBufferProvider::RasterBufferImpl::RasterBufferImpl(
GpuRasterBufferProvider* client,
const ResourcePool::InUsePoolResource& in_use_resource,
GpuRasterBacking* backing,
bool resource_has_previous_content,
bool depends_on_at_raster_decodes,
bool depends_on_hardware_accelerated_jpeg_candidates,
bool depends_on_hardware_accelerated_webp_candidates)
: client_(client),
backing_(backing),
resource_size_(in_use_resource.size()),
shared_image_format_(in_use_resource.format()),
color_space_(in_use_resource.color_space()),
resource_has_previous_content_(resource_has_previous_content),
depends_on_at_raster_decodes_(depends_on_at_raster_decodes),
depends_on_hardware_accelerated_jpeg_candidates_(
depends_on_hardware_accelerated_jpeg_candidates),
depends_on_hardware_accelerated_webp_candidates_(
depends_on_hardware_accelerated_webp_candidates) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Only do this in Chrome OS because:
// 1) We will use this timestamp to measure raster scheduling delay and we
// only need to collect that data to assess the impact of hardware
// acceleration of image decodes which works only on Chrome OS.
// 2) We use CLOCK_MONOTONIC in that OS to get timestamps, so we can assert
// certain assumptions.
creation_time_ = base::TimeTicks::Now();
#endif
}
GpuRasterBufferProvider::RasterBufferImpl::~RasterBufferImpl() = default;
void GpuRasterBufferProvider::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", "GpuRasterBuffer::Playback");
viz::RasterContextProvider::ScopedRasterContextLock scoped_context(
client_->worker_context_provider_, url.possibly_invalid_spec().c_str());
gpu::raster::RasterInterface* ri =
client_->worker_context_provider_->RasterInterface();
PlaybackOnWorkerThread(raster_source, raster_full_rect, raster_dirty_rect,
new_content_id, transform, playback_settings, url);
backing_->mailbox_sync_token =
viz::ClientResourceProvider::GenerateSyncTokenHelper(ri);
backing_->returned_sync_token = gpu::SyncToken();
}
bool GpuRasterBufferProvider::RasterBufferImpl::
SupportsBackgroundThreadPriority() const {
return true;
}
GpuRasterBufferProvider::GpuRasterBufferProvider(
viz::RasterContextProvider* compositor_context_provider,
viz::RasterContextProvider* worker_context_provider,
const RasterCapabilities& raster_caps,
const gfx::Size& max_tile_size,
bool unpremultiply_and_dither_low_bit_depth_tiles,
RasterQueryQueue* const pending_raster_queries,
float raster_metric_probability)
: compositor_context_provider_(compositor_context_provider),
worker_context_provider_(worker_context_provider),
tile_format_(raster_caps.tile_format),
tile_overlay_candidate_(raster_caps.tile_overlay_candidate),
tile_texture_target_(raster_caps.tile_texture_target),
max_tile_size_(max_tile_size),
pending_raster_queries_(pending_raster_queries),
raster_metric_probability_(raster_metric_probability),
is_using_raw_draw_(features::IsUsingRawDraw()),
is_using_dmsaa_(
base::FeatureList::IsEnabled(features::kUseDMSAAForTiles)) {
DCHECK(pending_raster_queries);
DCHECK(compositor_context_provider);
CHECK(worker_context_provider);
#if BUILDFLAG(IS_ANDROID)
{
std::optional<viz::RasterContextProvider::ScopedRasterContextLock> lock;
lock.emplace(worker_context_provider);
auto is_using_vulkan =
worker_context_provider->ContextCapabilities().using_vulkan_context;
// On Android, DMSAA on vulkan backend launch is controlled by
// kUseDMSAAForTiles whereas GL backend launch is controlled by
// kUseDMSAAForTilesAndroidGL.
is_using_dmsaa_ =
(base::FeatureList::IsEnabled(features::kUseDMSAAForTiles) &&
is_using_vulkan) ||
(base::FeatureList::IsEnabled(features::kUseDMSAAForTilesAndroidGL) &&
!is_using_vulkan);
}
#endif
}
GpuRasterBufferProvider::~GpuRasterBufferProvider() = default;
std::unique_ptr<RasterBuffer> GpuRasterBufferProvider::AcquireBufferForRaster(
const ResourcePool::InUsePoolResource& resource,
uint64_t resource_content_id,
uint64_t previous_content_id,
bool depends_on_at_raster_decodes,
bool depends_on_hardware_accelerated_jpeg_candidates,
bool depends_on_hardware_accelerated_webp_candidates) {
if (!resource.gpu_backing()) {
auto backing = std::make_unique<GpuRasterBacking>();
backing->worker_context_provider = worker_context_provider_;
backing->overlay_candidate = tile_overlay_candidate_;
backing->texture_target = tile_texture_target_;
backing->is_using_raw_draw =
!backing->overlay_candidate && is_using_raw_draw_;
resource.set_gpu_backing(std::move(backing));
}
GpuRasterBacking* backing =
static_cast<GpuRasterBacking*>(resource.gpu_backing());
bool resource_has_previous_content =
resource_content_id && resource_content_id == previous_content_id;
return std::make_unique<RasterBufferImpl>(
this, resource, backing, resource_has_previous_content,
depends_on_at_raster_decodes,
depends_on_hardware_accelerated_jpeg_candidates,
depends_on_hardware_accelerated_webp_candidates);
}
void GpuRasterBufferProvider::Flush() {
compositor_context_provider_->ContextSupport()->FlushPendingWork();
}
viz::SharedImageFormat GpuRasterBufferProvider::GetFormat() const {
return tile_format_;
}
bool GpuRasterBufferProvider::IsResourcePremultiplied() const {
return !ShouldUnpremultiplyAndDitherResource(GetFormat());
}
bool GpuRasterBufferProvider::IsResourceReadyToDraw(
const ResourcePool::InUsePoolResource& resource) {
FlushIfNeeded();
const gpu::SyncToken& sync_token = resource.gpu_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);
}
bool GpuRasterBufferProvider::CanPartialRasterIntoProvidedResource() const {
return true;
}
uint64_t GpuRasterBufferProvider::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->gpu_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 GpuRasterBufferProvider::Shutdown() {}
void GpuRasterBufferProvider::RasterBufferImpl::PlaybackOnWorkerThread(
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) {
RasterQuery query;
query.depends_on_hardware_accelerated_jpeg_candidates =
depends_on_hardware_accelerated_jpeg_candidates_;
query.depends_on_hardware_accelerated_webp_candidates =
depends_on_hardware_accelerated_webp_candidates_;
PlaybackOnWorkerThreadInternal(raster_source, raster_full_rect,
raster_dirty_rect, new_content_id, transform,
playback_settings, url, &query);
if (query.raster_duration_query_id) {
if (query.raster_start_query_id)
query.raster_buffer_creation_time = creation_time_;
// Note that it is important to scope the raster context lock to
// PlaybackOnWorkerThreadInternal and release it before calling this
// function to avoid a deadlock in
// RasterQueryQueue::CheckRasterFinishedQueries which acquires the raster
// context lock while holding a lock used in the function.
client_->pending_raster_queries_->Append(std::move(query));
}
}
void GpuRasterBufferProvider::RasterBufferImpl::PlaybackOnWorkerThreadInternal(
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,
RasterQuery* query) {
gpu::raster::RasterInterface* ri =
client_->worker_context_provider_->RasterInterface();
DCHECK(ri);
const bool measure_raster_metric = client_->metrics_subsampler_.ShouldSample(
client_->raster_metric_probability_);
gfx::Rect playback_rect = raster_full_rect;
if (resource_has_previous_content_) {
playback_rect.Intersect(raster_dirty_rect);
}
DCHECK(!playback_rect.IsEmpty())
<< "Why are we rastering a tile that's not dirty?";
if (measure_raster_metric) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Use a query to detect when the GPU side is ready to start issuing raster
// work to the driver. We will use the resulting timestamp to measure raster
// scheduling delay. We only care about this in Chrome OS because we will
// use this timestamp to measure raster scheduling delay and we only need to
// collect that data to assess the impact of hardware acceleration of image
// decodes which work only in Chrome OS. Furthermore, we don't count raster
// work that depends on at-raster image decodes. This is because we want the
// delay to always include image decoding and uploading time, and at-raster
// decodes should be relatively rare.
if (!depends_on_at_raster_decodes_) {
ri->GenQueriesEXT(1, &query->raster_start_query_id);
DCHECK_GT(query->raster_start_query_id, 0u);
ri->QueryCounterEXT(query->raster_start_query_id,
GL_COMMANDS_ISSUED_TIMESTAMP_CHROMIUM);
}
#else
std::ignore = depends_on_at_raster_decodes_;
#endif
// Use a query to time the GPU side work for rasterizing this tile.
ri->GenQueriesEXT(1, &query->raster_duration_query_id);
DCHECK_GT(query->raster_duration_query_id, 0u);
ri->BeginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM,
query->raster_duration_query_id);
}
{
std::optional<base::ElapsedTimer> timer;
if (measure_raster_metric)
timer.emplace();
RasterizeSource(raster_source, raster_full_rect, playback_rect, transform,
playback_settings);
if (measure_raster_metric) {
query->worker_raster_duration = timer->Elapsed();
ri->EndQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM);
}
}
}
void GpuRasterBufferProvider::RasterBufferImpl::RasterizeSource(
const RasterSource* raster_source,
const gfx::Rect& raster_full_rect,
const gfx::Rect& playback_rect,
const gfx::AxisTransform2d& transform,
const RasterSource::PlaybackSettings& playback_settings) {
gpu::raster::RasterInterface* ri =
client_->worker_context_provider_->RasterInterface();
bool mailbox_needs_clear = false;
if (!backing_->shared_image) {
DCHECK(!backing_->returned_sync_token.HasData());
auto* sii = client_->worker_context_provider_->SharedImageInterface();
// This SharedImage will serve as the destination of the raster defined by
// `raster_source` before being sent off to the display compositor.
uint32_t flags = gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
gpu::SHARED_IMAGE_USAGE_RASTER_WRITE |
gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
if (backing_->overlay_candidate) {
flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
if (features::IsDelegatedCompositingEnabled())
flags |= gpu::SHARED_IMAGE_USAGE_RASTER_DELEGATED_COMPOSITING;
} else if (client_->is_using_raw_draw_) {
flags |= gpu::SHARED_IMAGE_USAGE_RAW_DRAW;
}
backing_->shared_image = sii->CreateSharedImage(
shared_image_format_, resource_size_, color_space_,
kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, flags, "GpuRasterTile",
gpu::kNullSurfaceHandle);
CHECK(backing_->shared_image);
mailbox_needs_clear = true;
ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
} else {
ri->WaitSyncTokenCHROMIUM(backing_->returned_sync_token.GetConstData());
}
// Assume legacy MSAA if sample count is positive.
gpu::raster::MsaaMode msaa_mode =
playback_settings.msaa_sample_count > 0
? (client_->is_using_dmsaa_ ? gpu::raster::kDMSAA
: gpu::raster::kMSAA)
: gpu::raster::kNoMSAA;
// msaa_sample_count should be 1, 2, 4, 8, 16, 32, 64,
// and log2(msaa_sample_count) should be [0,6].
// If playback_settings.msaa_sample_count <= 0, the MSAA is not used. It is
// equivalent to MSAA sample count 1.
uint32_t sample_count =
std::clamp(playback_settings.msaa_sample_count, 1, 64);
UMA_HISTOGRAM_CUSTOM_COUNTS("Gpu.Rasterization.Raster.MSAASampleCountLog2",
std::bit_width(sample_count) - 1, 0, 7, 7);
// With Raw Draw, the framebuffer will be the rasterization target. It cannot
// support LCD text, so disable LCD text for Raw Draw backings.
// TODO(penghuang): remove it when sktext::gpu::Slug can be serialized.
bool is_raw_draw_backing =
client_->is_using_raw_draw_ && !backing_->overlay_candidate;
bool use_lcd_text = playback_settings.use_lcd_text && !is_raw_draw_backing;
ri->BeginRasterCHROMIUM(
raster_source->background_color(), mailbox_needs_clear,
playback_settings.msaa_sample_count, msaa_mode, use_lcd_text,
playback_settings.visible, color_space_, playback_settings.hdr_headroom,
backing_->shared_image->mailbox().name);
gfx::Vector2dF recording_to_raster_scale = transform.scale();
recording_to_raster_scale.InvScale(raster_source->recording_scale_factor());
gfx::Size content_size = raster_source->GetContentSize(transform.scale());
// TODO(enne): could skip the clear on new textures, as the service side has
// to do that anyway. resource_has_previous_content implies that the texture
// is not new, but the reverse does not hold, so more plumbing is needed.
ri->RasterCHROMIUM(
raster_source->GetDisplayItemList().get(),
playback_settings.image_provider, content_size, raster_full_rect,
playback_rect, transform.translation(), recording_to_raster_scale,
raster_source->requires_clear(),
const_cast<RasterSource*>(raster_source)->max_op_size_hint());
ri->EndRasterCHROMIUM();
// TODO(ericrk): Handle unpremultiply+dither for 4444 cases.
// https://crbug.com/789153
}
bool GpuRasterBufferProvider::ShouldUnpremultiplyAndDitherResource(
viz::SharedImageFormat format) const {
// TODO(crbug.com/1151490): Re-enable for OOPR.
return false;
}
} // namespace cc