[go: nahoru, domu]

blob: 70e048b0ca3bbd06f875ce228711c371c5e8e45c [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/command_buffer/service/shared_image/wrapped_sk_image_backing.h"
#include <utility>
#include "base/logging.h"
#include "base/task/bind_post_task.h"
#include "base/task/single_thread_task_runner.h"
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
#include "gpu/command_buffer/service/skia_utils.h"
#include "gpu/ipc/common/gpu_client_ids.h"
#include "third_party/skia/include/core/SkAlphaType.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkColorType.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkPixmap.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/core/SkSurfaceProps.h"
#include "third_party/skia/include/core/SkTextureCompressionType.h"
#include "third_party/skia/include/gpu/GpuTypes.h"
#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
#include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
namespace gpu {
namespace {
// Given a debug label string from the client, construct a string we can pass to
// debugging tools.
std::string GetLabel(const std::string& debug_label) {
return std::string("WrappedSkImage_" + debug_label);
}
} // namespace
class WrappedSkImageBacking::SkiaImageRepresentationImpl
: public SkiaGaneshImageRepresentation {
public:
SkiaImageRepresentationImpl(SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker,
scoped_refptr<SharedContextState> context_state)
: SkiaGaneshImageRepresentation(context_state->gr_context(),
manager,
backing,
tracker),
context_state_(std::move(context_state)) {}
~SkiaImageRepresentationImpl() override { DCHECK(write_surfaces_.empty()); }
std::vector<sk_sp<SkSurface>> BeginWriteAccess(
int final_msaa_count,
const SkSurfaceProps& surface_props,
const gfx::Rect& update_rect,
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores,
std::unique_ptr<skgpu::MutableTextureState>* end_state) override {
write_surfaces_ = wrapped_sk_image()->GetSkSurfaces(
final_msaa_count, surface_props, context_state_);
for (auto& surface : write_surfaces_) {
[[maybe_unused]] int save_count = surface->getCanvas()->save();
DCHECK_EQ(1, save_count);
}
return write_surfaces_;
}
std::vector<sk_sp<GrPromiseImageTexture>> BeginWriteAccess(
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores,
std::unique_ptr<skgpu::MutableTextureState>* end_state) override {
return wrapped_sk_image()->GetPromiseTextures();
}
void EndWriteAccess() override {
for (auto& write_surface : write_surfaces_) {
write_surface->getCanvas()->restoreToCount(1);
}
write_surfaces_.clear();
#if DCHECK_IS_ON()
for (auto& promise_texture : wrapped_sk_image()->GetPromiseTextures()) {
DCHECK(context_state_->CachedSkSurfaceIsUnique(promise_texture.get()));
}
#endif
}
std::vector<sk_sp<GrPromiseImageTexture>> BeginReadAccess(
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores,
std::unique_ptr<skgpu::MutableTextureState>* end_state) override {
DCHECK(write_surfaces_.empty());
return wrapped_sk_image()->GetPromiseTextures();
}
void EndReadAccess() override { DCHECK(write_surfaces_.empty()); }
bool SupportsMultipleConcurrentReadAccess() override { return true; }
private:
WrappedSkImageBacking* wrapped_sk_image() {
return static_cast<WrappedSkImageBacking*>(backing());
}
std::vector<sk_sp<SkSurface>> write_surfaces_;
scoped_refptr<SharedContextState> context_state_;
};
WrappedSkImageBacking::WrappedSkImageBacking(
base::PassKey<WrappedSkImageBackingFactory>,
const Mailbox& mailbox,
viz::SharedImageFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
uint32_t usage,
scoped_refptr<SharedContextState> context_state,
const bool thread_safe)
: ClearTrackingSharedImageBacking(mailbox,
format,
size,
color_space,
surface_origin,
alpha_type,
usage,
format.EstimatedSizeInBytes(size),
thread_safe),
context_state_(std::move(context_state)) {
DCHECK(!!context_state_);
// If the backing is meant to be thread safe, then grab the task runner to
// destroy the object later on same thread on which it was created on. Note
// that SkSurface and GrBackendTexture are not thread safe and hence should
// be destroyed on same thread on which it was created on.
if (is_thread_safe()) {
// If backing is thread safe, then ensure that we have a task runner to
// destroy backing on correct thread. Webview doesn't have a task runner
// but it uses and shares this backing on a single thread (on render
// passes for display compositor) and DrDc is disabled on webview. Hence
// using is_thread_safe() to grab task_runner is enough to ensure
// correctness.
DCHECK(base::SingleThreadTaskRunner::HasCurrentDefault());
task_runner_ = base::SingleThreadTaskRunner::GetCurrentDefault();
}
}
WrappedSkImageBacking::~WrappedSkImageBacking() {
auto destroy_resources = [](scoped_refptr<SharedContextState> context_state,
std::vector<TextureHolder> textures) {
context_state->MakeCurrent(nullptr);
for (auto& texture : textures) {
// Note that if we fail to initialize this backing, |promise_texture|
// will not be created and hence could be null while backing is
// destroyed after a failed init.
if (texture.promise_texture) {
context_state->EraseCachedSkSurface(texture.promise_texture.get());
texture.promise_texture.reset();
}
if (texture.backend_texture.isValid()) {
DeleteGrBackendTexture(context_state.get(), &texture.backend_texture);
}
}
if (!context_state->context_lost()) {
context_state->set_need_context_state_reset(true);
}
};
// Since the representation from this backing can be created on either gpu
// main or drdc thread, the last representation ref and hence the backing
// could be destroyed in any thread irrespective of the thread it was
// created on. Hence we need to ensure that the resources are destroyed on
// the thread they were created on.
if (task_runner_ && !task_runner_->BelongsToCurrentThread()) {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(destroy_resources, std::move(context_state_),
std::move(textures_)));
} else {
destroy_resources(std::move(context_state_), std::move(textures_));
}
}
bool WrappedSkImageBacking::Initialize(const std::string& debug_label) {
DCHECK(!format().IsCompressed());
// MakeCurrent to avoid destroying another client's state because Skia may
// change GL state to create and upload textures (crbug.com/1095679).
if (!context_state_->MakeCurrent(nullptr)) {
return false;
}
context_state_->set_need_context_state_reset(true);
auto mipmap = usage() & SHARED_IMAGE_USAGE_MIPMAP ? skgpu::Mipmapped::kYes
: skgpu::Mipmapped::kNo;
int num_planes = format().NumberOfPlanes();
textures_.resize(num_planes);
for (int plane = 0; plane < num_planes; ++plane) {
auto& texture = textures_[plane];
gfx::Size plane_size = format().GetPlaneSize(plane, size());
constexpr GrRenderable is_renderable = GrRenderable::kYes;
constexpr GrProtected is_protected = GrProtected::kNo;
#if DCHECK_IS_ON() && !BUILDFLAG(IS_LINUX)
// Blue for single-planar and magenta-ish for multi-planar.
SkColor4f fallback_color =
format().is_single_plane() ? SkColors::kBlue : SkColors::kWhite;
// Initializing to a color makes it obvious if the pixels are not properly
// set before they are displayed (e.g. https://crbug.com/956555).
// We don't do this on release builds because there is a slight overhead.
// Filling blue causes slight pixel difference, so linux-ref and
// linux-blink-ref bots cannot share the same baseline for webtest.
// So remove this color for this call for dcheck on build for now.
// TODO(crbug.com/1330278): add it back.
texture.backend_texture =
context_state_->gr_context()->createBackendTexture(
plane_size.width(), plane_size.height(), GetSkColorType(plane),
fallback_color, mipmap, is_renderable, is_protected, nullptr,
nullptr, GetLabel(debug_label));
#else
texture.backend_texture =
context_state_->gr_context()->createBackendTexture(
plane_size.width(), plane_size.height(), GetSkColorType(plane),
mipmap, is_renderable, is_protected, GetLabel(debug_label));
#endif
if (!texture.backend_texture.isValid()) {
DLOG(ERROR) << "createBackendTexture() failed with SkColorType:"
<< GetSkColorType(plane);
return false;
}
texture.promise_texture =
GrPromiseImageTexture::Make(texture.backend_texture);
}
return true;
}
bool WrappedSkImageBacking::InitializeWithData(
const std::string& debug_label,
base::span<const uint8_t> pixels) {
DCHECK(format().is_single_plane());
DCHECK(pixels.data());
// MakeCurrent to avoid destroying another client's state because Skia may
// change GL state to create and upload textures (crbug.com/1095679).
if (!context_state_->MakeCurrent(nullptr)) {
return false;
}
context_state_->set_need_context_state_reset(true);
textures_.resize(1);
{
absl::optional<gpu::raster::GrShaderCache::ScopedCacheUse> cache_use;
// ScopedCacheUse is used to avoid the empty/invalid client id DCHECKS
// caused while accessing GrShaderCache. Even though other clients can
// create shared images, the context used to create the backend texture
// here i.e. SharedContextState is the one used by display
// compositor/OOP-R, and therefore using kDisplayCompositorClientId is the
// right choice.
context_state_->UseShaderCache(cache_use, gpu::kDisplayCompositorClientId);
if (format().IsCompressed()) {
textures_[0].backend_texture =
context_state_->gr_context()->createCompressedBackendTexture(
size().width(), size().height(),
SkTextureCompressionType::kETC1_RGB8, pixels.data(),
pixels.size(), skgpu::Mipmapped::kNo, GrProtected::kNo);
} else {
auto info = AsSkImageInfo();
if (pixels.size() != info.computeMinByteSize()) {
DLOG(ERROR) << "Invalid initial pixel data size";
return false;
}
SkPixmap pixmap(info, pixels.data(), info.minRowBytes());
textures_[0].backend_texture =
context_state_->gr_context()->createBackendTexture(
pixmap, GrRenderable::kYes, GrProtected::kNo, nullptr, nullptr,
GetLabel(debug_label));
}
}
if (!textures_[0].backend_texture.isValid()) {
return false;
}
SetCleared();
textures_[0].promise_texture =
GrPromiseImageTexture::Make(textures_[0].backend_texture);
// Note that if the backing is meant to be thread safe (when DrDc and Vulkan
// is enabled), we need to do additional submit here in order to send the
// gpu commands in the correct order as per sync token dependencies. For eg
// tapping a tab tile creates a WrappedSkImageBacking mailbox with the the
// pixel data in LayerTreeHostImpl::CreateUIResource() which was showing
// corrupt data without this added synchronization.
if (is_thread_safe()) {
auto* gr_context = context_state_->gr_context();
// Note that all skia calls to GrBackendTexture does not require any
// flush() since the commands are already recorded by skia into the
// command buffer. Hence only calling submit here since pushing data to a
// texture will require sending commands to gpu.
gr_context->submit();
}
return true;
}
SharedImageBackingType WrappedSkImageBacking::GetType() const {
return SharedImageBackingType::kWrappedSkImage;
}
void WrappedSkImageBacking::Update(std::unique_ptr<gfx::GpuFence> in_fence) {
NOTREACHED();
}
bool WrappedSkImageBacking::UploadFromMemory(
const std::vector<SkPixmap>& pixmaps) {
DCHECK_EQ(pixmaps.size(), textures_.size());
if (context_state_->context_lost()) {
return false;
}
DCHECK(context_state_->IsCurrent(nullptr));
bool updated = true;
for (size_t i = 0; i < textures_.size(); ++i) {
updated = updated && context_state_->gr_context()->updateBackendTexture(
textures_[i].backend_texture, &pixmaps[i],
/*numLevels=*/1, nullptr, nullptr);
}
return updated;
}
std::vector<sk_sp<GrPromiseImageTexture>>
WrappedSkImageBacking::GetPromiseTextures() {
std::vector<sk_sp<GrPromiseImageTexture>> promise_textures;
promise_textures.reserve(textures_.size());
for (auto& texture : textures_) {
DCHECK(texture.promise_texture);
promise_textures.push_back(texture.promise_texture);
}
return promise_textures;
}
SkColorType WrappedSkImageBacking::GetSkColorType(int plane_index) {
return viz::ToClosestSkColorType(/*gpu_compositing=*/true, format(),
plane_index);
}
std::vector<sk_sp<SkSurface>> WrappedSkImageBacking::GetSkSurfaces(
int final_msaa_count,
const SkSurfaceProps& surface_props,
scoped_refptr<SharedContextState> context_state) {
// This method should only be called on the same thread on which this
// backing is created on. Hence adding a dcheck on context_state to ensure
// this.
DCHECK_EQ(context_state_, context_state);
if (context_state_->context_lost()) {
return {};
}
DCHECK(context_state_->IsCurrent(nullptr));
std::vector<sk_sp<SkSurface>> surfaces;
surfaces.reserve(textures_.size());
for (int plane = 0; plane < format().NumberOfPlanes(); ++plane) {
auto& texture = textures_[plane];
// Note that we are using |promise_texture| as a key to the cache below
// since it is safe to do so. |promise_texture| is not destroyed until we
// remove the entry from the cache.
DCHECK(texture.promise_texture);
auto surface =
context_state_->GetCachedSkSurface(texture.promise_texture.get());
if (!surface || final_msaa_count != surface_msaa_count_ ||
surface_props != surface->props()) {
surface = SkSurfaces::WrapBackendTexture(
context_state_->gr_context(), texture.backend_texture,
surface_origin(), final_msaa_count, GetSkColorType(plane),
color_space().ToSkColorSpace(), &surface_props);
if (!surface) {
LOG(ERROR) << "MakeFromBackendTexture() failed.";
context_state_->EraseCachedSkSurface(texture.promise_texture.get());
return {};
}
context_state_->CacheSkSurface(texture.promise_texture.get(), surface);
}
surfaces.push_back(std::move(surface));
}
surface_msaa_count_ = final_msaa_count;
return surfaces;
}
std::unique_ptr<SkiaGaneshImageRepresentation>
WrappedSkImageBacking::ProduceSkiaGanesh(
SharedImageManager* manager,
MemoryTypeTracker* tracker,
scoped_refptr<SharedContextState> context_state) {
if (context_state_->context_lost()) {
return nullptr;
}
return std::make_unique<SkiaImageRepresentationImpl>(
manager, this, tracker, std::move(context_state));
}
WrappedSkImageBacking::TextureHolder::TextureHolder() = default;
WrappedSkImageBacking::TextureHolder::TextureHolder(TextureHolder&& other) =
default;
WrappedSkImageBacking::TextureHolder&
WrappedSkImageBacking::TextureHolder::operator=(TextureHolder&& other) =
default;
WrappedSkImageBacking::TextureHolder::~TextureHolder() = default;
} // namespace gpu