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/surface/SkSurface_Ganesh.h"
9
10#include "include/core/SkCanvas.h"
11#include "include/core/SkColorSpace.h"
12#include "include/core/SkColorType.h"
13#include "include/core/SkImage.h"
14#include "include/core/SkRect.h"
15#include "include/core/SkSize.h"
16#include "include/core/SkSurface.h"
17#include "include/core/SkSurfaceProps.h"
18#include "include/gpu/GpuTypes.h"
19#include "include/gpu/GrBackendSurface.h"
20#include "include/gpu/GrContextThreadSafeProxy.h"
21#include "include/gpu/GrDirectContext.h"
22#include "include/gpu/GrRecordingContext.h"
23#include "include/gpu/GrTypes.h"
24#include "include/gpu/ganesh/SkSurfaceGanesh.h"
25#include "include/private/base/SkTo.h"
26#include "include/private/chromium/GrDeferredDisplayList.h"
27#include "include/private/chromium/GrSurfaceCharacterization.h"
28#include "include/private/gpu/ganesh/GrTypesPriv.h"
29#include "src/core/SkDevice.h"
30#include "src/core/SkSurfacePriv.h"
31#include "src/gpu/RefCntedCallback.h"
32#include "src/gpu/SkBackingFit.h"
33#include "src/gpu/SkRenderEngineAbortf.h"
34#include "src/gpu/ganesh/Device.h"
35#include "src/gpu/ganesh/GrCaps.h"
36#include "src/gpu/ganesh/GrContextThreadSafeProxyPriv.h"
37#include "src/gpu/ganesh/GrDirectContextPriv.h"
38#include "src/gpu/ganesh/GrGpuResourcePriv.h"
39#include "src/gpu/ganesh/GrProxyProvider.h"
40#include "src/gpu/ganesh/GrRecordingContextPriv.h"
41#include "src/gpu/ganesh/GrRenderTarget.h"
42#include "src/gpu/ganesh/GrRenderTargetProxy.h"
43#include "src/gpu/ganesh/GrSurfaceProxy.h"
44#include "src/gpu/ganesh/GrSurfaceProxyPriv.h"
45#include "src/gpu/ganesh/GrSurfaceProxyView.h"
46#include "src/gpu/ganesh/GrTexture.h"
47#include "src/gpu/ganesh/GrTextureProxy.h"
48#include "src/gpu/ganesh/image/SkImage_Ganesh.h"
49#include "src/image/SkImage_Base.h"
50
51#include <algorithm>
52#include <cstddef>
53#include <utility>
54
55class GrBackendSemaphore;
56class SkCapabilities;
57class SkPaint;
58class SkPixmap;
59
60SkSurface_Ganesh::SkSurface_Ganesh(sk_sp<skgpu::ganesh::Device> device)
61 : INHERITED(device->width(), device->height(), &device->surfaceProps())
62 , fDevice(std::move(device)) {
63 SkASSERT(fDevice->targetProxy()->priv().isExact());
64}
65
66SkSurface_Ganesh::~SkSurface_Ganesh() {
67 if (this->hasCachedImage()) {
68 as_IB(image: this->refCachedImage())->generatingSurfaceIsDeleted();
69 }
70}
71
72GrRecordingContext* SkSurface_Ganesh::onGetRecordingContext() const {
73 return fDevice->recordingContext();
74}
75
76skgpu::ganesh::Device* SkSurface_Ganesh::getDevice() { return fDevice.get(); }
77
78SkImageInfo SkSurface_Ganesh::imageInfo() const { return fDevice->imageInfo(); }
79
80static GrRenderTarget* prepare_rt_for_external_access(SkSurface_Ganesh* surface,
81 SkSurfaces::BackendHandleAccess access) {
82 auto dContext = surface->recordingContext()->asDirectContext();
83 if (!dContext) {
84 return nullptr;
85 }
86 if (dContext->abandoned()) {
87 return nullptr;
88 }
89
90 switch (access) {
91 case SkSurfaces::BackendHandleAccess::kFlushRead:
92 break;
93 case SkSurfaces::BackendHandleAccess::kFlushWrite:
94 case SkSurfaces::BackendHandleAccess::kDiscardWrite:
95 // for now we don't special-case on Discard, but we may in the future.
96 surface->notifyContentWillChange(mode: SkSurface::kRetain_ContentChangeMode);
97 break;
98 }
99
100 dContext->priv().flushSurface(proxy: surface->getDevice()->targetProxy());
101
102 // Grab the render target *after* firing notifications, as it may get switched if CoW kicks in.
103 return surface->getDevice()->targetProxy()->peekRenderTarget();
104}
105
106GrBackendTexture SkSurface_Ganesh::getBackendTexture(BackendHandleAccess access) {
107 GrRenderTarget* rt = prepare_rt_for_external_access(surface: this, access);
108 if (!rt) {
109 return GrBackendTexture(); // invalid
110 }
111 GrTexture* texture = rt->asTexture();
112 if (texture) {
113 return texture->getBackendTexture();
114 }
115 return GrBackendTexture(); // invalid
116}
117
118GrBackendRenderTarget SkSurface_Ganesh::getBackendRenderTarget(BackendHandleAccess access) {
119 GrRenderTarget* rt = prepare_rt_for_external_access(surface: this, access);
120 if (!rt) {
121 return GrBackendRenderTarget(); // invalid
122 }
123
124 return rt->getBackendRenderTarget();
125}
126
127SkCanvas* SkSurface_Ganesh::onNewCanvas() { return new SkCanvas(fDevice); }
128
129sk_sp<SkSurface> SkSurface_Ganesh::onNewSurface(const SkImageInfo& info) {
130 GrSurfaceProxyView targetView = fDevice->readSurfaceView();
131 int sampleCount = targetView.asRenderTargetProxy()->numSamples();
132 GrSurfaceOrigin origin = targetView.origin();
133 // TODO: Make caller specify this (change virtual signature of onNewSurface).
134 static const skgpu::Budgeted kBudgeted = skgpu::Budgeted::kNo;
135 return SkSurfaces::RenderTarget(
136 context: fDevice->recordingContext(), budgeted: kBudgeted, imageInfo: info, sampleCount, surfaceOrigin: origin, surfaceProps: &this->props());
137}
138
139sk_sp<SkImage> SkSurface_Ganesh::onNewImageSnapshot(const SkIRect* subset) {
140 GrRenderTargetProxy* rtp = fDevice->targetProxy();
141 if (!rtp) {
142 return nullptr;
143 }
144
145 auto rContext = fDevice->recordingContext();
146
147 GrSurfaceProxyView srcView = fDevice->readSurfaceView();
148
149 skgpu::Budgeted budgeted = rtp->isBudgeted();
150
151 if (subset || !srcView.asTextureProxy() || rtp->refsWrappedObjects()) {
152 // If the original render target is a buffer originally created by the client, then we don't
153 // want to ever retarget the SkSurface at another buffer we create. If the source is a
154 // texture (and the image is not subsetted) we make a dual-proxied SkImage that will
155 // attempt to share the backing store until the surface writes to the shared backing store
156 // at which point it uses a copy.
157 if (!subset && srcView.asTextureProxy()) {
158 return SkImage_Ganesh::MakeWithVolatileSrc(
159 rContext: sk_ref_sp(obj: rContext), volatileSrc: srcView, colorInfo: fDevice->imageInfo().colorInfo());
160 }
161 auto rect = subset ? *subset : SkIRect::MakeSize(size: srcView.dimensions());
162 GrMipmapped mipmapped = srcView.mipmapped();
163 srcView = GrSurfaceProxyView::Copy(context: rContext,
164 src: std::move(srcView),
165 mipmapped,
166 srcRect: rect,
167 fit: SkBackingFit::kExact,
168 budgeted,
169 /*label=*/"SurfaceGpu_NewImageSnapshot");
170 }
171
172 const SkImageInfo info = fDevice->imageInfo();
173 if (!srcView.asTextureProxy()) {
174 return nullptr;
175 }
176 // The surfaceDrawContext coming out of SkGpuDevice should always be exact and the
177 // above copy creates a kExact surfaceContext.
178 SkASSERT(srcView.proxy()->priv().isExact());
179 return sk_make_sp<SkImage_Ganesh>(
180 args: sk_ref_sp(obj: rContext), args: kNeedNewImageUniqueID, args: std::move(srcView), args: info.colorInfo());
181}
182
183void SkSurface_Ganesh::onWritePixels(const SkPixmap& src, int x, int y) {
184 fDevice->writePixels(src, x, y);
185}
186
187void SkSurface_Ganesh::onAsyncRescaleAndReadPixels(const SkImageInfo& info,
188 SkIRect srcRect,
189 RescaleGamma rescaleGamma,
190 RescaleMode rescaleMode,
191 ReadPixelsCallback callback,
192 ReadPixelsContext context) {
193 fDevice->asyncRescaleAndReadPixels(info, srcRect, rescaleGamma, rescaleMode, callback, context);
194}
195
196void SkSurface_Ganesh::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
197 bool readAlpha,
198 sk_sp<SkColorSpace> dstColorSpace,
199 SkIRect srcRect,
200 SkISize dstSize,
201 RescaleGamma rescaleGamma,
202 RescaleMode rescaleMode,
203 ReadPixelsCallback callback,
204 ReadPixelsContext context) {
205 fDevice->asyncRescaleAndReadPixelsYUV420(yuvColorSpace,
206 readAlpha,
207 dstColorSpace: std::move(dstColorSpace),
208 srcRect,
209 dstSize,
210 rescaleGamma,
211 rescaleMode,
212 callback,
213 context);
214}
215
216// Create a new render target and, if necessary, copy the contents of the old
217// render target into it. Note that this flushes the SkGpuDevice but
218// doesn't force an OpenGL flush.
219bool SkSurface_Ganesh::onCopyOnWrite(ContentChangeMode mode) {
220 GrSurfaceProxyView readSurfaceView = fDevice->readSurfaceView();
221
222 // are we sharing our backing proxy with the image? Note this call should never create a new
223 // image because onCopyOnWrite is only called when there is a cached image.
224 sk_sp<SkImage> image = this->refCachedImage();
225 SkASSERT(image);
226
227 if (static_cast<SkImage_Ganesh*>(image.get())
228 ->surfaceMustCopyOnWrite(surfaceProxy: readSurfaceView.proxy())) {
229 if (!fDevice->replaceBackingProxy(mode)) {
230 return false;
231 }
232 } else if (kDiscard_ContentChangeMode == mode) {
233 this->SkSurface_Ganesh::onDiscard();
234 }
235 return true;
236}
237
238void SkSurface_Ganesh::onDiscard() { fDevice->discard(); }
239
240void SkSurface_Ganesh::resolveMSAA() { fDevice->resolveMSAA(); }
241
242bool SkSurface_Ganesh::onWait(int numSemaphores,
243 const GrBackendSemaphore* waitSemaphores,
244 bool deleteSemaphoresAfterWait) {
245 return fDevice->wait(numSemaphores, waitSemaphores, deleteSemaphoresAfterWait);
246}
247
248bool SkSurface_Ganesh::onCharacterize(GrSurfaceCharacterization* characterization) const {
249 auto direct = fDevice->recordingContext()->asDirectContext();
250 if (!direct) {
251 return false;
252 }
253
254 SkImageInfo ii = fDevice->imageInfo();
255 if (ii.colorType() == kUnknown_SkColorType) {
256 return false;
257 }
258
259 GrSurfaceProxyView readSurfaceView = fDevice->readSurfaceView();
260 size_t maxResourceBytes = direct->getResourceCacheLimit();
261
262 bool mipmapped = readSurfaceView.asTextureProxy()
263 ? GrMipmapped::kYes == readSurfaceView.asTextureProxy()->mipmapped()
264 : false;
265
266 bool usesGLFBO0 = readSurfaceView.asRenderTargetProxy()->glRTFBOIDIs0();
267 // We should never get in the situation where we have a texture render target that is also
268 // backend by FBO 0.
269 SkASSERT(!usesGLFBO0 || !SkToBool(readSurfaceView.asTextureProxy()));
270
271 bool vkRTSupportsInputAttachment =
272 readSurfaceView.asRenderTargetProxy()->supportsVkInputAttachment();
273
274 GrBackendFormat format = readSurfaceView.proxy()->backendFormat();
275 int numSamples = readSurfaceView.asRenderTargetProxy()->numSamples();
276 GrProtected isProtected = readSurfaceView.asRenderTargetProxy()->isProtected();
277
278 characterization->set(
279 contextInfo: direct->threadSafeProxy(),
280 cacheMaxResourceBytes: maxResourceBytes,
281 ii,
282 backendFormat: format,
283 origin: readSurfaceView.origin(),
284 sampleCnt: numSamples,
285 isTextureable: GrSurfaceCharacterization::Textureable(SkToBool(x: readSurfaceView.asTextureProxy())),
286 isMipMapped: GrSurfaceCharacterization::MipMapped(mipmapped),
287 usesGLFBO0: GrSurfaceCharacterization::UsesGLFBO0(usesGLFBO0),
288 vkRTSupportsInputAttachment: GrSurfaceCharacterization::VkRTSupportsInputAttachment(vkRTSupportsInputAttachment),
289 vulkanSecondaryCBCompatible: GrSurfaceCharacterization::VulkanSecondaryCBCompatible(false),
290 isProtected,
291 surfaceProps: this->props());
292 return true;
293}
294
295void SkSurface_Ganesh::onDraw(SkCanvas* canvas,
296 SkScalar x,
297 SkScalar y,
298 const SkSamplingOptions& sampling,
299 const SkPaint* paint) {
300 // If the dst is also GPU we try to not force a new image snapshot (by calling the base class
301 // onDraw) since that may not always perform the copy-on-write optimization.
302 auto tryDraw = [&] {
303 auto surfaceContext = fDevice->recordingContext();
304 auto canvasContext = GrAsDirectContext(base: canvas->recordingContext());
305 if (!canvasContext) {
306 return false;
307 }
308 if (canvasContext->priv().contextID() != surfaceContext->priv().contextID()) {
309 return false;
310 }
311 GrSurfaceProxyView srcView = fDevice->readSurfaceView();
312 if (!srcView.asTextureProxyRef()) {
313 return false;
314 }
315 // Possibly we could skip making an image here if SkGpuDevice exposed a lower level way
316 // of drawing a texture proxy.
317 const SkImageInfo info = fDevice->imageInfo();
318 sk_sp<SkImage> image = sk_make_sp<SkImage_Ganesh>(args: sk_ref_sp(obj: canvasContext),
319 args: kNeedNewImageUniqueID,
320 args: std::move(srcView),
321 args: info.colorInfo());
322 canvas->drawImage(image.get(), x, y, sampling, paint);
323 return true;
324 };
325 if (!tryDraw()) {
326 INHERITED::onDraw(canvas, x, y, sampling, paint);
327 }
328}
329
330bool SkSurface_Ganesh::onIsCompatible(const GrSurfaceCharacterization& characterization) const {
331 auto direct = fDevice->recordingContext()->asDirectContext();
332 if (!direct) {
333 return false;
334 }
335
336 if (!characterization.isValid()) {
337 return false;
338 }
339
340 if (characterization.vulkanSecondaryCBCompatible()) {
341 return false;
342 }
343
344 SkImageInfo ii = fDevice->imageInfo();
345 if (ii.colorType() == kUnknown_SkColorType) {
346 return false;
347 }
348
349 GrSurfaceProxyView targetView = fDevice->readSurfaceView();
350 // As long as the current state if the context allows for greater or equal resources,
351 // we allow the DDL to be replayed.
352 // DDL TODO: should we just remove the resource check and ignore the cache limits on playback?
353 size_t maxResourceBytes = direct->getResourceCacheLimit();
354
355 if (characterization.isTextureable()) {
356 if (!targetView.asTextureProxy()) {
357 // If the characterization was textureable we require the replay dest to also be
358 // textureable. If the characterized surface wasn't textureable we allow the replay
359 // dest to be textureable.
360 return false;
361 }
362
363 if (characterization.isMipMapped() &&
364 GrMipmapped::kNo == targetView.asTextureProxy()->mipmapped()) {
365 // Fail if the DDL's surface was mipmapped but the replay surface is not.
366 // Allow drawing to proceed if the DDL was not mipmapped but the replay surface is.
367 return false;
368 }
369 }
370
371 if (characterization.usesGLFBO0() != targetView.asRenderTargetProxy()->glRTFBOIDIs0()) {
372 // FBO0-ness effects how MSAA and window rectangles work. If the characterization was
373 // tagged as FBO0 it would never have been allowed to use window rectangles. If MSAA
374 // was also never used then a DDL recorded with this characterization should be replayable
375 // on a non-FBO0 surface.
376 if (!characterization.usesGLFBO0() || characterization.sampleCount() > 1) {
377 return false;
378 }
379 }
380
381 GrBackendFormat format = targetView.asRenderTargetProxy()->backendFormat();
382 int numSamples = targetView.asRenderTargetProxy()->numSamples();
383 GrProtected isProtected = targetView.proxy()->isProtected();
384
385 return characterization.contextInfo() &&
386 characterization.contextInfo()->priv().matches(candidate: direct) &&
387 characterization.cacheMaxResourceBytes() <= maxResourceBytes &&
388 characterization.origin() == targetView.origin() &&
389 characterization.backendFormat() == format && characterization.width() == ii.width() &&
390 characterization.height() == ii.height() &&
391 characterization.colorType() == ii.colorType() &&
392 characterization.sampleCount() == numSamples &&
393 SkColorSpace::Equals(characterization.colorSpace(), ii.colorInfo().colorSpace()) &&
394 characterization.isProtected() == isProtected &&
395 characterization.surfaceProps() == fDevice->surfaceProps();
396}
397
398bool SkSurface_Ganesh::draw(sk_sp<const GrDeferredDisplayList> ddl) {
399 if (!ddl || !this->isCompatible(characterization: ddl->characterization())) {
400 return false;
401 }
402
403 auto direct = fDevice->recordingContext()->asDirectContext();
404 if (!direct || direct->abandoned()) {
405 return false;
406 }
407
408 GrSurfaceProxyView view = fDevice->readSurfaceView();
409
410 direct->priv().createDDLTask(std::move(ddl), newDest: view.asRenderTargetProxyRef());
411 return true;
412}
413
414sk_sp<const SkCapabilities> SkSurface_Ganesh::onCapabilities() {
415 return fDevice->recordingContext()->skCapabilities();
416}
417
418///////////////////////////////////////////////////////////////////////////////
419
420static bool validate_backend_texture(const GrCaps* caps,
421 const GrBackendTexture& tex,
422 int sampleCnt,
423 GrColorType grCT,
424 bool texturable) {
425 if (!tex.isValid()) {
426 return false;
427 }
428
429 GrBackendFormat backendFormat = tex.getBackendFormat();
430 if (!backendFormat.isValid()) {
431 RENDERENGINE_ABORTF("%s failed due to an invalid format", __func__);
432 return false;
433 }
434
435 if (!caps->areColorTypeAndFormatCompatible(grCT, format: backendFormat)) {
436 RENDERENGINE_ABORTF("%s failed due to an invalid format and colorType combination",
437 __func__);
438 return false;
439 }
440
441 if (!caps->isFormatAsColorTypeRenderable(ct: grCT, format: backendFormat, sampleCount: sampleCnt)) {
442 RENDERENGINE_ABORTF(
443 "%s failed due to no supported rendering path for the selected "
444 "format and colorType",
445 __func__);
446 return false;
447 }
448
449 if (texturable && !caps->isFormatTexturable(backendFormat, tex.textureType())) {
450 RENDERENGINE_ABORTF(
451 "%s failed due to no texturing support for the selected format and "
452 "colorType",
453 __func__);
454 return false;
455 }
456
457 return true;
458}
459
460bool SkSurface_Ganesh::replaceBackendTexture(const GrBackendTexture& backendTexture,
461 GrSurfaceOrigin origin,
462 ContentChangeMode mode,
463 TextureReleaseProc releaseProc,
464 ReleaseContext releaseContext) {
465 auto releaseHelper = skgpu::RefCntedCallback::Make(proc: releaseProc, ctx: releaseContext);
466
467 auto rContext = fDevice->recordingContext();
468 if (rContext->abandoned()) {
469 return false;
470 }
471 if (!backendTexture.isValid()) {
472 return false;
473 }
474 if (backendTexture.width() != this->width() || backendTexture.height() != this->height()) {
475 return false;
476 }
477 auto* oldRTP = fDevice->targetProxy();
478 auto oldProxy = sk_ref_sp(obj: oldRTP->asTextureProxy());
479 if (!oldProxy) {
480 return false;
481 }
482 auto* oldTexture = oldProxy->peekTexture();
483 if (!oldTexture) {
484 return false;
485 }
486 if (!oldTexture->resourcePriv().refsWrappedObjects()) {
487 return false;
488 }
489 if (oldTexture->backendFormat() != backendTexture.getBackendFormat()) {
490 return false;
491 }
492 if (oldTexture->getBackendTexture().isSameTexture(backendTexture)) {
493 return false;
494 }
495 SkASSERT(oldTexture->asRenderTarget());
496 int sampleCnt = oldTexture->asRenderTarget()->numSamples();
497 GrColorType grColorType = SkColorTypeToGrColorType(ct: this->getCanvas()->imageInfo().colorType());
498 if (!validate_backend_texture(
499 caps: rContext->priv().caps(), tex: backendTexture, sampleCnt, grCT: grColorType, texturable: true)) {
500 return false;
501 }
502
503 sk_sp<SkColorSpace> colorSpace = fDevice->imageInfo().refColorSpace();
504
505 SkASSERT(sampleCnt > 0);
506 sk_sp<GrTextureProxy> proxy(rContext->priv().proxyProvider()->wrapRenderableBackendTexture(
507 backendTexture,
508 sampleCnt,
509 kBorrow_GrWrapOwnership,
510 GrWrapCacheable::kNo,
511 releaseHelper: std::move(releaseHelper)));
512 if (!proxy) {
513 return false;
514 }
515
516 return fDevice->replaceBackingProxy(mode,
517 sk_ref_sp(obj: proxy->asRenderTargetProxy()),
518 grColorType,
519 std::move(colorSpace),
520 origin,
521 this->props());
522}
523
524bool validate_backend_render_target(const GrCaps* caps,
525 const GrBackendRenderTarget& rt,
526 GrColorType grCT) {
527 if (!caps->areColorTypeAndFormatCompatible(grCT, format: rt.getBackendFormat())) {
528 return false;
529 }
530
531 if (!caps->isFormatAsColorTypeRenderable(ct: grCT, format: rt.getBackendFormat(), sampleCount: rt.sampleCnt())) {
532 return false;
533 }
534
535 // We require the stencil bits to be either 0, 8, or 16.
536 int stencilBits = rt.stencilBits();
537 if (stencilBits != 0 && stencilBits != 8 && stencilBits != 16) {
538 return false;
539 }
540
541 return true;
542}
543
544namespace SkSurfaces {
545sk_sp<SkSurface> RenderTarget(GrRecordingContext* rContext,
546 const GrSurfaceCharacterization& c,
547 skgpu::Budgeted budgeted) {
548 if (!rContext || !c.isValid()) {
549 return nullptr;
550 }
551
552 if (c.usesGLFBO0()) {
553 // If we are making the surface we will never use FBO0.
554 return nullptr;
555 }
556
557 if (c.vulkanSecondaryCBCompatible()) {
558 return nullptr;
559 }
560
561 auto device = rContext->priv().createDevice(budgeted,
562 c.imageInfo(),
563 SkBackingFit::kExact,
564 sampleCount: c.sampleCount(),
565 GrMipmapped(c.isMipMapped()),
566 c.isProtected(),
567 c.origin(),
568 c.surfaceProps(),
569 skgpu::ganesh::Device::InitContents::kClear);
570 if (!device) {
571 return nullptr;
572 }
573
574 sk_sp<SkSurface> result = sk_make_sp<SkSurface_Ganesh>(args: std::move(device));
575#ifdef SK_DEBUG
576 if (result) {
577 SkASSERT(result->isCompatible(c));
578 }
579#endif
580
581 return result;
582}
583
584sk_sp<SkSurface> RenderTarget(GrRecordingContext* rContext,
585 skgpu::Budgeted budgeted,
586 const SkImageInfo& info,
587 int sampleCount,
588 GrSurfaceOrigin origin,
589 const SkSurfaceProps* props,
590 bool shouldCreateWithMips) {
591 if (!rContext) {
592 return nullptr;
593 }
594 sampleCount = std::max(a: 1, b: sampleCount);
595 GrMipmapped mipmapped = shouldCreateWithMips ? GrMipmapped::kYes : GrMipmapped::kNo;
596
597 if (!rContext->priv().caps()->mipmapSupport()) {
598 mipmapped = GrMipmapped::kNo;
599 }
600
601 auto device = rContext->priv().createDevice(budgeted,
602 info,
603 SkBackingFit::kExact,
604 sampleCount,
605 mipmapped,
606 GrProtected::kNo,
607 origin,
608 SkSurfacePropsCopyOrDefault(props),
609 skgpu::ganesh::Device::InitContents::kClear);
610 if (!device) {
611 return nullptr;
612 }
613 return sk_make_sp<SkSurface_Ganesh>(args: std::move(device));
614}
615
616sk_sp<SkSurface> WrapBackendTexture(GrRecordingContext* rContext,
617 const GrBackendTexture& tex,
618 GrSurfaceOrigin origin,
619 int sampleCnt,
620 SkColorType colorType,
621 sk_sp<SkColorSpace> colorSpace,
622 const SkSurfaceProps* props,
623 TextureReleaseProc textureReleaseProc,
624 ReleaseContext releaseContext) {
625 auto releaseHelper = skgpu::RefCntedCallback::Make(proc: textureReleaseProc, ctx: releaseContext);
626
627 if (!rContext) {
628 RENDERENGINE_ABORTF("%s failed due to a null context ", __func__);
629 return nullptr;
630 }
631 sampleCnt = std::max(a: 1, b: sampleCnt);
632
633 GrColorType grColorType = SkColorTypeToGrColorType(ct: colorType);
634 if (grColorType == GrColorType::kUnknown) {
635 RENDERENGINE_ABORTF("%s failed due to an unsupported colorType %d", __func__, colorType);
636 return nullptr;
637 }
638
639 if (!validate_backend_texture(caps: rContext->priv().caps(), tex, sampleCnt, grCT: grColorType, texturable: true)) {
640 return nullptr;
641 }
642
643 sk_sp<GrTextureProxy> proxy(rContext->priv().proxyProvider()->wrapRenderableBackendTexture(
644 tex,
645 sampleCnt,
646 kBorrow_GrWrapOwnership,
647 GrWrapCacheable::kNo,
648 releaseHelper: std::move(releaseHelper)));
649 if (!proxy) {
650#ifdef SK_IN_RENDERENGINE
651 GrGLTextureInfo textureInfo;
652 bool retrievedTextureInfo = tex.getGLTextureInfo(&textureInfo);
653 RENDERENGINE_ABORTF(
654 "%s failed to wrap the texture into a renderable target "
655 "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i texType: %i"
656 "\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u"
657 "\n\tmaxRenderTargetSize: %d",
658 __func__,
659 tex.width(),
660 tex.height(),
661 tex.hasMipmaps(),
662 tex.isProtected(),
663 static_cast<int>(tex.textureType()),
664 retrievedTextureInfo,
665 textureInfo.fTarget,
666 textureInfo.fFormat,
667 rContext->priv().caps()->maxRenderTargetSize());
668#endif
669 return nullptr;
670 }
671
672 auto device = rContext->priv().createDevice(grColorType,
673 std::move(proxy),
674 std::move(colorSpace),
675 origin,
676 SkSurfacePropsCopyOrDefault(props),
677 skgpu::ganesh::Device::InitContents::kUninit);
678 if (!device) {
679 RENDERENGINE_ABORTF("%s failed to wrap the renderTarget into a surface", __func__);
680 return nullptr;
681 }
682
683 return sk_make_sp<SkSurface_Ganesh>(args: std::move(device));
684}
685
686sk_sp<SkSurface> WrapBackendRenderTarget(GrRecordingContext* rContext,
687 const GrBackendRenderTarget& rt,
688 GrSurfaceOrigin origin,
689 SkColorType colorType,
690 sk_sp<SkColorSpace> colorSpace,
691 const SkSurfaceProps* props,
692 RenderTargetReleaseProc relProc,
693 ReleaseContext releaseContext) {
694 auto releaseHelper = skgpu::RefCntedCallback::Make(proc: relProc, ctx: releaseContext);
695
696 if (!rContext) {
697 return nullptr;
698 }
699
700 GrColorType grColorType = SkColorTypeToGrColorType(ct: colorType);
701 if (grColorType == GrColorType::kUnknown) {
702 return nullptr;
703 }
704
705 if (!validate_backend_render_target(caps: rContext->priv().caps(), rt, grCT: grColorType)) {
706 return nullptr;
707 }
708
709 auto proxyProvider = rContext->priv().proxyProvider();
710 auto proxy = proxyProvider->wrapBackendRenderTarget(rt, releaseHelper: std::move(releaseHelper));
711 if (!proxy) {
712 return nullptr;
713 }
714
715 auto device = rContext->priv().createDevice(grColorType,
716 std::move(proxy),
717 std::move(colorSpace),
718 origin,
719 SkSurfacePropsCopyOrDefault(props),
720 skgpu::ganesh::Device::InitContents::kUninit);
721 if (!device) {
722 return nullptr;
723 }
724
725 return sk_make_sp<SkSurface_Ganesh>(args: std::move(device));
726}
727
728GrBackendTexture GetBackendTexture(SkSurface* surface, BackendHandleAccess access) {
729 if (surface == nullptr) {
730 return GrBackendTexture();
731 }
732 auto sb = asSB(surface);
733 if (!sb->isGaneshBacked()) {
734 return GrBackendTexture();
735 }
736 return static_cast<SkSurface_Ganesh*>(surface)->getBackendTexture(access);
737}
738
739GrBackendRenderTarget GetBackendRenderTarget(SkSurface* surface, BackendHandleAccess access) {
740 if (surface == nullptr) {
741 return GrBackendRenderTarget();
742 }
743 auto sb = asSB(surface);
744 if (!sb->isGaneshBacked()) {
745 return GrBackendRenderTarget();
746 }
747 return static_cast<SkSurface_Ganesh*>(surface)->getBackendRenderTarget(access);
748}
749
750void ResolveMSAA(SkSurface* surface) {
751 if (!surface) {
752 return;
753 }
754 auto sb = asSB(surface);
755 if (!sb->isGaneshBacked()) {
756 return;
757 }
758 auto gs = static_cast<SkSurface_Ganesh*>(surface);
759 gs->resolveMSAA();
760}
761
762} // namespace SkSurfaces
763
764namespace skgpu::ganesh {
765GrSemaphoresSubmitted Flush(SkSurface* surface) {
766 if (!surface) {
767 return GrSemaphoresSubmitted::kNo;
768 }
769 if (auto rContext = surface->recordingContext(); rContext != nullptr) {
770 return rContext->asDirectContext()->flush(surface, info: {});
771 }
772 return GrSemaphoresSubmitted::kNo;
773}
774
775GrSemaphoresSubmitted Flush(sk_sp<SkSurface> surface) {
776 if (!surface) {
777 return GrSemaphoresSubmitted::kNo;
778 }
779 if (auto rContext = surface->recordingContext(); rContext != nullptr) {
780 return rContext->asDirectContext()->flush(surface, info: {});
781 }
782 return GrSemaphoresSubmitted::kNo;
783}
784
785void FlushAndSubmit(SkSurface* surface) {
786 if (!surface) {
787 return;
788 }
789 if (auto rContext = surface->recordingContext(); rContext != nullptr) {
790 rContext->asDirectContext()->flushAndSubmit(syncCpu: surface);
791 }
792}
793
794void FlushAndSubmit(sk_sp<SkSurface> surface) {
795 if (!surface) {
796 return;
797 }
798 if (auto rContext = surface->recordingContext(); rContext != nullptr) {
799 rContext->asDirectContext()->flushAndSubmit(surface);
800 }
801}
802
803} // namespace skgpu::ganesh
804

source code of flutter_engine/third_party/skia/src/gpu/ganesh/surface/SkSurface_Ganesh.cpp