| /* |
| * Copyright 2023 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/private/chromium/GrDeferredDisplayListRecorder.h" |
| |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkSurface.h" |
| #include "include/gpu/GpuTypes.h" |
| #include "include/gpu/GrRecordingContext.h" |
| #include "include/gpu/GrTypes.h" |
| #include "include/private/base/SkTo.h" |
| #include "include/private/chromium/GrDeferredDisplayList.h" |
| #include "include/private/chromium/GrSurfaceCharacterization.h" |
| #include "include/private/gpu/ganesh/GrTypesPriv.h" |
| #include "src/gpu/SkBackingFit.h" |
| #include "src/gpu/ganesh/Device.h" |
| #include "src/gpu/ganesh/GrCaps.h" |
| #include "src/gpu/ganesh/GrProxyProvider.h" |
| #include "src/gpu/ganesh/GrRecordingContextPriv.h" |
| #include "src/gpu/ganesh/GrRenderTargetProxy.h" |
| #include "src/gpu/ganesh/GrSurface.h" |
| #include "src/gpu/ganesh/GrSurfaceProxy.h" |
| #include "src/gpu/ganesh/GrSurfaceProxyPriv.h" |
| #include "src/gpu/ganesh/surface/SkSurface_Ganesh.h" |
| |
| #include <functional> |
| #include <utility> |
| |
| class GrResourceProvider; |
| |
| GrDeferredDisplayListRecorder::GrDeferredDisplayListRecorder(const GrSurfaceCharacterization& c) |
| : fCharacterization(c) { |
| if (fCharacterization.isValid()) { |
| fContext = GrRecordingContextPriv::MakeDDL(fCharacterization.refContextInfo()); |
| } |
| } |
| |
| GrDeferredDisplayListRecorder::~GrDeferredDisplayListRecorder() { |
| if (fContext) { |
| auto proxyProvider = fContext->priv().proxyProvider(); |
| |
| // This allows the uniquely keyed proxies to keep their keys but removes their back |
| // pointer to the about-to-be-deleted proxy provider. The proxies will use their |
| // unique key to reattach to cached versions of themselves or to appropriately tag new |
| // resources (if a cached version was not found). This system operates independent of |
| // the replaying context's proxy provider (i.e., these uniquely keyed proxies will not |
| // appear in the replaying proxy providers uniquely keyed proxy map). This should be fine |
| // since no one else should be trying to reconnect to the orphaned proxies and orphaned |
| // proxies from different DDLs that share the same key should simply reconnect to the |
| // same cached resource. |
| proxyProvider->orphanAllUniqueKeys(); |
| } |
| } |
| |
| bool GrDeferredDisplayListRecorder::init() { |
| SkASSERT(fContext); |
| SkASSERT(!fTargetProxy); |
| SkASSERT(!fLazyProxyData); |
| SkASSERT(!fSurface); |
| |
| if (!fCharacterization.isValid()) { |
| return false; |
| } |
| |
| fLazyProxyData = sk_sp<GrDeferredDisplayList::LazyProxyData>( |
| new GrDeferredDisplayList::LazyProxyData); |
| |
| auto proxyProvider = fContext->priv().proxyProvider(); |
| const GrCaps* caps = fContext->priv().caps(); |
| |
| bool usesGLFBO0 = fCharacterization.usesGLFBO0(); |
| if (usesGLFBO0) { |
| if (GrBackendApi::kOpenGL != fContext->backend() || |
| fCharacterization.isTextureable()) { |
| return false; |
| } |
| } |
| |
| bool vkRTSupportsInputAttachment = fCharacterization.vkRTSupportsInputAttachment(); |
| if (vkRTSupportsInputAttachment && GrBackendApi::kVulkan != fContext->backend()) { |
| return false; |
| } |
| |
| if (fCharacterization.vulkanSecondaryCBCompatible()) { |
| // Because of the restrictive API allowed for a GrVkSecondaryCBDrawContext, we know ahead |
| // of time that we don't be able to support certain parameter combinations. Specifically we |
| // fail on usesGLFBO0 since we can't mix GL and Vulkan. We can't have a texturable object. |
| // We can't use it as in input attachment since we don't control the render pass this will |
| // be played into and thus can't force it to have an input attachment and the correct |
| // dependencies. And finally the GrVkSecondaryCBDrawContext always assumes a top left |
| // origin. |
| if (usesGLFBO0 || |
| vkRTSupportsInputAttachment || |
| fCharacterization.isTextureable() || |
| fCharacterization.origin() == kBottomLeft_GrSurfaceOrigin) { |
| return false; |
| } |
| } |
| |
| GrColorType grColorType = SkColorTypeToGrColorType(fCharacterization.colorType()); |
| |
| // What we're doing here is we're creating a lazy proxy to back the SkSurface. The lazy |
| // proxy, when instantiated, will use the GrRenderTarget that backs the SkSurface that the |
| // DDL is being replayed into. |
| |
| GrInternalSurfaceFlags surfaceFlags = GrInternalSurfaceFlags::kNone; |
| if (usesGLFBO0) { |
| surfaceFlags |= GrInternalSurfaceFlags::kGLRTFBOIDIs0; |
| } else if (fCharacterization.sampleCount() > 1 && !caps->msaaResolvesAutomatically() && |
| fCharacterization.isTextureable()) { |
| surfaceFlags |= GrInternalSurfaceFlags::kRequiresManualMSAAResolve; |
| } |
| |
| if (vkRTSupportsInputAttachment) { |
| surfaceFlags |= GrInternalSurfaceFlags::kVkRTSupportsInputAttachment; |
| } |
| |
| // FIXME: Why do we use skgpu::Mipmapped::kNo instead of |
| // GrSurfaceCharacterization::fIsMipMapped? |
| static constexpr GrProxyProvider::TextureInfo kTextureInfo{skgpu::Mipmapped::kNo, |
| GrTextureType::k2D}; |
| const GrProxyProvider::TextureInfo* optionalTextureInfo = nullptr; |
| if (fCharacterization.isTextureable()) { |
| optionalTextureInfo = &kTextureInfo; |
| } |
| |
| fTargetProxy = proxyProvider->createLazyRenderTargetProxy( |
| [lazyProxyData = fLazyProxyData](GrResourceProvider* resourceProvider, |
| const GrSurfaceProxy::LazySurfaceDesc&) { |
| // The proxy backing the destination surface had better have been instantiated |
| // prior to this one (i.e., the proxy backing the DDL's surface). |
| // Fulfill this lazy proxy with the destination surface's GrRenderTarget. |
| SkASSERT(lazyProxyData->fReplayDest->peekSurface()); |
| auto surface = sk_ref_sp<GrSurface>(lazyProxyData->fReplayDest->peekSurface()); |
| return GrSurfaceProxy::LazyCallbackResult(std::move(surface)); |
| }, |
| fCharacterization.backendFormat(), |
| fCharacterization.dimensions(), |
| fCharacterization.sampleCount(), |
| surfaceFlags, |
| optionalTextureInfo, |
| GrMipmapStatus::kNotAllocated, |
| SkBackingFit::kExact, |
| skgpu::Budgeted::kYes, |
| fCharacterization.isProtected(), |
| fCharacterization.vulkanSecondaryCBCompatible(), |
| GrSurfaceProxy::UseAllocator::kYes); |
| |
| if (!fTargetProxy) { |
| return false; |
| } |
| fTargetProxy->priv().setIsDDLTarget(); |
| |
| auto device = fContext->priv().createDevice(grColorType, |
| fTargetProxy, |
| fCharacterization.refColorSpace(), |
| fCharacterization.origin(), |
| fCharacterization.surfaceProps(), |
| skgpu::ganesh::Device::InitContents::kUninit); |
| if (!device) { |
| return false; |
| } |
| |
| fSurface = sk_make_sp<SkSurface_Ganesh>(std::move(device)); |
| return SkToBool(fSurface.get()); |
| } |
| |
| SkCanvas* GrDeferredDisplayListRecorder::getCanvas() { |
| if (!fContext) { |
| return nullptr; |
| } |
| |
| if (!fSurface && !this->init()) { |
| return nullptr; |
| } |
| |
| return fSurface->getCanvas(); |
| } |
| |
| sk_sp<GrDeferredDisplayList> GrDeferredDisplayListRecorder::detach() { |
| if (!fContext || !fTargetProxy) { |
| return nullptr; |
| } |
| |
| if (fSurface) { |
| SkCanvas* canvas = fSurface->getCanvas(); |
| |
| canvas->restoreToCount(0); |
| } |
| |
| auto ddl = sk_sp<GrDeferredDisplayList>(new GrDeferredDisplayList(fCharacterization, |
| std::move(fTargetProxy), |
| std::move(fLazyProxyData))); |
| |
| fContext->priv().moveRenderTasksToDDL(ddl.get()); |
| |
| // We want a new lazy proxy target for each recorded DDL so force the (lazy proxy-backed) |
| // SkSurface to be regenerated for each DDL. |
| fSurface = nullptr; |
| return ddl; |
| } |