1 /*
2 * Copyright 2023 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/private/chromium/GrDeferredDisplayListRecorder.h"
9
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkSurface.h"
12 #include "include/gpu/GpuTypes.h"
13 #include "include/gpu/ganesh/GrRecordingContext.h"
14 #include "include/gpu/ganesh/GrTypes.h"
15 #include "include/private/base/SkTo.h"
16 #include "include/private/chromium/GrDeferredDisplayList.h"
17 #include "include/private/chromium/GrSurfaceCharacterization.h"
18 #include "include/private/gpu/ganesh/GrTypesPriv.h"
19 #include "src/gpu/SkBackingFit.h"
20 #include "src/gpu/ganesh/Device.h"
21 #include "src/gpu/ganesh/GrCaps.h"
22 #include "src/gpu/ganesh/GrProxyProvider.h"
23 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
24 #include "src/gpu/ganesh/GrRenderTargetProxy.h"
25 #include "src/gpu/ganesh/GrSurface.h"
26 #include "src/gpu/ganesh/GrSurfaceProxy.h"
27 #include "src/gpu/ganesh/GrSurfaceProxyPriv.h"
28 #include "src/gpu/ganesh/surface/SkSurface_Ganesh.h"
29
30 #include <functional>
31 #include <utility>
32
33 class GrResourceProvider;
34
GrDeferredDisplayListRecorder(const GrSurfaceCharacterization & c)35 GrDeferredDisplayListRecorder::GrDeferredDisplayListRecorder(const GrSurfaceCharacterization& c)
36 : fCharacterization(c) {
37 if (fCharacterization.isValid()) {
38 fContext = GrRecordingContextPriv::MakeDDL(fCharacterization.refContextInfo());
39 }
40 }
41
~GrDeferredDisplayListRecorder()42 GrDeferredDisplayListRecorder::~GrDeferredDisplayListRecorder() {
43 if (fContext) {
44 auto proxyProvider = fContext->priv().proxyProvider();
45
46 // This allows the uniquely keyed proxies to keep their keys but removes their back
47 // pointer to the about-to-be-deleted proxy provider. The proxies will use their
48 // unique key to reattach to cached versions of themselves or to appropriately tag new
49 // resources (if a cached version was not found). This system operates independent of
50 // the replaying context's proxy provider (i.e., these uniquely keyed proxies will not
51 // appear in the replaying proxy providers uniquely keyed proxy map). This should be fine
52 // since no one else should be trying to reconnect to the orphaned proxies and orphaned
53 // proxies from different DDLs that share the same key should simply reconnect to the
54 // same cached resource.
55 proxyProvider->orphanAllUniqueKeys();
56 }
57 }
58
init()59 bool GrDeferredDisplayListRecorder::init() {
60 SkASSERT(fContext);
61 SkASSERT(!fTargetProxy);
62 SkASSERT(!fLazyProxyData);
63 SkASSERT(!fSurface);
64
65 if (!fCharacterization.isValid()) {
66 return false;
67 }
68
69 fLazyProxyData = sk_sp<GrDeferredDisplayList::LazyProxyData>(
70 new GrDeferredDisplayList::LazyProxyData);
71
72 auto proxyProvider = fContext->priv().proxyProvider();
73 const GrCaps* caps = fContext->priv().caps();
74
75 bool usesGLFBO0 = fCharacterization.usesGLFBO0();
76 if (usesGLFBO0) {
77 if (GrBackendApi::kOpenGL != fContext->backend() ||
78 fCharacterization.isTextureable()) {
79 return false;
80 }
81 }
82
83 bool vkRTSupportsInputAttachment = fCharacterization.vkRTSupportsInputAttachment();
84 if (vkRTSupportsInputAttachment && GrBackendApi::kVulkan != fContext->backend()) {
85 return false;
86 }
87
88 if (fCharacterization.vulkanSecondaryCBCompatible()) {
89 // Because of the restrictive API allowed for a GrVkSecondaryCBDrawContext, we know ahead
90 // of time that we don't be able to support certain parameter combinations. Specifically we
91 // fail on usesGLFBO0 since we can't mix GL and Vulkan. We can't have a texturable object.
92 // We can't use it as in input attachment since we don't control the render pass this will
93 // be played into and thus can't force it to have an input attachment and the correct
94 // dependencies. And finally the GrVkSecondaryCBDrawContext always assumes a top left
95 // origin.
96 if (usesGLFBO0 ||
97 vkRTSupportsInputAttachment ||
98 fCharacterization.isTextureable() ||
99 fCharacterization.origin() == kBottomLeft_GrSurfaceOrigin) {
100 return false;
101 }
102 }
103
104 GrColorType grColorType = SkColorTypeToGrColorType(fCharacterization.colorType());
105
106 // What we're doing here is we're creating a lazy proxy to back the SkSurface. The lazy
107 // proxy, when instantiated, will use the GrRenderTarget that backs the SkSurface that the
108 // DDL is being replayed into.
109
110 GrInternalSurfaceFlags surfaceFlags = GrInternalSurfaceFlags::kNone;
111 if (usesGLFBO0) {
112 surfaceFlags |= GrInternalSurfaceFlags::kGLRTFBOIDIs0;
113 } else if (fCharacterization.sampleCount() > 1 && !caps->msaaResolvesAutomatically() &&
114 fCharacterization.isTextureable()) {
115 surfaceFlags |= GrInternalSurfaceFlags::kRequiresManualMSAAResolve;
116 }
117
118 if (vkRTSupportsInputAttachment) {
119 surfaceFlags |= GrInternalSurfaceFlags::kVkRTSupportsInputAttachment;
120 }
121
122 // FIXME: Why do we use skgpu::Mipmapped::kNo instead of
123 // GrSurfaceCharacterization::fIsMipMapped?
124 static constexpr GrProxyProvider::TextureInfo kTextureInfo{skgpu::Mipmapped::kNo,
125 GrTextureType::k2D};
126 const GrProxyProvider::TextureInfo* optionalTextureInfo = nullptr;
127 if (fCharacterization.isTextureable()) {
128 optionalTextureInfo = &kTextureInfo;
129 }
130
131 fTargetProxy = proxyProvider->createLazyRenderTargetProxy(
132 [lazyProxyData = fLazyProxyData](GrResourceProvider* resourceProvider,
133 const GrSurfaceProxy::LazySurfaceDesc&) {
134 // The proxy backing the destination surface had better have been instantiated
135 // prior to this one (i.e., the proxy backing the DDL's surface).
136 // Fulfill this lazy proxy with the destination surface's GrRenderTarget.
137 SkASSERT(lazyProxyData->fReplayDest->peekSurface());
138 auto surface = sk_ref_sp<GrSurface>(lazyProxyData->fReplayDest->peekSurface());
139 return GrSurfaceProxy::LazyCallbackResult(std::move(surface));
140 },
141 fCharacterization.backendFormat(),
142 fCharacterization.dimensions(),
143 fCharacterization.sampleCount(),
144 surfaceFlags,
145 optionalTextureInfo,
146 GrMipmapStatus::kNotAllocated,
147 SkBackingFit::kExact,
148 skgpu::Budgeted::kYes,
149 fCharacterization.isProtected(),
150 fCharacterization.vulkanSecondaryCBCompatible(),
151 GrSurfaceProxy::UseAllocator::kYes);
152
153 if (!fTargetProxy) {
154 return false;
155 }
156 fTargetProxy->priv().setIsDDLTarget();
157
158 auto device = fContext->priv().createDevice(grColorType,
159 fTargetProxy,
160 fCharacterization.refColorSpace(),
161 fCharacterization.origin(),
162 fCharacterization.surfaceProps(),
163 skgpu::ganesh::Device::InitContents::kUninit);
164 if (!device) {
165 return false;
166 }
167
168 fSurface = sk_make_sp<SkSurface_Ganesh>(std::move(device));
169 return SkToBool(fSurface.get());
170 }
171
getCanvas()172 SkCanvas* GrDeferredDisplayListRecorder::getCanvas() {
173 if (!fContext) {
174 return nullptr;
175 }
176
177 if (!fSurface && !this->init()) {
178 return nullptr;
179 }
180
181 return fSurface->getCanvas();
182 }
183
detach()184 sk_sp<GrDeferredDisplayList> GrDeferredDisplayListRecorder::detach() {
185 if (!fContext || !fTargetProxy) {
186 return nullptr;
187 }
188
189 if (fSurface) {
190 SkCanvas* canvas = fSurface->getCanvas();
191
192 canvas->restoreToCount(0);
193 }
194
195 auto ddl = sk_sp<GrDeferredDisplayList>(new GrDeferredDisplayList(fCharacterization,
196 std::move(fTargetProxy),
197 std::move(fLazyProxyData)));
198
199 fContext->priv().moveRenderTasksToDDL(ddl.get());
200
201 // We want a new lazy proxy target for each recorded DDL so force the (lazy proxy-backed)
202 // SkSurface to be regenerated for each DDL.
203 fSurface = nullptr;
204 return ddl;
205 }
206