* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/ganesh/GrSurfaceProxy.h"
#include "include/core/SkAlphaType.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkPoint.h"
#include "include/gpu/GpuTypes.h"
#include "include/gpu/ganesh/GrRecordingContext.h"
#ifdef SKIA_DFX_FOR_RECORD_VKIMAGE
#include "include/gpu/vk/GrVulkanTrackerInterface.h"
#endif
#include "src/gpu/SkBackingFit.h"
#include "src/gpu/Swizzle.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrGpuResourcePriv.h"
#include "src/gpu/ganesh/GrImageInfo.h"
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
#include "src/gpu/ganesh/GrRenderTargetProxy.h"
#include "src/gpu/ganesh/GrRenderTask.h"
#include "src/gpu/ganesh/GrResourceProvider.h"
#include "src/gpu/ganesh/GrSurface.h"
#include "src/gpu/ganesh/GrSurfaceProxyPriv.h"
#include "src/gpu/ganesh/GrSurfaceProxyView.h"
#include "src/gpu/ganesh/GrTexture.h"
#include "src/gpu/ganesh/GrTextureProxy.h"
#include "src/gpu/ganesh/SurfaceContext.h"
#include "src/gpu/ganesh/SurfaceFillContext.h"
#ifdef SKIA_OHOS
#include "src/gpu/ganesh/GrPerfMonitorReporter.h"
#endif
#include <memory>
#ifdef SK_DEBUG
#include "include/gpu/ganesh/GrDirectContext.h"
#include "src/gpu/ganesh/GrDirectContextPriv.h"
static bool is_valid_lazy(const SkISize& dimensions, SkBackingFit fit) {
return ((dimensions.fWidth < 0 && dimensions.fHeight < 0 && SkBackingFit::kApprox == fit) ||
(dimensions.fWidth > 0 && dimensions.fHeight > 0));
}
static bool is_valid_non_lazy(SkISize dimensions) {
return dimensions.fWidth > 0 && dimensions.fHeight > 0;
}
#endif
#ifdef SKIA_DFX_FOR_RECORD_VKIMAGE
#ifndef SK_VULKAN
namespace ParallelDebug {
bool IsVkImageDfxEnabled() { return false; }
void RecordNodeId(uint64_t nodeId) {}
uint64_t GetNodeId() { return 0; }
}
#endif
#endif
#ifdef SKIA_DFX_FOR_OHOS
#ifndef SK_VULKAN
namespace RealAllocConfig {
bool GetRealAllocStatus() { return false; }
void SetRealAllocStatus(bool ret) { static_cast<void>(ret); }
};
#endif
#endif
GrSurfaceProxy::LazyCallbackResult::LazyCallbackResult(sk_sp<GrSurface> surf,
bool releaseCallback,
LazyInstantiationKeyMode mode)
: fSurface(std::move(surf)), fKeyMode(mode), fReleaseCallback(releaseCallback) {}
GrSurfaceProxy::LazyCallbackResult::LazyCallbackResult(sk_sp<GrTexture> tex)
: LazyCallbackResult(sk_sp<GrSurface>(std::move(tex))) {}
GrSurfaceProxy::GrSurfaceProxy(const GrBackendFormat& format,
SkISize dimensions,
SkBackingFit fit,
skgpu::Budgeted budgeted,
GrProtected isProtected,
GrInternalSurfaceFlags surfaceFlags,
UseAllocator useAllocator,
std::string_view label)
: fSurfaceFlags(surfaceFlags)
, fFormat(format)
, fDimensions(dimensions)
, fFit(fit)
, fBudgeted(budgeted)
, fUseAllocator(useAllocator)
#ifdef SKIA_DFX_FOR_RECORD_VKIMAGE
, fNodeId(ParallelDebug::GetNodeId())
#endif
#ifdef SKIA_DFX_FOR_OHOS
, fRealAllocProxy(RealAllocConfig::GetRealAllocStatus())
#endif
, fIsProtected(isProtected)
, fLabel(label) {
SkASSERT(fFormat.isValid());
SkASSERT(is_valid_non_lazy(dimensions));
}
GrSurfaceProxy::GrSurfaceProxy(LazyInstantiateCallback&& callback,
const GrBackendFormat& format,
SkISize dimensions,
SkBackingFit fit,
skgpu::Budgeted budgeted,
GrProtected isProtected,
GrInternalSurfaceFlags surfaceFlags,
UseAllocator useAllocator,
std::string_view label)
: fSurfaceFlags(surfaceFlags)
, fFormat(format)
, fDimensions(dimensions)
, fFit(fit)
, fBudgeted(budgeted)
, fUseAllocator(useAllocator)
, fLazyInstantiateCallback(std::move(callback))
#ifdef SKIA_DFX_FOR_RECORD_VKIMAGE
, fNodeId(ParallelDebug::GetNodeId())
#endif
#ifdef SKIA_DFX_FOR_OHOS
, fRealAllocProxy(RealAllocConfig::GetRealAllocStatus())
#endif
, fIsProtected(isProtected)
, fLabel(label) {
SkASSERT(fFormat.isValid());
SkASSERT(fLazyInstantiateCallback);
SkASSERT(is_valid_lazy(dimensions, fit));
}
GrSurfaceProxy::GrSurfaceProxy(sk_sp<GrSurface> surface,
SkBackingFit fit,
UseAllocator useAllocator)
: fTarget(std::move(surface))
, fSurfaceFlags(fTarget->flags())
, fFormat(fTarget->backendFormat())
, fDimensions(fTarget->dimensions())
, fFit(fit)
, fBudgeted(fTarget->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted
? skgpu::Budgeted::kYes
: skgpu::Budgeted::kNo)
, fUseAllocator(useAllocator)
, fUniqueID(fTarget->uniqueID())
#ifdef SKIA_DFX_FOR_RECORD_VKIMAGE
, fNodeId(ParallelDebug::GetNodeId())
#endif
#ifdef SKIA_DFX_FOR_OHOS
, fRealAllocProxy(RealAllocConfig::GetRealAllocStatus())
#endif
, fIsProtected(fTarget->isProtected() ? GrProtected::kYes : GrProtected::kNo)
, fLabel(fTarget->getLabel()) {
SkASSERT(fFormat.isValid());
}
GrSurfaceProxy::~GrSurfaceProxy() {
}
#ifdef SKIA_DFX_FOR_RECORD_VKIMAGE
struct NodeIdHelper {
explicit inline NodeIdHelper(uint64_t nodeId) : initNodeId_(ParallelDebug::GetNodeId())
{
ParallelDebug::RecordNodeId(nodeId);
}
inline ~NodeIdHelper()
{
ParallelDebug::RecordNodeId(initNodeId_);
}
uint64_t initNodeId_;
};
#endif
sk_sp<GrSurface> GrSurfaceProxy::createSurfaceImpl(GrResourceProvider* resourceProvider,
int sampleCnt,
GrRenderable renderable,
skgpu::Mipmapped mipmapped) const {
#ifdef SKIA_DFX_FOR_RECORD_VKIMAGE
NodeIdHelper helper(fNodeId);
#endif
SkASSERT(mipmapped == skgpu::Mipmapped::kNo || fFit == SkBackingFit::kExact);
SkASSERT(!this->isLazy());
SkASSERT(!fTarget);
#ifdef SKIA_OHOS
int64_t currentTime = GrPerfMonitorReporter::getCurrentTime();
#endif
sk_sp<GrSurface> surface;
if (SkBackingFit::kApprox == fFit) {
surface = resourceProvider->createApproxTexture(fDimensions,
fFormat,
fFormat.textureType(),
renderable,
sampleCnt,
fIsProtected,
fLabel);
} else {
surface = resourceProvider->createTexture(fDimensions,
fFormat,
fFormat.textureType(),
renderable,
sampleCnt,
mipmapped,
fBudgeted,
fIsProtected,
fLabel);
}
if (!surface) {
return nullptr;
}
#ifdef SKIA_DFX_FOR_RECORD_VKIMAGE
if (ParallelDebug::IsVkImageDfxEnabled()) {
surface->updateNodeId(fNodeId);
}
#endif
if (fGrProxyTag.isGrTagValid()) {
#ifdef SKIA_DFX_FOR_OHOS
surface->setResourceTag(fGrProxyTag, fRealAllocProxy);
#else
surface->setResourceTag(fGrProxyTag);
#endif
#ifdef SKIA_OHOS
int64_t allocTime = GrPerfMonitorReporter::getCurrentTime() - currentTime;
GrPerfMonitorReporter::GetInstance().recordTextureNode(fGrProxyTag.fName, allocTime);
GrPerfMonitorReporter::GetInstance().recordTexturePerfEvent(fGrProxyTag.fName,
fGrProxyTag.fPid, static_cast<int32_t>(resourceProvider->getMaxResourceBytes()),
static_cast<int32_t>(resourceProvider->getBudgetedResourceBytes()), allocTime);
#endif
}
return surface;
}
bool GrSurfaceProxy::canSkipResourceAllocator() const {
if (fUseAllocator == UseAllocator::kNo) {
return true;
}
auto peek = this->peekSurface();
if (!peek) {
return false;
}
return !peek->resourcePriv().getScratchKey().isValid();
}
void GrSurfaceProxy::assign(sk_sp<GrSurface> surface) {
SkASSERT(!fTarget && surface);
SkDEBUGCODE(this->validateSurface(surface.get());)
fTarget = std::move(surface);
#ifdef SK_DEBUG
if (this->asRenderTargetProxy()) {
SkASSERT(fTarget->asRenderTarget());
}
if (!this->isDDLTarget() && kInvalidGpuMemorySize != this->getRawGpuMemorySize_debugOnly()) {
SkASSERT(fTarget->gpuMemorySize() <= this->getRawGpuMemorySize_debugOnly());
}
#endif
}
bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider,
int sampleCnt,
GrRenderable renderable,
skgpu::Mipmapped mipmapped,
const skgpu::UniqueKey* uniqueKey) {
SkASSERT(!this->isLazy());
if (fTarget) {
if (uniqueKey && uniqueKey->isValid()) {
SkASSERT(fTarget->getUniqueKey().isValid() && fTarget->getUniqueKey() == *uniqueKey);
}
return true;
}
sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, sampleCnt, renderable,
mipmapped);
if (!surface) {
return false;
}
if (uniqueKey && uniqueKey->isValid()) {
resourceProvider->assignUniqueKeyToResource(*uniqueKey, surface.get());
}
this->assign(std::move(surface));
return true;
}
void GrSurfaceProxy::deinstantiate() {
SkASSERT(this->isInstantiated());
fTarget = nullptr;
}
void GrSurfaceProxy::computeScratchKey(const GrCaps& caps, skgpu::ScratchKey* key) const {
SkASSERT(!this->isFullyLazy());
GrRenderable renderable = GrRenderable::kNo;
int sampleCount = 1;
if (const auto* rtp = this->asRenderTargetProxy()) {
renderable = GrRenderable::kYes;
sampleCount = rtp->numSamples();
}
const GrTextureProxy* tp = this->asTextureProxy();
skgpu::Mipmapped mipmapped = skgpu::Mipmapped::kNo;
if (tp) {
mipmapped = tp->mipmapped();
}
GrTexture::ComputeScratchKey(caps, this->backendFormat(), this->backingStoreDimensions(),
renderable, sampleCount, mipmapped, fIsProtected, key);
}
SkISize GrSurfaceProxy::backingStoreDimensions() const {
SkASSERT(!this->isFullyLazy());
if (fTarget) {
return fTarget->dimensions();
}
if (SkBackingFit::kExact == fFit) {
return fDimensions;
}
return skgpu::GetApproxSize(fDimensions);
}
bool GrSurfaceProxy::isFunctionallyExact() const {
SkASSERT(!this->isFullyLazy());
return fFit == SkBackingFit::kExact ||
fDimensions == skgpu::GetApproxSize(fDimensions);
}
bool GrSurfaceProxy::isFormatCompressed(const GrCaps* caps) const {
return caps->isFormatCompressed(this->backendFormat());
}
#ifdef SK_DEBUG
void GrSurfaceProxy::validate(GrContext_Base* context) const {
if (fTarget) {
SkASSERT(fTarget->getContext()->priv().matches(context));
}
}
#endif
sk_sp<GrSurfaceProxy> GrSurfaceProxy::Copy(GrRecordingContext* rContext,
sk_sp<GrSurfaceProxy> src,
GrSurfaceOrigin origin,
skgpu::Mipmapped mipmapped,
SkIRect srcRect,
SkBackingFit fit,
skgpu::Budgeted budgeted,
std::string_view label,
RectsMustMatch rectsMustMatch,
sk_sp<GrRenderTask>* outTask) {
SkASSERT(!src->isFullyLazy());
int width;
int height;
SkIPoint dstPoint;
if (rectsMustMatch == RectsMustMatch::kYes) {
width = src->width();
height = src->height();
dstPoint = {srcRect.fLeft, srcRect.fTop};
} else {
width = srcRect.width();
height = srcRect.height();
dstPoint = {0, 0};
}
if (!srcRect.intersect(SkIRect::MakeSize(src->dimensions()))) {
return {};
}
auto format = src->backendFormat().makeTexture2D();
SkASSERT(format.isValid());
if (src->backendFormat().textureType() != GrTextureType::kExternal) {
GrImageInfo info(GrColorType::kUnknown, kUnknown_SkAlphaType, nullptr, {width, height});
auto dstContext = rContext->priv().makeSC(info,
format,
label,
fit,
origin,
GrRenderable::kNo,
1,
mipmapped,
src->isProtected(),
budgeted);
sk_sp<GrRenderTask> copyTask;
if (dstContext && (copyTask = dstContext->copy(src, srcRect, dstPoint))) {
if (outTask) {
*outTask = std::move(copyTask);
}
return dstContext->asSurfaceProxyRef();
}
}
if (src->asTextureProxy()) {
auto dstContext = rContext->priv().makeSFC(kUnknown_SkAlphaType,
nullptr,
{width, height},
fit,
format,
1,
mipmapped,
src->isProtected(),
skgpu::Swizzle::RGBA(),
skgpu::Swizzle::RGBA(),
origin,
budgeted,
label);
GrSurfaceProxyView view(std::move(src), origin, skgpu::Swizzle::RGBA());
if (dstContext && dstContext->blitTexture(std::move(view), srcRect, dstPoint)) {
if (outTask) {
*outTask = dstContext->refRenderTask();
}
return dstContext->asSurfaceProxyRef();
}
}
return nullptr;
}
sk_sp<GrSurfaceProxy> GrSurfaceProxy::Copy(GrRecordingContext* context,
sk_sp<GrSurfaceProxy> src,
GrSurfaceOrigin origin,
skgpu::Mipmapped mipmapped,
SkBackingFit fit,
skgpu::Budgeted budgeted,
std::string_view label,
sk_sp<GrRenderTask>* outTask) {
SkASSERT(!src->isFullyLazy());
auto rect = SkIRect::MakeSize(src->dimensions());
return Copy(context,
std::move(src),
origin,
mipmapped,
rect,
fit,
budgeted,
label,
RectsMustMatch::kNo,
outTask);
}
#if defined(GPU_TEST_UTILS)
int32_t GrSurfaceProxy::testingOnly_getBackingRefCnt() const {
if (fTarget) {
return fTarget->testingOnly_getRefCnt();
}
return -1;
}
GrInternalSurfaceFlags GrSurfaceProxy::testingOnly_getFlags() const {
return fSurfaceFlags;
}
SkString GrSurfaceProxy::dump() const {
SkString tmp;
tmp.appendf("proxyID: %u - surfaceID: %u",
this->uniqueID().asUInt(),
this->peekSurface() ? this->peekSurface()->uniqueID().asUInt()
: -1);
return tmp;
}
#endif
void GrSurfaceProxyPriv::exactify() {
SkASSERT(!fProxy->isFullyLazy());
if (this->isExact()) {
return;
}
SkASSERT(SkBackingFit::kApprox == fProxy->fFit);
fProxy->fDimensions = fProxy->fTarget ? fProxy->fTarget->dimensions()
: fProxy->backingStoreDimensions();
fProxy->fFit = SkBackingFit::kExact;
}
bool GrSurfaceProxyPriv::doLazyInstantiation(GrResourceProvider* resourceProvider) {
SkASSERT(fProxy->isLazy());
sk_sp<GrSurface> surface;
if (const auto& uniqueKey = fProxy->getUniqueKey(); uniqueKey.isValid()) {
surface = resourceProvider->findByUniqueKey<GrSurface>(uniqueKey);
}
bool syncKey = true;
bool releaseCallback = false;
if (!surface) {
#ifdef SKIA_DFX_FOR_RECORD_VKIMAGE
NodeIdHelper helper(fNodeId);
#endif
auto result = fProxy->fLazyInstantiateCallback(resourceProvider, fProxy->callbackDesc());
surface = std::move(result.fSurface);
syncKey = result.fKeyMode == GrSurfaceProxy::LazyInstantiationKeyMode::kSynced;
releaseCallback = surface && result.fReleaseCallback;
}
if (!surface) {
fProxy->fDimensions.setEmpty();
return false;
}
#ifdef SKIA_DFX_FOR_RECORD_VKIMAGE
if (ParallelDebug::IsVkImageDfxEnabled()) {
surface->updateNodeId(fNodeId);
}
#endif
if (fProxy->isFullyLazy()) {
fProxy->fDimensions = surface->dimensions();
}
SkASSERT(fProxy->width() <= surface->width());
SkASSERT(fProxy->height() <= surface->height());
if (GrTextureProxy* texProxy = fProxy->asTextureProxy()) {
texProxy->setTargetKeySync(syncKey);
if (syncKey) {
const skgpu::UniqueKey& key = texProxy->getUniqueKey();
if (key.isValid()) {
if (!surface->asTexture()->getUniqueKey().isValid()) {
resourceProvider->assignUniqueKeyToResource(key, surface.get());
} else {
SkASSERT(surface->asTexture()->getUniqueKey() == key);
}
} else {
SkASSERT(!surface->getUniqueKey().isValid());
}
}
}
this->assign(std::move(surface));
if (releaseCallback) {
fProxy->fLazyInstantiateCallback = nullptr;
}
return true;
}
#ifdef SK_DEBUG
void GrSurfaceProxy::validateSurface(const GrSurface* surface) {
SkASSERTF(surface->backendFormat() == fFormat, "%s != %s",
surface->backendFormat().toStr().c_str(), fFormat.toStr().c_str());
this->onValidateSurface(surface);
}
#endif