xref: /aosp_15_r20/external/skia/src/gpu/ganesh/image/SkImage_Ganesh.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2012 Google Inc.
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 "src/gpu/ganesh/image/SkImage_Ganesh.h"
9 
10 #include "include/core/SkColorSpace.h"
11 #include "include/core/SkImage.h"
12 #include "include/core/SkImageInfo.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkSize.h"
15 #include "include/core/SkSurface.h"
16 #include "include/gpu/GpuTypes.h"
17 #include "include/gpu/ganesh/GrBackendSurface.h"
18 #include "include/gpu/ganesh/GrDirectContext.h"
19 #include "include/gpu/ganesh/GrRecordingContext.h"
20 #include "include/gpu/ganesh/GrTypes.h"
21 #include "include/private/base/SkAssert.h"
22 #include "include/private/gpu/ganesh/GrImageContext.h"
23 #include "include/private/gpu/ganesh/GrTypesPriv.h"
24 #include "src/gpu/SkBackingFit.h"
25 #include "src/gpu/ganesh/GrCaps.h"
26 #include "src/gpu/ganesh/GrColorInfo.h"
27 #include "src/gpu/ganesh/GrColorSpaceXform.h"
28 #include "src/gpu/ganesh/GrDirectContextPriv.h"
29 #include "src/gpu/ganesh/GrFragmentProcessor.h"
30 #include "src/gpu/ganesh/GrImageContextPriv.h"
31 #include "src/gpu/ganesh/GrImageInfo.h"
32 #include "src/gpu/ganesh/GrRenderTask.h"
33 #include "src/gpu/ganesh/GrSurfaceProxy.h"
34 #include "src/gpu/ganesh/GrTexture.h"
35 #include "src/gpu/ganesh/GrTextureProxy.h"
36 #include "src/gpu/ganesh/SkGr.h"
37 #include "src/gpu/ganesh/SurfaceContext.h"
38 #include "src/gpu/ganesh/SurfaceFillContext.h"
39 #include "src/gpu/ganesh/effects/GrTextureEffect.h"
40 #include "src/gpu/ganesh/image/GrImageUtils.h"
41 #include "src/image/SkImage_Base.h"
42 
43 #include <cstddef>
44 #include <utility>
45 
46 class SkMatrix;
47 enum SkColorType : int;
48 enum class SkTileMode;
49 
ProxyChooser(sk_sp<GrSurfaceProxy> stableProxy)50 inline SkImage_Ganesh::ProxyChooser::ProxyChooser(sk_sp<GrSurfaceProxy> stableProxy)
51         : fStableProxy(std::move(stableProxy)) {
52     SkASSERT(fStableProxy);
53 }
54 
ProxyChooser(sk_sp<GrSurfaceProxy> stableProxy,sk_sp<GrSurfaceProxy> volatileProxy,sk_sp<GrRenderTask> copyTask,int volatileProxyTargetCount)55 inline SkImage_Ganesh::ProxyChooser::ProxyChooser(sk_sp<GrSurfaceProxy> stableProxy,
56                                                   sk_sp<GrSurfaceProxy> volatileProxy,
57                                                   sk_sp<GrRenderTask> copyTask,
58                                                   int volatileProxyTargetCount)
59         : fStableProxy(std::move(stableProxy))
60         , fVolatileProxy(std::move(volatileProxy))
61         , fVolatileToStableCopyTask(std::move(copyTask))
62         , fVolatileProxyTargetCount(volatileProxyTargetCount) {
63     SkASSERT(fStableProxy);
64     SkASSERT(fVolatileProxy);
65     SkASSERT(fVolatileToStableCopyTask);
66 }
67 
~ProxyChooser()68 inline SkImage_Ganesh::ProxyChooser::~ProxyChooser() {
69     // The image is being destroyed. If there is a stable copy proxy but we've been able to use
70     // the volatile proxy for all requests then we can skip the copy.
71     if (fVolatileToStableCopyTask) {
72         fVolatileToStableCopyTask->makeSkippable();
73     }
74 }
75 
chooseProxy(GrRecordingContext * context)76 inline sk_sp<GrSurfaceProxy> SkImage_Ganesh::ProxyChooser::chooseProxy(
77         GrRecordingContext* context) {
78     SkAutoSpinlock hold(fLock);
79     if (fVolatileProxy) {
80         SkASSERT(fVolatileProxyTargetCount <= fVolatileProxy->getTaskTargetCount());
81         // If this image is used off the direct context it originated on, i.e. on a recording-only
82         // context, we don't know how the recording context's actions are ordered WRT direct context
83         // actions until the recording context's DAG is imported into the direct context.
84         if (context->asDirectContext() &&
85             fVolatileProxyTargetCount == fVolatileProxy->getTaskTargetCount()) {
86             return fVolatileProxy;
87         }
88         fVolatileProxy.reset();
89         fVolatileToStableCopyTask.reset();
90         return fStableProxy;
91     }
92     return fStableProxy;
93 }
94 
switchToStableProxy()95 inline sk_sp<GrSurfaceProxy> SkImage_Ganesh::ProxyChooser::switchToStableProxy() {
96     SkAutoSpinlock hold(fLock);
97     fVolatileProxy.reset();
98     fVolatileToStableCopyTask.reset();
99     return fStableProxy;
100 }
101 
makeVolatileProxyStable()102 inline sk_sp<GrSurfaceProxy> SkImage_Ganesh::ProxyChooser::makeVolatileProxyStable() {
103     SkAutoSpinlock hold(fLock);
104     if (fVolatileProxy) {
105         fStableProxy = std::move(fVolatileProxy);
106         fVolatileToStableCopyTask->makeSkippable();
107         fVolatileToStableCopyTask.reset();
108     }
109     return fStableProxy;
110 }
111 
surfaceMustCopyOnWrite(GrSurfaceProxy * surfaceProxy) const112 inline bool SkImage_Ganesh::ProxyChooser::surfaceMustCopyOnWrite(
113         GrSurfaceProxy* surfaceProxy) const {
114     SkAutoSpinlock hold(fLock);
115     return surfaceProxy->underlyingUniqueID() == fStableProxy->underlyingUniqueID();
116 }
117 
gpuMemorySize() const118 inline size_t SkImage_Ganesh::ProxyChooser::gpuMemorySize() const {
119     SkAutoSpinlock hold(fLock);
120     size_t size = fStableProxy->gpuMemorySize();
121     if (fVolatileProxy) {
122         SkASSERT(fVolatileProxy->gpuMemorySize() == size);
123     }
124     return size;
125 }
126 
mipmapped() const127 inline skgpu::Mipmapped SkImage_Ganesh::ProxyChooser::mipmapped() const {
128     SkAutoSpinlock hold(fLock);
129     skgpu::Mipmapped mipmapped = fStableProxy->asTextureProxy()->mipmapped();
130     if (fVolatileProxy) {
131         SkASSERT(fVolatileProxy->asTextureProxy()->mipmapped() == mipmapped);
132     }
133     return mipmapped;
134 }
135 
isProtected() const136 inline skgpu::Protected SkImage_Ganesh::ProxyChooser::isProtected() const {
137     SkAutoSpinlock hold(fLock);
138     skgpu::Protected isProtected = fStableProxy->asTextureProxy()->isProtected();
139     if (fVolatileProxy) {
140         SkASSERT(fVolatileProxy->asTextureProxy()->isProtected() == isProtected);
141     }
142     return isProtected;
143 }
144 
145 #ifdef SK_DEBUG
backendFormat()146 inline const GrBackendFormat& SkImage_Ganesh::ProxyChooser::backendFormat() {
147     SkAutoSpinlock hold(fLock);
148     if (fVolatileProxy) {
149         SkASSERT(fVolatileProxy->backendFormat() == fStableProxy->backendFormat());
150     }
151     return fStableProxy->backendFormat();
152 }
153 #endif
154 
155 //////////////////////////////////////////////////////////////////////////////
156 
SkImage_Ganesh(sk_sp<GrImageContext> context,uint32_t uniqueID,GrSurfaceProxyView view,SkColorInfo info)157 SkImage_Ganesh::SkImage_Ganesh(sk_sp<GrImageContext> context,
158                                uint32_t uniqueID,
159                                GrSurfaceProxyView view,
160                                SkColorInfo info)
161         : INHERITED(std::move(context),
162                     SkImageInfo::Make(view.proxy()->backingStoreDimensions(), std::move(info)),
163                     uniqueID)
164         , fChooser(view.detachProxy())
165         , fSwizzle(view.swizzle())
166         , fOrigin(view.origin()) {
167 #ifdef SK_DEBUG
168     const GrBackendFormat& format = fChooser.backendFormat();
169     const GrCaps* caps = this->context()->priv().caps();
170     GrColorType grCT = SkColorTypeToGrColorType(this->colorType());
171     SkASSERT(caps->isFormatCompressed(format) ||
172              caps->areColorTypeAndFormatCompatible(grCT, format));
173 #endif
174 }
175 
SkImage_Ganesh(sk_sp<GrDirectContext> dContext,GrSurfaceProxyView volatileSrc,sk_sp<GrSurfaceProxy> stableCopy,sk_sp<GrRenderTask> copyTask,int volatileSrcTargetCount,SkColorInfo info)176 SkImage_Ganesh::SkImage_Ganesh(sk_sp<GrDirectContext> dContext,
177                                GrSurfaceProxyView volatileSrc,
178                                sk_sp<GrSurfaceProxy> stableCopy,
179                                sk_sp<GrRenderTask> copyTask,
180                                int volatileSrcTargetCount,
181                                SkColorInfo info)
182         : INHERITED(
183                   std::move(dContext),
184                   SkImageInfo::Make(volatileSrc.proxy()->backingStoreDimensions(), std::move(info)),
185                   kNeedNewImageUniqueID)
186         , fChooser(std::move(stableCopy),
187                    volatileSrc.detachProxy(),
188                    std::move(copyTask),
189                    volatileSrcTargetCount)
190         , fSwizzle(volatileSrc.swizzle())
191         , fOrigin(volatileSrc.origin()) {
192 #ifdef SK_DEBUG
193     const GrBackendFormat& format = fChooser.backendFormat();
194     const GrCaps* caps = this->context()->priv().caps();
195     GrColorType grCT = SkColorTypeToGrColorType(this->colorType());
196     SkASSERT(caps->isFormatCompressed(format) ||
197              caps->areColorTypeAndFormatCompatible(grCT, format));
198 #endif
199 }
200 
MakeWithVolatileSrc(sk_sp<GrRecordingContext> rContext,GrSurfaceProxyView volatileSrc,SkColorInfo colorInfo)201 sk_sp<SkImage> SkImage_Ganesh::MakeWithVolatileSrc(sk_sp<GrRecordingContext> rContext,
202                                                    GrSurfaceProxyView volatileSrc,
203                                                    SkColorInfo colorInfo) {
204     SkASSERT(rContext);
205     SkASSERT(volatileSrc);
206     SkASSERT(volatileSrc.proxy()->asTextureProxy());
207     skgpu::Mipmapped mm = volatileSrc.proxy()->asTextureProxy()->mipmapped();
208     sk_sp<GrRenderTask> copyTask;
209     auto copy = GrSurfaceProxy::Copy(rContext.get(),
210                                      volatileSrc.refProxy(),
211                                      volatileSrc.origin(),
212                                      mm,
213                                      SkBackingFit::kExact,
214                                      skgpu::Budgeted::kYes,
215                                      /*label=*/"ImageGpu_MakeWithVolatileSrc",
216                                      &copyTask);
217     if (!copy) {
218         return nullptr;
219     }
220     // We only attempt to make a dual-proxy image on a direct context. This optimziation requires
221     // knowing how things are ordered and recording-only contexts are not well ordered WRT other
222     // recording contexts.
223     if (auto direct = sk_ref_sp(rContext->asDirectContext())) {
224         int targetCount = volatileSrc.proxy()->getTaskTargetCount();
225         return sk_sp<SkImage>(new SkImage_Ganesh(std::move(direct),
226                                                  std::move(volatileSrc),
227                                                  std::move(copy),
228                                                  std::move(copyTask),
229                                                  targetCount,
230                                                  std::move(colorInfo)));
231     }
232     GrSurfaceProxyView copyView(std::move(copy), volatileSrc.origin(), volatileSrc.swizzle());
233     return sk_make_sp<SkImage_Ganesh>(
234             std::move(rContext), kNeedNewImageUniqueID, std::move(copyView), std::move(colorInfo));
235 }
236 
237 SkImage_Ganesh::~SkImage_Ganesh() = default;
238 
surfaceMustCopyOnWrite(GrSurfaceProxy * surfaceProxy) const239 bool SkImage_Ganesh::surfaceMustCopyOnWrite(GrSurfaceProxy* surfaceProxy) const {
240     return fChooser.surfaceMustCopyOnWrite(surfaceProxy);
241 }
242 
onHasMipmaps() const243 bool SkImage_Ganesh::onHasMipmaps() const { return fChooser.mipmapped() == skgpu::Mipmapped::kYes; }
244 
onIsProtected() const245 bool SkImage_Ganesh::onIsProtected() const {
246     return fChooser.isProtected() == skgpu::Protected::kYes;
247 }
248 
flush(GrDirectContext * dContext,const GrFlushInfo & info) const249 GrSemaphoresSubmitted SkImage_Ganesh::flush(GrDirectContext* dContext,
250                                             const GrFlushInfo& info) const {
251     if (!fContext->priv().matches(dContext) || dContext->abandoned()) {
252         if (info.fSubmittedProc) {
253             info.fSubmittedProc(info.fSubmittedContext, false);
254         }
255         if (info.fFinishedProc) {
256             info.fFinishedProc(info.fFinishedContext);
257         }
258         return GrSemaphoresSubmitted::kNo;
259     }
260 
261     sk_sp<GrSurfaceProxy> proxy = fChooser.chooseProxy(dContext);
262     return dContext->priv().flushSurface(
263             proxy.get(), SkSurfaces::BackendSurfaceAccess::kNoAccess, info);
264 }
265 
getExistingBackendTexture(GrBackendTexture * outTexture,bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const266 bool SkImage_Ganesh::getExistingBackendTexture(GrBackendTexture* outTexture,
267                                                bool flushPendingGrContextIO,
268                                                GrSurfaceOrigin* origin) const {
269     auto direct = fContext->asDirectContext();
270     if (!direct) {
271         // This image was created with a DDL context and cannot be instantiated.
272         return false;
273     }
274     if (direct->abandoned()) {
275         return false;
276     }
277 
278     // We don't know how client's use of the texture will be ordered WRT Skia's. Ensure the
279     // texture seen by the client won't be mutated by a SkSurface.
280     sk_sp<GrSurfaceProxy> proxy = fChooser.switchToStableProxy();
281 
282     if (!proxy->isInstantiated()) {
283         auto resourceProvider = direct->priv().resourceProvider();
284 
285         if (!proxy->instantiate(resourceProvider)) {
286             return false;
287         }
288     }
289 
290     GrTexture* texture = proxy->peekTexture();
291     if (!texture) {
292         return false;
293     }
294     if (flushPendingGrContextIO) {
295         direct->priv().flushSurface(proxy.get());
296     }
297     if (origin) {
298         *origin = fOrigin;
299     }
300     if (outTexture) {
301         *outTexture = texture->getBackendTexture();
302     }
303     return true;
304 }
305 
textureSize() const306 size_t SkImage_Ganesh::textureSize() const { return fChooser.gpuMemorySize(); }
307 
onMakeColorTypeAndColorSpace(SkColorType targetCT,sk_sp<SkColorSpace> targetCS,GrDirectContext * dContext) const308 sk_sp<SkImage> SkImage_Ganesh::onMakeColorTypeAndColorSpace(SkColorType targetCT,
309                                                             sk_sp<SkColorSpace> targetCS,
310                                                             GrDirectContext* dContext) const {
311     SkColorInfo info(targetCT, this->alphaType(), std::move(targetCS));
312     if (!fContext->priv().matches(dContext)) {
313         return nullptr;
314     }
315 
316     sk_sp<GrSurfaceProxy> proxy = fChooser.chooseProxy(dContext);
317 
318     auto sfc = dContext->priv().makeSFCWithFallback(GrImageInfo(info, this->dimensions()),
319                                                     SkBackingFit::kExact,
320                                                     /* sampleCount= */ 1,
321                                                     skgpu::Mipmapped::kNo,
322                                                     proxy->isProtected());
323     if (!sfc) {
324         return nullptr;
325     }
326     // We respecify info's CT because we called MakeWithFallback.
327     auto ct = GrColorTypeToSkColorType(sfc->colorInfo().colorType());
328     info = info.makeColorType(ct);
329 
330     // Draw this image's texture into the SFC.
331     auto [view, _] = skgpu::ganesh::AsView(dContext, this, skgpu::Mipmapped(this->hasMipmaps()));
332     auto texFP = GrTextureEffect::Make(std::move(view), this->alphaType());
333     auto colorFP =
334             GrColorSpaceXformEffect::Make(std::move(texFP), this->imageInfo().colorInfo(), info);
335     sfc->fillWithFP(std::move(colorFP));
336 
337     return sk_make_sp<SkImage_Ganesh>(
338             sk_ref_sp(dContext), kNeedNewImageUniqueID, sfc->readSurfaceView(), std::move(info));
339 }
340 
onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const341 sk_sp<SkImage> SkImage_Ganesh::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
342     // It doesn't seem worth the complexity of trying to share the ProxyChooser among multiple
343     // images. Just fall back to the stable copy.
344     GrSurfaceProxyView view(fChooser.switchToStableProxy(), fOrigin, fSwizzle);
345     return sk_make_sp<SkImage_Ganesh>(
346             fContext,
347             kNeedNewImageUniqueID,
348             std::move(view),
349             this->imageInfo().colorInfo().makeColorSpace(std::move(newCS)));
350 }
351 
onAsyncRescaleAndReadPixels(const SkImageInfo & info,SkIRect srcRect,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context) const352 void SkImage_Ganesh::onAsyncRescaleAndReadPixels(const SkImageInfo& info,
353                                                  SkIRect srcRect,
354                                                  RescaleGamma rescaleGamma,
355                                                  RescaleMode rescaleMode,
356                                                  ReadPixelsCallback callback,
357                                                  ReadPixelsContext context) const {
358     auto dContext = fContext->asDirectContext();
359     if (!dContext) {
360         // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
361         callback(context, nullptr);
362         return;
363     }
364     auto ctx = dContext->priv().makeSC(this->makeView(dContext), this->imageInfo().colorInfo());
365     if (!ctx) {
366         callback(context, nullptr);
367         return;
368     }
369     ctx->asyncRescaleAndReadPixels(
370             dContext, info, srcRect, rescaleGamma, rescaleMode, callback, context);
371 }
372 
onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,bool readAlpha,sk_sp<SkColorSpace> dstColorSpace,SkIRect srcRect,SkISize dstSize,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context) const373 void SkImage_Ganesh::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
374                                                        bool readAlpha,
375                                                        sk_sp<SkColorSpace> dstColorSpace,
376                                                        SkIRect srcRect,
377                                                        SkISize dstSize,
378                                                        RescaleGamma rescaleGamma,
379                                                        RescaleMode rescaleMode,
380                                                        ReadPixelsCallback callback,
381                                                        ReadPixelsContext context) const {
382     auto dContext = fContext->asDirectContext();
383     if (!dContext) {
384         // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
385         callback(context, nullptr);
386         return;
387     }
388     auto ctx = dContext->priv().makeSC(this->makeView(dContext), this->imageInfo().colorInfo());
389     if (!ctx) {
390         callback(context, nullptr);
391         return;
392     }
393     ctx->asyncRescaleAndReadPixelsYUV420(dContext,
394                                          yuvColorSpace,
395                                          readAlpha,
396                                          std::move(dstColorSpace),
397                                          srcRect,
398                                          dstSize,
399                                          rescaleGamma,
400                                          rescaleMode,
401                                          callback,
402                                          context);
403 }
404 
generatingSurfaceIsDeleted()405 void SkImage_Ganesh::generatingSurfaceIsDeleted() { fChooser.makeVolatileProxyStable(); }
406 
asView(GrRecordingContext * recordingContext,skgpu::Mipmapped mipmapped,GrImageTexGenPolicy policy) const407 std::tuple<GrSurfaceProxyView, GrColorType> SkImage_Ganesh::asView(
408         GrRecordingContext* recordingContext,
409         skgpu::Mipmapped mipmapped,
410         GrImageTexGenPolicy policy) const {
411     if (!fContext->priv().matches(recordingContext)) {
412         return {};
413     }
414     if (policy != GrImageTexGenPolicy::kDraw) {
415         return {skgpu::ganesh::CopyView(recordingContext,
416                                         this->makeView(recordingContext),
417                                         mipmapped,
418                                         policy,
419                                         /*label=*/"SkImageGpu_AsView"),
420                 SkColorTypeToGrColorType(this->colorType())};
421     }
422     GrSurfaceProxyView view = this->makeView(recordingContext);
423     GrColorType ct = SkColorTypeToGrColorType(this->colorType());
424     if (mipmapped == skgpu::Mipmapped::kYes) {
425         view = skgpu::ganesh::FindOrMakeCachedMipmappedView(recordingContext, std::move(view),
426                                                             this->uniqueID());
427     }
428     return {std::move(view), ct};
429 }
430 
asFragmentProcessor(GrRecordingContext * rContext,SkSamplingOptions sampling,const SkTileMode tileModes[2],const SkMatrix & m,const SkRect * subset,const SkRect * domain) const431 std::unique_ptr<GrFragmentProcessor> SkImage_Ganesh::asFragmentProcessor(
432         GrRecordingContext* rContext,
433         SkSamplingOptions sampling,
434         const SkTileMode tileModes[2],
435         const SkMatrix& m,
436         const SkRect* subset,
437         const SkRect* domain) const {
438     if (!fContext->priv().matches(rContext)) {
439         return {};
440     }
441     auto mm =
442             sampling.mipmap == SkMipmapMode::kNone ? skgpu::Mipmapped::kNo : skgpu::Mipmapped::kYes;
443     return skgpu::ganesh::MakeFragmentProcessorFromView(
444             rContext,
445             std::get<0>(skgpu::ganesh::AsView(rContext, this, mm)),
446             this->alphaType(),
447             sampling,
448             tileModes,
449             m,
450             subset,
451             domain);
452 }
453 
makeView(GrRecordingContext * rContext) const454 GrSurfaceProxyView SkImage_Ganesh::makeView(GrRecordingContext* rContext) const {
455     return {fChooser.chooseProxy(rContext), fOrigin, fSwizzle};
456 }
457