blob: 38e45c1d410d75420fa8f8a904c8a11e53d81eb9 [file] [log] [blame]
// Copyright 2018 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 "components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h"
#include "base/atomic_sequence_num.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/optional.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/viz/common/features.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_util.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/common/skia_helper.h"
#include "components/viz/service/display/gl_renderer_copier.h"
#include "components/viz/service/display/output_surface_frame.h"
#include "components/viz/service/display/texture_deleter.h"
#include "components/viz/service/display_embedder/direct_context_provider.h"
#include "components/viz/service/display_embedder/image_context.h"
#include "components/viz/service/display_embedder/skia_output_device.h"
#include "components/viz/service/display_embedder/skia_output_device_gl.h"
#include "components/viz/service/display_embedder/skia_output_device_offscreen.h"
#include "components/viz/service/display_embedder/skia_output_surface_dependency.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/common/swap_buffers_complete_params.h"
#include "gpu/command_buffer/service/context_state.h"
#include "gpu/command_buffer/service/gr_shader_cache.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/scheduler.h"
#include "gpu/command_buffer/service/service_utils.h"
#include "gpu/command_buffer/service/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image_representation.h"
#include "gpu/command_buffer/service/skia_utils.h"
#include "gpu/command_buffer/service/texture_base.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "gpu/config/gpu_preferences.h"
#include "gpu/ipc/common/gpu_client_ids.h"
#include "gpu/ipc/common/gpu_surface_lookup.h"
#include "gpu/ipc/service/image_transport_surface.h"
#include "gpu/vulkan/buildflags.h"
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkPixelRef.h"
#include "third_party/skia/include/private/SkDeferredDisplayList.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/skia_util.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_gl_api_implementation.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gl_version_info.h"
#include "ui/gl/init/gl_factory.h"
#if BUILDFLAG(ENABLE_VULKAN)
#include "components/viz/service/display_embedder/skia_output_device_vulkan.h"
#endif
#if BUILDFLAG(ENABLE_VULKAN) && defined(USE_X11)
#include "components/viz/service/display_embedder/skia_output_device_x11.h"
#endif
#if defined(USE_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#include "ui/ozone/public/platform_window_surface.h"
#include "ui/ozone/public/surface_factory_ozone.h"
#endif
namespace viz {
namespace {
struct ReadPixelsContext {
ReadPixelsContext(std::unique_ptr<CopyOutputRequest> request,
const gfx::Rect& result_rect)
: request(std::move(request)), result_rect(result_rect) {}
std::unique_ptr<CopyOutputRequest> request;
gfx::Rect result_rect;
};
class CopyOutputResultYUV : public CopyOutputResult {
public:
CopyOutputResultYUV(const gfx::Rect& rect,
const void* data[3],
size_t row_bytes[3])
: CopyOutputResult(Format::I420_PLANES, rect) {
DCHECK_EQ(0, size().width() % 2);
DCHECK_EQ(0, size().height() % 2);
for (int i = 0; i < 3; ++i) {
data_[i] = std::make_unique<uint8_t[]>(row_bytes[i] * height(i));
memcpy(data_[i].get(), data[i], row_bytes[i] * height(i));
row_bytes_[i] = row_bytes[i];
}
}
// CopyOutputResult implementation.
bool ReadI420Planes(uint8_t* y_out,
int y_out_stride,
uint8_t* u_out,
int u_out_stride,
uint8_t* v_out,
int v_out_stride) const override {
const auto CopyPlane = [](const uint8_t* src, int src_stride, int width,
int height, uint8_t* out, int out_stride) {
for (int i = 0; i < height; ++i, src += src_stride, out += out_stride) {
memcpy(out, src, width);
}
};
CopyPlane(data_[0].get(), row_bytes_[0], width(0), height(0), y_out,
y_out_stride);
CopyPlane(data_[1].get(), row_bytes_[1], width(1), height(1), u_out,
u_out_stride);
CopyPlane(data_[2].get(), row_bytes_[2], width(2), height(2), v_out,
v_out_stride);
return true;
}
private:
uint32_t width(int plane) const {
if (plane == 0)
return size().width();
else
return size().width() / 2;
}
uint32_t height(int plane) const {
if (plane == 0)
return size().height();
else
return size().height() / 2;
}
std::unique_ptr<uint8_t[]> data_[3];
size_t row_bytes_[3];
};
void OnYUVReadbackDone(void* c, const void* data[3], size_t row_bytes[3]) {
std::unique_ptr<ReadPixelsContext> context(
static_cast<ReadPixelsContext*>(c));
if (!data) {
// This will automatically send an empty result.
return;
}
std::unique_ptr<CopyOutputResult> result =
std::make_unique<CopyOutputResultYUV>(context->result_rect, data,
row_bytes);
context->request->SendResult(std::move(result));
}
} // namespace
class SkiaOutputSurfaceImplOnGpu::ScopedPromiseImageAccess {
public:
ScopedPromiseImageAccess(SkiaOutputSurfaceImplOnGpu* impl_on_gpu,
std::vector<ImageContext*> image_contexts)
: impl_on_gpu_(impl_on_gpu), image_contexts_(std::move(image_contexts)) {
begin_semaphores_.reserve(image_contexts_.size());
// We may need one more space for the swap buffer semaphore.
end_semaphores_.reserve(image_contexts_.size() + 1);
// TODO(penghuang): gather begin read access semaphores from shared images.
// https://crbug.com/944194
impl_on_gpu_->BeginAccessImages(image_contexts_, &begin_semaphores_,
&end_semaphores_);
}
~ScopedPromiseImageAccess() {
// TODO(penghuang): end shared image access with meaningful semaphores.
// https://crbug.com/944194
impl_on_gpu_->EndAccessImages(image_contexts_);
}
std::vector<GrBackendSemaphore>& begin_semaphores() {
return begin_semaphores_;
}
std::vector<GrBackendSemaphore>& end_semaphores() { return end_semaphores_; }
private:
SkiaOutputSurfaceImplOnGpu* const impl_on_gpu_;
std::vector<ImageContext*> image_contexts_;
std::vector<GrBackendSemaphore> begin_semaphores_;
std::vector<GrBackendSemaphore> end_semaphores_;
DISALLOW_COPY_AND_ASSIGN(ScopedPromiseImageAccess);
};
// Skia gr_context() and |context_provider_| share an underlying GLContext.
// Each of them caches some GL state. Interleaving usage could make cached
// state inconsistent with GL state. Using a ScopedUseContextProvider whenever
// |context_provider_| could be accessed (e.g. processing completed queries),
// will keep cached state consistent with driver GL state.
class SkiaOutputSurfaceImplOnGpu::ScopedUseContextProvider {
public:
ScopedUseContextProvider(SkiaOutputSurfaceImplOnGpu* impl_on_gpu,
GLuint texture_client_id)
: impl_on_gpu_(impl_on_gpu) {
if (!impl_on_gpu_->MakeCurrent(true /* need_fbo0 */)) {
valid_ = false;
return;
}
// GLRendererCopier uses context_provider_->ContextGL(), which caches GL
// state and removes state setting calls that it considers redundant. To get
// to a known GL state, we first set driver GL state and then make client
// side consistent with that.
auto* api = impl_on_gpu_->api_;
api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, 0);
impl_on_gpu_->context_provider_->SetGLRendererCopierRequiredState(
texture_client_id);
}
~ScopedUseContextProvider() {
if (valid_)
impl_on_gpu_->gr_context()->resetContext();
}
bool valid() { return valid_; }
private:
SkiaOutputSurfaceImplOnGpu* const impl_on_gpu_;
bool valid_ = true;
DISALLOW_COPY_AND_ASSIGN(ScopedUseContextProvider);
};
namespace {
base::AtomicSequenceNumber g_next_command_buffer_id;
scoped_refptr<gpu::gles2::FeatureInfo> CreateFeatureInfo(
SkiaOutputSurfaceDependency* deps) {
return base::MakeRefCounted<gpu::gles2::FeatureInfo>(
deps->GetGpuDriverBugWorkarounds(), deps->GetGpuFeatureInfo());
}
scoped_refptr<gpu::SyncPointClientState> CreateSyncPointClientState(
SkiaOutputSurfaceDependency* deps) {
auto command_buffer_id = gpu::CommandBufferId::FromUnsafeValue(
g_next_command_buffer_id.GetNext() + 1);
return deps->GetSyncPointManager()->CreateSyncPointClientState(
gpu::CommandBufferNamespace::VIZ_SKIA_OUTPUT_SURFACE, command_buffer_id,
deps->GetSequenceId());
}
std::unique_ptr<gpu::SharedImageRepresentationFactory>
CreateSharedImageRepresentationFactory(SkiaOutputSurfaceDependency* deps) {
// TODO(https://crbug.com/899905): Use a real MemoryTracker, not nullptr.
return std::make_unique<gpu::SharedImageRepresentationFactory>(
deps->GetSharedImageManager(), nullptr);
}
class ScopedSurfaceToTexture {
public:
ScopedSurfaceToTexture(scoped_refptr<DirectContextProvider> context_provider,
SkSurface* surface)
: context_provider_(context_provider) {
GrBackendTexture skia_texture =
surface->getBackendTexture(SkSurface::kFlushRead_BackendHandleAccess);
GrGLTextureInfo gl_texture_info;
skia_texture.getGLTextureInfo(&gl_texture_info);
GLuint client_id = context_provider_->GenClientTextureId();
auto* texture_manager = context_provider_->texture_manager();
texture_ref_ =
texture_manager->CreateTexture(client_id, gl_texture_info.fID);
texture_manager->SetTarget(texture_ref_.get(), gl_texture_info.fTarget);
texture_manager->SetLevelInfo(
texture_ref_.get(), gl_texture_info.fTarget,
/*level=*/0,
/*internal_format=*/GL_RGBA, surface->width(), surface->height(),
/*depth=*/1, /*border=*/0,
/*format=*/GL_RGBA, /*type=*/GL_UNSIGNED_BYTE,
/*cleared_rect=*/gfx::Rect(surface->width(), surface->height()));
}
~ScopedSurfaceToTexture() {
context_provider_->DeleteClientTextureId(client_id());
// Skia owns the texture. It will delete it when it is done.
texture_ref_->ForceContextLost();
}
GLuint client_id() { return texture_ref_->client_id(); }
private:
scoped_refptr<DirectContextProvider> context_provider_;
scoped_refptr<gpu::gles2::TextureRef> texture_ref_;
DISALLOW_COPY_AND_ASSIGN(ScopedSurfaceToTexture);
};
// This SingleThreadTaskRunner runs tasks on the GPU main thread, where
// DirectContextProvider can safely service calls. It wraps all posted tasks to
// ensure that |impl_on_gpu_->context_provider_| is made current and in a known
// state when the task is run. If |impl_on_gpu| is destructed, pending tasks are
// no-oped when they are run.
class ContextCurrentTaskRunner : public base::SingleThreadTaskRunner {
public:
explicit ContextCurrentTaskRunner(SkiaOutputSurfaceImplOnGpu* impl_on_gpu)
: real_task_runner_(base::ThreadTaskRunnerHandle::Get()),
impl_on_gpu_(impl_on_gpu) {}
bool PostDelayedTask(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
return real_task_runner_->PostDelayedTask(
from_here, WrapClosure(std::move(task)), delay);
}
bool PostNonNestableDelayedTask(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
return real_task_runner_->PostNonNestableDelayedTask(
from_here, WrapClosure(std::move(task)), delay);
}
bool RunsTasksInCurrentSequence() const override {
return real_task_runner_->RunsTasksInCurrentSequence();
}
private:
base::OnceClosure WrapClosure(base::OnceClosure task) {
return base::BindOnce(
[](base::WeakPtr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu,
base::OnceClosure task) {
if (!impl_on_gpu)
return;
SkiaOutputSurfaceImplOnGpu::ScopedUseContextProvider scoped_use(
impl_on_gpu.get(), /*texture_client_id=*/0);
if (!scoped_use.valid())
return;
std::move(task).Run();
},
impl_on_gpu_->weak_ptr(), std::move(task));
}
~ContextCurrentTaskRunner() override = default;
scoped_refptr<base::SingleThreadTaskRunner> real_task_runner_;
SkiaOutputSurfaceImplOnGpu* const impl_on_gpu_;
DISALLOW_COPY_AND_ASSIGN(ContextCurrentTaskRunner);
};
class DirectContextProviderDelegateImpl : public DirectContextProviderDelegate,
public gpu::SharedImageInterface {
public:
DirectContextProviderDelegateImpl(
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuDriverBugWorkarounds& workarounds,
const gpu::GpuFeatureInfo& gpu_feature_info,
gpu::SharedContextState* context_state,
gpu::MailboxManager* mailbox_manager,
gpu::SharedImageManager* shared_image_manager,
scoped_refptr<gpu::SyncPointClientState> sync_point_client_state)
: shared_image_manager_(shared_image_manager),
shared_image_factory_(gpu_preferences,
workarounds,
gpu_feature_info,
context_state,
mailbox_manager,
shared_image_manager,
nullptr /* image_factory */,
nullptr /* memory_tracker */,
true /* is_using_skia_renderer */),
sync_point_client_state_(sync_point_client_state) {}
~DirectContextProviderDelegateImpl() override {
sync_point_client_state_->Destroy();
}
// SharedImageInterface implementation:
gpu::Mailbox CreateSharedImage(ResourceFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
uint32_t usage) override {
auto mailbox = gpu::Mailbox::GenerateForSharedImage();
if (shared_image_factory_.CreateSharedImage(mailbox, format, size,
color_space, usage))
return mailbox;
return gpu::Mailbox();
}
gpu::Mailbox CreateSharedImage(
ResourceFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
uint32_t usage,
base::span<const uint8_t> pixel_data) override {
auto mailbox = gpu::Mailbox::GenerateForSharedImage();
if (shared_image_factory_.CreateSharedImage(mailbox, format, size,
color_space, usage, pixel_data))
return mailbox;
return gpu::Mailbox();
}
gpu::Mailbox CreateSharedImage(
gfx::GpuMemoryBuffer* gpu_memory_buffer,
gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
const gfx::ColorSpace& color_space,
uint32_t usage) override {
// We do not support creating GMB backed SharedImages.
NOTIMPLEMENTED();
return gpu::Mailbox();
}
void UpdateSharedImage(const gpu::SyncToken& sync_token,
std::unique_ptr<gfx::GpuFence> acquire_fence,
const gpu::Mailbox& mailbox) override {
NOTREACHED();
}
void UpdateSharedImage(const gpu::SyncToken& sync_token,
const gpu::Mailbox& mailbox) override {
DCHECK(!ShouldWait(sync_token))
<< "Cannot UpdateSharedImage with SyncToken from different "
"command buffer.";
shared_image_factory_.UpdateSharedImage(mailbox);
}
void DestroySharedImage(const gpu::SyncToken& sync_token,
const gpu::Mailbox& mailbox) override {
DCHECK(!ShouldWait(sync_token))
<< "Cannot DestroySharedImage with SyncToken from different "
"command buffer.";
shared_image_factory_.DestroySharedImage(mailbox);
}
#if defined(OS_WIN)
SwapChainMailboxes CreateSwapChain(ResourceFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
uint32_t usage) override {
NOTREACHED();
return {};
}
void PresentSwapChain(const gpu::SyncToken& sync_token,
const gpu::Mailbox& mailbox) override {
NOTREACHED();
}
#endif // OS_WIN
gpu::SyncToken GenUnverifiedSyncToken() override {
return gpu::SyncToken(sync_point_client_state_->namespace_id(),
sync_point_client_state_->command_buffer_id(),
GenerateFenceSyncRelease());
}
gpu::SyncToken GenVerifiedSyncToken() override {
gpu::SyncToken sync_token = GenUnverifiedSyncToken();
sync_token.SetVerifyFlush();
return sync_token;
}
// DirectContextProviderDelegate implementation.
gpu::SharedImageManager* GetSharedImageManager() override {
return shared_image_manager_;
}
gpu::SharedImageInterface* GetSharedImageInterface() override { return this; }
gpu::CommandBufferNamespace GetNamespaceID() const override {
return sync_point_client_state_->namespace_id();
}
gpu::CommandBufferId GetCommandBufferID() const override {
return sync_point_client_state_->command_buffer_id();
}
uint64_t GenerateFenceSyncRelease() override {
uint64_t release = ++sync_fence_release_;
// Release fence immediately because the relevant GPU calls were already
// issued.
sync_point_client_state_->ReleaseFenceSync(release);
return release;
}
void SignalSyncToken(const gpu::SyncToken& sync_token,
base::OnceClosure callback) override {
base::RepeatingClosure maybe_pass_callback =
base::AdaptCallbackForRepeating(std::move(callback));
if (!sync_point_client_state_->Wait(sync_token, maybe_pass_callback)) {
maybe_pass_callback.Run();
}
}
private:
bool ShouldWait(const gpu::SyncToken& sync_token) {
// Don't wait on an invalid SyncToken.
if (!sync_token.HasData())
return false;
// Don't wait on SyncTokens our own sync tokens because we've already issued
// the relevant calls to the GPU.
return sync_point_client_state_->namespace_id() !=
sync_token.namespace_id() ||
sync_point_client_state_->command_buffer_id() !=
sync_token.command_buffer_id();
}
gpu::SharedImageManager* const shared_image_manager_;
gpu::SharedImageFactory shared_image_factory_;
scoped_refptr<gpu::SyncPointClientState> sync_point_client_state_;
uint64_t sync_fence_release_ = 0;
DISALLOW_COPY_AND_ASSIGN(DirectContextProviderDelegateImpl);
};
} // namespace
SkiaOutputSurfaceImplOnGpu::OffscreenSurface::OffscreenSurface() = default;
SkiaOutputSurfaceImplOnGpu::OffscreenSurface::~OffscreenSurface() = default;
SkiaOutputSurfaceImplOnGpu::OffscreenSurface::OffscreenSurface(
OffscreenSurface&& offscreen_surface) = default;
SkiaOutputSurfaceImplOnGpu::OffscreenSurface&
SkiaOutputSurfaceImplOnGpu::OffscreenSurface::operator=(
OffscreenSurface&& offscreen_surface) = default;
SkSurface* SkiaOutputSurfaceImplOnGpu::OffscreenSurface::surface() const {
return surface_.get();
}
sk_sp<SkPromiseImageTexture>
SkiaOutputSurfaceImplOnGpu::OffscreenSurface::fulfill() {
DCHECK(surface_);
if (!promise_texture_) {
promise_texture_ = SkPromiseImageTexture::Make(
surface_->getBackendTexture(SkSurface::kFlushRead_BackendHandleAccess));
}
return promise_texture_;
}
void SkiaOutputSurfaceImplOnGpu::OffscreenSurface::set_surface(
sk_sp<SkSurface> surface) {
surface_ = std::move(surface);
promise_texture_ = {};
}
SkiaOutputSurfaceImplOnGpu::SkiaOutputSurfaceImplOnGpu(
SkiaOutputSurfaceDependency* deps,
const RendererSettings& renderer_settings,
const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback,
const BufferPresentedCallback& buffer_presented_callback,
const ContextLostCallback& context_lost_callback)
: dependency_(std::move(deps)),
feature_info_(CreateFeatureInfo(dependency_)),
sync_point_client_state_(CreateSyncPointClientState(dependency_)),
shared_image_representation_factory_(
CreateSharedImageRepresentationFactory(dependency_)),
vulkan_context_provider_(dependency_->GetVulkanContextProvider()),
renderer_settings_(renderer_settings),
did_swap_buffer_complete_callback_(did_swap_buffer_complete_callback),
buffer_presented_callback_(buffer_presented_callback),
context_lost_callback_(context_lost_callback),
weak_ptr_factory_(this) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
#if defined(USE_OZONE)
window_surface_ =
ui::OzonePlatform::GetInstance()
->GetSurfaceFactoryOzone()
->CreatePlatformWindowSurface(dependency_->GetSurfaceHandle());
#endif
gpu_preferences_ = dependency_->GetGpuPreferences();
if (is_using_vulkan())
InitializeForVulkan();
else
InitializeForGL();
max_resource_cache_bytes_ = context_state_->max_resource_cache_bytes();
}
SkiaOutputSurfaceImplOnGpu::~SkiaOutputSurfaceImplOnGpu() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// |context_provider_| and clients want either the context to be lost or made
// current on destruction.
if (MakeCurrent(false /* need_fbo0 */)) {
// This ensures any outstanding callbacks for promise images are performed.
gr_context()->flush();
}
copier_ = nullptr;
texture_deleter_ = nullptr;
context_provider_ = nullptr;
sync_point_client_state_->Destroy();
}
void SkiaOutputSurfaceImplOnGpu::Reshape(
const gfx::Size& size,
float device_scale_factor,
const gfx::ColorSpace& color_space,
bool has_alpha,
bool use_stencil,
SkSurfaceCharacterization* characterization,
base::WaitableEvent* event) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(gr_context());
base::ScopedClosureRunner scoped_runner;
if (event) {
scoped_runner.ReplaceClosure(
base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
}
if (!MakeCurrent(!dependency_->IsOffscreen() /* need_fbo0 */))
return;
size_ = size;
color_space_ = color_space;
output_device_->Reshape(size_, device_scale_factor, color_space, has_alpha);
if (characterization) {
output_sk_surface()->characterize(characterization);
DCHECK(characterization->isValid());
}
}
void SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame(
std::unique_ptr<SkDeferredDisplayList> ddl,
std::unique_ptr<SkDeferredDisplayList> overdraw_ddl,
std::vector<ImageContext*> image_contexts,
std::vector<gpu::SyncToken> sync_tokens,
uint64_t sync_fence_release,
base::OnceClosure on_finished) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(ddl);
DCHECK(output_sk_surface());
if (!MakeCurrent(true /* need_fbo0 */))
return;
dependency_->ScheduleGrContextCleanup();
PullTextureUpdates(std::move(sync_tokens));
{
base::Optional<gpu::raster::GrShaderCache::ScopedCacheUse> cache_use;
if (dependency_->GetGrShaderCache()) {
cache_use.emplace(dependency_->GetGrShaderCache(),
gpu::kInProcessCommandBufferClientId);
}
ScopedPromiseImageAccess scoped_promise_image_access(
this, std::move(image_contexts));
if (!scoped_promise_image_access.begin_semaphores().empty()) {
auto result = output_sk_surface()->wait(
scoped_promise_image_access.begin_semaphores().size(),
scoped_promise_image_access.begin_semaphores().data());
DCHECK(result);
}
output_sk_surface()->draw(ddl.get());
ddl = nullptr;
if (overdraw_ddl) {
sk_sp<SkSurface> overdraw_surface = SkSurface::MakeRenderTarget(
gr_context(), overdraw_ddl->characterization(), SkBudgeted::kNo);
overdraw_surface->draw(overdraw_ddl.get());
overdraw_ddl = nullptr;
SkPaint paint;
sk_sp<SkImage> overdraw_image = overdraw_surface->makeImageSnapshot();
sk_sp<SkColorFilter> colorFilter = SkiaHelper::MakeOverdrawColorFilter();
paint.setColorFilter(colorFilter);
// TODO(xing.xu): move below to the thread where skia record happens.
output_sk_surface()->getCanvas()->drawImage(overdraw_image.get(), 0, 0,
&paint);
}
if (output_device_->need_swap_semaphore())
scoped_promise_image_access.end_semaphores().emplace_back();
GrFlushInfo flush_info = {
.fFlags = kNone_GrFlushFlags,
.fNumSemaphores = scoped_promise_image_access.end_semaphores().size(),
.fSignalSemaphores =
scoped_promise_image_access.end_semaphores().data(),
};
gpu::AddVulkanCleanupTaskForSkiaFlush(vulkan_context_provider_,
&flush_info);
if (on_finished)
gpu::AddCleanupTaskForSkiaFlush(std::move(on_finished), &flush_info);
auto result = output_sk_surface()->flush(
SkSurface::BackendSurfaceAccess::kPresent, flush_info);
if (result != GrSemaphoresSubmitted::kYes &&
!(scoped_promise_image_access.begin_semaphores().empty() &&
scoped_promise_image_access.end_semaphores().empty())) {
// TODO(penghuang): handle vulkan device lost.
DLOG(ERROR) << "output_sk_surface()->flush() failed.";
return;
}
if (output_device_->need_swap_semaphore()) {
DCHECK(!swap_buffers_semaphore_.isInitialized());
swap_buffers_semaphore_ =
scoped_promise_image_access.end_semaphores().back();
DCHECK(swap_buffers_semaphore_.isInitialized());
}
}
ReleaseFenceSyncAndPushTextureUpdates(sync_fence_release);
}
void SkiaOutputSurfaceImplOnGpu::SwapBuffers(OutputSurfaceFrame frame) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(output_sk_surface());
if (!MakeCurrent(!dependency_->IsOffscreen() /* need_fbo0 */))
return;
DCHECK(output_device_);
gfx::SwapResponse response;
if (frame.sub_buffer_rect && frame.sub_buffer_rect->IsEmpty()) {
// TODO(https://crbug.com/898680): Maybe do something for overlays here.
// This codepath was added in https://codereview.chromium.org/1489153002
// to support updating overlays without changing the framebuffer contents.
} else if (capabilities().supports_post_sub_buffer && frame.sub_buffer_rect) {
if (!capabilities().flipped_output_surface)
frame.sub_buffer_rect->set_y(size_.height() - frame.sub_buffer_rect->y() -
frame.sub_buffer_rect->height());
response = output_device_->PostSubBuffer(*frame.sub_buffer_rect,
swap_buffers_semaphore_,
buffer_presented_callback_);
} else {
response = output_device_->SwapBuffers(swap_buffers_semaphore_,
buffer_presented_callback_);
}
swap_buffers_semaphore_ = GrBackendSemaphore();
for (auto& latency : frame.latency_info) {
latency.AddLatencyNumberWithTimestamp(
ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, response.timings.swap_start,
1);
latency.AddLatencyNumberWithTimestamp(
ui::INPUT_EVENT_LATENCY_FRAME_SWAP_COMPONENT, response.timings.swap_end,
1);
}
latency_tracker_.OnGpuSwapBuffersCompleted(frame.latency_info);
}
void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass(
RenderPassId id,
std::unique_ptr<SkDeferredDisplayList> ddl,
std::vector<ImageContext*> image_contexts,
std::vector<gpu::SyncToken> sync_tokens,
uint64_t sync_fence_release) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(ddl);
if (!MakeCurrent(true /* need_fbo0 */))
return;
PullTextureUpdates(std::move(sync_tokens));
auto& offscreen = offscreen_surfaces_[id];
if (!offscreen.surface()) {
offscreen.set_surface(SkSurface::MakeRenderTarget(
gr_context(), ddl->characterization(), SkBudgeted::kNo));
DCHECK(offscreen.surface());
} else {
#if DCHECK_IS_ON()
SkSurfaceCharacterization characterization;
DCHECK(offscreen.surface()->characterize(&characterization) &&
characterization == ddl->characterization());
#endif
}
{
base::Optional<gpu::raster::GrShaderCache::ScopedCacheUse> cache_use;
if (dependency_->GetGrShaderCache()) {
cache_use.emplace(dependency_->GetGrShaderCache(),
gpu::kInProcessCommandBufferClientId);
}
ScopedPromiseImageAccess scoped_promise_image_access(
this, std::move(image_contexts));
if (!scoped_promise_image_access.begin_semaphores().empty()) {
auto result = offscreen.surface()->wait(
scoped_promise_image_access.begin_semaphores().size(),
scoped_promise_image_access.begin_semaphores().data());
DCHECK(result);
}
offscreen.surface()->draw(ddl.get());
GrFlushInfo flush_info = {
.fFlags = kNone_GrFlushFlags,
.fNumSemaphores = scoped_promise_image_access.end_semaphores().size(),
.fSignalSemaphores =
scoped_promise_image_access.end_semaphores().data(),
};
gpu::AddVulkanCleanupTaskForSkiaFlush(vulkan_context_provider_,
&flush_info);
auto result = offscreen.surface()->flush(
SkSurface::BackendSurfaceAccess::kNoAccess, flush_info);
if (result != GrSemaphoresSubmitted::kYes &&
!(scoped_promise_image_access.begin_semaphores().empty() &&
scoped_promise_image_access.end_semaphores().empty())) {
// TODO(penghuang): handle vulkan device lost.
DLOG(ERROR) << "offscreen.surface()->flush() failed.";
return;
}
}
ReleaseFenceSyncAndPushTextureUpdates(sync_fence_release);
}
void SkiaOutputSurfaceImplOnGpu::RemoveRenderPassResource(
std::vector<std::unique_ptr<ImageContext>> image_contexts) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!image_contexts.empty());
for (auto& image_context : image_contexts) {
auto it = offscreen_surfaces_.find(image_context->render_pass_id);
DCHECK(it != offscreen_surfaces_.end());
offscreen_surfaces_.erase(it);
}
}
void SkiaOutputSurfaceImplOnGpu::CopyOutput(
RenderPassId id,
const copy_output::RenderPassGeometry& geometry,
const gfx::ColorSpace& color_space,
std::unique_ptr<CopyOutputRequest> request) {
// TODO(crbug.com/898595): Do this on the GPU instead of CPU with Vulkan.
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
bool from_fbo0 = !id;
if (!MakeCurrent(true /* need_fbo0 */))
return;
DCHECK(from_fbo0 ||
offscreen_surfaces_.find(id) != offscreen_surfaces_.end());
auto* surface =
from_fbo0 ? output_sk_surface() : offscreen_surfaces_[id].surface();
if (!is_using_vulkan() && !features::IsUsingSkiaForGLReadback()) {
// Lazy initialize GLRendererCopier.
if (!copier_) {
auto client = std::make_unique<DirectContextProviderDelegateImpl>(
gpu_preferences_, dependency_->GetGpuDriverBugWorkarounds(),
dependency_->GetGpuFeatureInfo(), context_state_.get(),
dependency_->GetMailboxManager(),
dependency_->GetSharedImageManager(),
CreateSyncPointClientState(dependency_));
context_provider_ = base::MakeRefCounted<DirectContextProvider>(
context_state_->context(), gl_surface_, supports_alpha_,
gpu_preferences_, feature_info_.get(), std::move(client));
auto result = context_provider_->BindToCurrentThread();
if (result != gpu::ContextResult::kSuccess) {
DLOG(ERROR) << "Couldn't initialize GLRendererCopier";
context_provider_ = nullptr;
return;
}
context_current_task_runner_ =
base::MakeRefCounted<ContextCurrentTaskRunner>(this);
texture_deleter_ =
std::make_unique<TextureDeleter>(context_current_task_runner_);
copier_ = std::make_unique<GLRendererCopier>(context_provider_,
texture_deleter_.get());
copier_->set_async_gl_task_runner(context_current_task_runner_);
}
surface->flush();
GLuint gl_id = 0;
GLenum internal_format = supports_alpha_ ? GL_RGBA : GL_RGB;
bool flipped = from_fbo0 ? !capabilities().flipped_output_surface : false;
base::Optional<ScopedSurfaceToTexture> texture_mapper;
if (!from_fbo0 || dependency_->IsOffscreen()) {
texture_mapper.emplace(context_provider_.get(), surface);
gl_id = texture_mapper.value().client_id();
internal_format = GL_RGBA;
}
gfx::Size surface_size(surface->width(), surface->height());
ScopedUseContextProvider use_context_provider(this, gl_id);
copier_->CopyFromTextureOrFramebuffer(std::move(request), geometry,
internal_format, gl_id, surface_size,
flipped, color_space);
if (decoder()->HasMoreIdleWork() || decoder()->HasPendingQueries())
ScheduleDelayedWork();
return;
}
if (request->result_format() ==
CopyOutputRequest::ResultFormat::I420_PLANES) {
base::Optional<gpu::raster::GrShaderCache::ScopedCacheUse> cache_use;
if (dependency_->GetGrShaderCache()) {
cache_use.emplace(dependency_->GetGrShaderCache(),
gpu::kInProcessCommandBufferClientId);
}
// For downscaling, use the GOOD quality setting (appropriate for
// thumbnailing); and, for upscaling, use the BEST quality.
bool is_downscale_in_both_dimensions =
request->scale_to().x() < request->scale_from().x() &&
request->scale_to().y() < request->scale_from().y();
SkFilterQuality filter_quality = is_downscale_in_both_dimensions
? kMedium_SkFilterQuality
: kHigh_SkFilterQuality;
SkIRect srcRect = SkIRect::MakeXYWH(
geometry.sampling_bounds.x(), geometry.sampling_bounds.y(),
geometry.sampling_bounds.width(), geometry.sampling_bounds.height());
std::unique_ptr<ReadPixelsContext> context =
std::make_unique<ReadPixelsContext>(std::move(request),
geometry.result_bounds);
surface->asyncRescaleAndReadPixelsYUV420(
kRec709_SkYUVColorSpace, SkColorSpace::MakeSRGB(), srcRect,
geometry.result_bounds.width(), geometry.result_bounds.height(),
SkSurface::RescaleGamma::kSrc, filter_quality, OnYUVReadbackDone,
context.release());
return;
}
SkBitmap bitmap;
if (request->is_scaled()) {
SkImageInfo sampling_bounds_info = SkImageInfo::Make(
geometry.sampling_bounds.width(), geometry.sampling_bounds.height(),
SkColorType::kN32_SkColorType, SkAlphaType::kPremul_SkAlphaType,
surface->getCanvas()->imageInfo().refColorSpace());
bitmap.allocPixels(sampling_bounds_info);
surface->readPixels(bitmap, geometry.sampling_bounds.x(),
geometry.sampling_bounds.y());
// Execute the scaling: For downscaling, use the RESIZE_BETTER strategy
// (appropriate for thumbnailing); and, for upscaling, use the RESIZE_BEST
// strategy. Note that processing is only done on the subset of the
// RenderPass output that contributes to the result.
using skia::ImageOperations;
const bool is_downscale_in_both_dimensions =
request->scale_to().x() < request->scale_from().x() &&
request->scale_to().y() < request->scale_from().y();
const ImageOperations::ResizeMethod method =
is_downscale_in_both_dimensions ? ImageOperations::RESIZE_BETTER
: ImageOperations::RESIZE_BEST;
bitmap = ImageOperations::Resize(
bitmap, method, geometry.result_bounds.width(),
geometry.result_bounds.height(),
SkIRect{geometry.result_selection.x(), geometry.result_selection.y(),
geometry.result_selection.right(),
geometry.result_selection.bottom()});
} else {
SkImageInfo sampling_bounds_info = SkImageInfo::Make(
geometry.result_selection.width(), geometry.result_selection.height(),
SkColorType::kN32_SkColorType, SkAlphaType::kPremul_SkAlphaType,
surface->getCanvas()->imageInfo().refColorSpace());
bitmap.allocPixels(sampling_bounds_info);
surface->readPixels(bitmap, geometry.readback_offset.x(),
geometry.readback_offset.y());
}
// TODO(crbug.com/795132): Plumb color space throughout SkiaRenderer up to the
// the SkSurface/SkImage here. Until then, play "musical chairs" with the
// SkPixelRef to hack-in the RenderPass's |color_space|.
sk_sp<SkPixelRef> pixels(SkSafeRef(bitmap.pixelRef()));
SkIPoint origin = bitmap.pixelRefOrigin();
bitmap.setInfo(bitmap.info().makeColorSpace(color_space.ToSkColorSpace()),
bitmap.rowBytes());
bitmap.setPixelRef(std::move(pixels), origin.x(), origin.y());
// Deliver the result. SkiaRenderer supports RGBA_BITMAP and I420_PLANES
// only. For legacy reasons, if a RGBA_TEXTURE request is being made, clients
// are prepared to accept RGBA_BITMAP results.
//
// TODO(crbug/754872): Get rid of the legacy behavior and send empty results
// for RGBA_TEXTURE requests once tab capture is moved into VIZ.
const CopyOutputResult::Format result_format =
(request->result_format() == CopyOutputResult::Format::RGBA_TEXTURE)
? CopyOutputResult::Format::RGBA_BITMAP
: request->result_format();
// Note: The CopyOutputSkBitmapResult automatically provides I420 format
// conversion, if needed.
request->SendResult(std::make_unique<CopyOutputSkBitmapResult>(
result_format, geometry.result_selection, bitmap));
}
gpu::DecoderContext* SkiaOutputSurfaceImplOnGpu::decoder() {
return context_provider_->decoder();
}
void SkiaOutputSurfaceImplOnGpu::ScheduleDelayedWork() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (delayed_work_pending_)
return;
delayed_work_pending_ = true;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&SkiaOutputSurfaceImplOnGpu::PerformDelayedWork,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(2));
}
void SkiaOutputSurfaceImplOnGpu::PerformDelayedWork() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
ScopedUseContextProvider use_context_provider(this, /*texture_client_id=*/0);
delayed_work_pending_ = false;
if (MakeCurrent(true /* need_fbo0 */)) {
decoder()->PerformIdleWork();
decoder()->ProcessPendingQueries(false);
if (decoder()->HasMoreIdleWork() || decoder()->HasPendingQueries()) {
ScheduleDelayedWork();
}
}
}
// TODO(backer): Add memory tracking.
void SkiaOutputSurfaceImplOnGpu::CreateFallbackImage(ImageContext* context) {
SkColorType color_type = ResourceFormatToClosestSkColorType(
true /* gpu_compositing */, context->resource_format);
// Don't use a fallback SkColorType because we may fail checks inside Skia
if (SkColorTypeBytesPerPixel(color_type) * 8 !=
BitsPerPixel(context->resource_format))
return;
context->fallback_texture =
context_state_->gr_context()->createBackendTexture(
context->size.width(), context->size.height(), color_type,
#if DCHECK_IS_ON()
SkColors::kRed,
#else
SkColors::kWhite,
#endif
GrMipMapped::kNo, GrRenderable::kYes);
if (!context->fallback_texture.isValid()) {
DLOG(ERROR) << "Could not create backend texture.";
return;
}
context->promise_image_texture =
SkPromiseImageTexture::Make(context->fallback_texture);
}
void SkiaOutputSurfaceImplOnGpu::BeginAccessImages(
const std::vector<ImageContext*>& image_contexts,
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (auto* context : image_contexts) {
// Skip the context if it has been processed.
if (context->representation_is_being_accessed)
continue;
// Prepare for accessing render pass.
if (context->render_pass_id) {
// We don't cache promise image for render pass, so the it should always
// be nullptr.
auto it = offscreen_surfaces_.find(context->render_pass_id);
DCHECK(it != offscreen_surfaces_.end());
context->promise_image_texture = it->second.fulfill();
if (!context->promise_image_texture) {
DLOG(ERROR) << "Failed to fulfill the promise texture created from "
"RenderPassId:"
<< context->render_pass_id;
CreateFallbackImage(context);
}
continue;
}
// Prepare for accessing shared image.
if (context->mailbox.IsSharedImage()) {
if (context->promise_image_texture) {
// The promise image has been fulfilled early, so we just need begin
// access the shared image representation. If representation is nullptr,
// it means the promise image was not fulfilled successfully last time
// and a fallback content has been used, in that case, we cannot change
// the content, so we do nothing.
if (context->representation) {
auto promise_image_texture = context->representation->BeginReadAccess(
begin_semaphores, end_semaphores);
// The image has been fulfilled and cached. It is too late to tell
// skia the backing of the cached image is not accessible right now,
// so crash for now.
// TODO(penghuang): find a way to notify skia.
CHECK(promise_image_texture);
context->representation_is_being_accessed = true;
}
continue;
}
auto representation = shared_image_representation_factory_->ProduceSkia(
context->mailbox, context_state_.get());
if (!representation) {
DLOG(ERROR) << "Failed to fulfill the promise texture - SharedImage "
"mailbox not found in SharedImageManager.";
CreateFallbackImage(context);
continue;
}
if (!(representation->usage() & gpu::SHARED_IMAGE_USAGE_DISPLAY)) {
DLOG(ERROR) << "Failed to fulfill the promise texture - SharedImage "
"was not created with display usage.";
CreateFallbackImage(context);
continue;
}
if (representation->size() != context->size) {
DLOG(ERROR) << "Failed to fulfill the promise texture - SharedImage "
"size does not match TransferableResource size.";
CreateFallbackImage(context);
continue;
}
context->representation = std::move(representation);
context->promise_image_texture = context->representation->BeginReadAccess(
begin_semaphores, end_semaphores);
if (!context->promise_image_texture) {
DLOG(ERROR) << "Failed to fulfill the promise texture - SharedImage "
"begin read access failed..";
CreateFallbackImage(context);
continue;
}
context->representation_is_being_accessed = true;
continue;
}
// Prepare for accessing legacy mailbox.
// The promise image has been fulfilled once, so we do need do anything.
if (context->promise_image_texture)
continue;
if (is_using_vulkan()) {
// Probably this texture is created with wrong interface
// (GLES2Interface).
DLOG(ERROR)
<< "Failed to fulfill the promise texture whose backend is not "
"compatible with vulkan.";
CreateFallbackImage(context);
continue;
}
auto* texture_base =
dependency_->GetMailboxManager()->ConsumeTexture(context->mailbox);
if (!texture_base) {
DLOG(ERROR) << "Failed to fulfill the promise texture.";
CreateFallbackImage(context);
continue;
}
gfx::Size texture_size;
if (BindOrCopyTextureIfNecessary(texture_base, &texture_size) &&
texture_size != context->size) {
DLOG(ERROR) << "Failed to fulfill the promise texture - texture "
"size does not match TransferableResource size.";
CreateFallbackImage(context);
continue;
}
GrBackendTexture backend_texture;
gpu::GetGrBackendTexture(gl_version_info_, texture_base->target(),
context->size, texture_base->service_id(),
context->resource_format, &backend_texture);
if (!backend_texture.isValid()) {
DLOG(ERROR) << "Failed to fulfill the promise texture.";
CreateFallbackImage(context);
continue;
}
context->promise_image_texture =
SkPromiseImageTexture::Make(backend_texture);
}
}
void SkiaOutputSurfaceImplOnGpu::EndAccessImages(
const std::vector<ImageContext*>& image_contexts) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (auto* context : image_contexts) {
if (!context->representation_is_being_accessed)
continue;
// TODO(penghuang): create end read access semaphores and call
// EndReadAccess() with them. https://crbug.com/944194
DCHECK(context->representation);
context->representation->EndReadAccess();
context->representation_is_being_accessed = false;
}
}
void SkiaOutputSurfaceImplOnGpu::SetDrawRectangle(
const gfx::Rect& draw_rectangle) {
output_device_->SetDrawRectangle(draw_rectangle);
}
sk_sp<GrContextThreadSafeProxy>
SkiaOutputSurfaceImplOnGpu::GetGrContextThreadSafeProxy() {
return gr_context()->threadSafeProxy();
}
void SkiaOutputSurfaceImplOnGpu::ReleaseImageContexts(
std::vector<std::unique_ptr<ImageContext>> image_contexts) {
DCHECK(!image_contexts.empty());
// The window could be destroyed already, and the MakeCurrent will fail with
// an destroyed window, so MakeCurrent without requiring the fbo0.
MakeCurrent(false /* need_fbo0 */);
for (const auto& image_context : image_contexts) {
if (image_context->fallback_texture.isValid())
gpu::DeleteGrBackendTexture(context_state_.get(),
&image_context->fallback_texture);
}
}
void SkiaOutputSurfaceImplOnGpu::SetCapabilitiesForTesting(
const OutputSurface::Capabilities& capabilities) {
MakeCurrent(false /* need_fbo0 */);
// Check that we're using an offscreen surface.
DCHECK(dependency_->IsOffscreen());
output_device_ = std::make_unique<SkiaOutputDeviceOffscreen>(
gr_context(), capabilities.flipped_output_surface,
renderer_settings_.requires_alpha_channel,
did_swap_buffer_complete_callback_);
}
void SkiaOutputSurfaceImplOnGpu::InitializeForGL() {
std::unique_ptr<SkiaOutputDeviceGL> onscreen_device;
if (!dependency_->IsOffscreen()) {
onscreen_device = std::make_unique<SkiaOutputDeviceGL>(
dependency_, feature_info_, did_swap_buffer_complete_callback_);
gl_surface_ = onscreen_device->gl_surface();
} else {
gl_surface_ = dependency_->CreateGLSurface(nullptr);
}
DCHECK(gl_surface_);
DCHECK_EQ(gl_surface_->IsOffscreen(), dependency_->IsOffscreen());
context_state_ = dependency_->GetSharedContextState();
if (!context_state_) {
LOG(FATAL) << "Failed to create GrContext";
// TODO(penghuang): handle the failure.
}
auto* context = context_state_->real_context();
auto* current_gl = context->GetCurrentGL();
api_ = current_gl->Api;
gl_version_info_ = context->GetVersionInfo();
if (onscreen_device) {
if (!MakeCurrent(true /* need_fbo0 */)) {
LOG(FATAL) << "Failed to make current during initialization.";
return;
}
onscreen_device->Initialize(gr_context(), context);
supports_alpha_ = onscreen_device->supports_alpha();
output_device_ = std::move(onscreen_device);
} else {
output_device_ = std::make_unique<SkiaOutputDeviceOffscreen>(
gr_context(), true /* flipped */,
renderer_settings_.requires_alpha_channel,
did_swap_buffer_complete_callback_);
supports_alpha_ = renderer_settings_.requires_alpha_channel;
}
}
void SkiaOutputSurfaceImplOnGpu::InitializeForVulkan() {
context_state_ = dependency_->GetSharedContextState();
DCHECK(context_state_);
#if BUILDFLAG(ENABLE_VULKAN)
if (dependency_->IsOffscreen()) {
output_device_ = std::make_unique<SkiaOutputDeviceOffscreen>(
gr_context(), false /* flipped */,
renderer_settings_.requires_alpha_channel,
did_swap_buffer_complete_callback_);
supports_alpha_ = renderer_settings_.requires_alpha_channel;
} else {
#if defined(USE_X11)
supports_alpha_ = true;
if (gpu_preferences_.disable_vulkan_surface) {
output_device_ = std::make_unique<SkiaOutputDeviceX11>(
gr_context(), dependency_->GetSurfaceHandle(),
did_swap_buffer_complete_callback_);
} else {
output_device_ = std::make_unique<SkiaOutputDeviceVulkan>(
vulkan_context_provider_, dependency_->GetSurfaceHandle(),
did_swap_buffer_complete_callback_);
}
#else
output_device_ = std::make_unique<SkiaOutputDeviceVulkan>(
vulkan_context_provider_, dependency_->GetSurfaceHandle(),
did_swap_buffer_complete_callback_);
#endif
}
#endif
}
bool SkiaOutputSurfaceImplOnGpu::BindOrCopyTextureIfNecessary(
gpu::TextureBase* texture_base,
gfx::Size* size) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (texture_base->GetType() != gpu::TextureBase::Type::kValidated)
return false;
// If a texture is validated and bound to an image, we may defer copying the
// image to the texture until the texture is used. It is for implementing low
// latency drawing (e.g. fast ink) and avoiding unnecessary texture copy. So
// we need check the texture image state, and bind or copy the image to the
// texture if necessary.
auto* texture = gpu::gles2::Texture::CheckedCast(texture_base);
gpu::gles2::Texture::ImageState image_state;
auto* image = texture->GetLevelImage(GL_TEXTURE_2D, 0, &image_state);
if (image && image_state == gpu::gles2::Texture::UNBOUND) {
glBindTexture(texture_base->target(), texture_base->service_id());
if (image->ShouldBindOrCopy() == gl::GLImage::BIND) {
if (!image->BindTexImage(texture_base->target())) {
LOG(ERROR) << "Failed to bind a gl image to texture.";
return false;
}
} else {
texture->SetLevelImageState(texture_base->target(), 0,
gpu::gles2::Texture::COPIED);
if (!image->CopyTexImage(texture_base->target())) {
LOG(ERROR) << "Failed to copy a gl image to texture.";
return false;
}
}
}
GLsizei temp_width, temp_height;
texture->GetLevelSize(texture_base->target(), 0 /* level */, &temp_width,
&temp_height, nullptr /* depth */);
*size = gfx::Size(temp_width, temp_height);
return true;
}
bool SkiaOutputSurfaceImplOnGpu::MakeCurrent(bool need_fbo0) {
if (!is_using_vulkan()) {
// Only make current with |gl_surface_|, if following operations will use
// fbo0.
if (!context_state_->MakeCurrent(need_fbo0 ? gl_surface_.get() : nullptr)) {
LOG(ERROR) << "Failed to make current.";
context_lost_callback_.Run();
if (context_provider_)
context_provider_->MarkContextLost();
return false;
}
context_state_->set_need_context_state_reset(true);
}
return true;
}
void SkiaOutputSurfaceImplOnGpu::PullTextureUpdates(
std::vector<gpu::SyncToken> sync_tokens) {
// TODO(https://crbug.com/900973): Remove it when MailboxManager is replaced
// with SharedImage API.
if (dependency_->GetMailboxManager()->UsesSync()) {
for (auto& sync_token : sync_tokens)
dependency_->GetMailboxManager()->PullTextureUpdates(sync_token);
}
}
void SkiaOutputSurfaceImplOnGpu::ReleaseFenceSyncAndPushTextureUpdates(
uint64_t sync_fence_release) {
// TODO(https://crbug.com/900973): Remove it when MailboxManager is replaced
// with SharedImage API.
if (dependency_->GetMailboxManager()->UsesSync()) {
// If MailboxManagerSync is used, we are sharing textures between threads.
// In this case, sync point can only guarantee GL commands are issued in
// correct order across threads and GL contexts. However GPU driver may
// execute GL commands out of the issuing order across GL contexts. So we
// have to use PushTextureUpdates() and PullTextureUpdates() to ensure the
// correct GL commands executing order.
// PushTextureUpdates(token) will insert a GL fence into the current GL
// context, PullTextureUpdates(token) will wait the GL fence associated with
// the give token on the current GL context.
// Reconstruct sync_token from sync_fence_release.
gpu::SyncToken sync_token(
gpu::CommandBufferNamespace::VIZ_SKIA_OUTPUT_SURFACE,
command_buffer_id(), sync_fence_release);
dependency_->GetMailboxManager()->PushTextureUpdates(sync_token);
}
sync_point_client_state_->ReleaseFenceSync(sync_fence_release);
}
} // namespace viz