Skip to content

Commit be5a093

Browse files
bsalomonSkia Commit-Bot
authored andcommitted
Consolidate details of lazy proxy creation for promise image textures.
The SkSpecialImage helper is now function-local and *is* the lazy instantiation callback rather than coordinating with one in the promise SkImage factory functions. Bug: skia: Change-Id: I4883618ef440d09a9ac7959cef254e6f5a49aa55 Reviewed-on: https://skia-review.googlesource.com/c/175822 Commit-Queue: Brian Salomon <bsalomon@google.com> Reviewed-by: Greg Daniel <egdaniel@google.com>
1 parent 0c75727 commit be5a093

File tree

5 files changed

+184
-205
lines changed

5 files changed

+184
-205
lines changed

src/core/SkScopeExit.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class SkScopeExit {
2525
}
2626
}
2727

28+
void clear() { fFn = {}; }
29+
2830
SkScopeExit& operator=(SkScopeExit&& that) {
2931
fFn = std::move(that.fFn);
3032
return *this;

src/image/SkImage_Gpu.cpp

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "SkImageInfoPriv.h"
3939
#include "SkImage_Gpu.h"
4040
#include "SkMipMap.h"
41+
#include "SkScopeExit.h"
4142
#include "SkTraceEvent.h"
4243
#include "effects/GrYUVtoRGBEffect.h"
4344
#include "gl/GrGLTexture.h"
@@ -329,68 +330,39 @@ sk_sp<SkImage> SkImage_Gpu::MakePromiseTexture(GrContext* context,
329330
PromiseDoneProc promiseDoneProc,
330331
TextureContext textureContext) {
331332
// The contract here is that if 'promiseDoneProc' is passed in it should always be called,
332-
// even if creation of the SkImage fails.
333+
// even if creation of the SkImage fails. Once we call MakePromiseImageLazyProxy it takes
334+
// responsibility for calling the done proc.
333335
if (!promiseDoneProc) {
334336
return nullptr;
335337
}
338+
SkScopeExit callDone([promiseDoneProc, textureContext]() { promiseDoneProc(textureContext); });
336339

337-
SkPromiseImageHelper promiseHelper(textureFulfillProc, textureReleaseProc, promiseDoneProc,
338-
textureContext);
339-
340-
if (!context) {
340+
SkImageInfo info = SkImageInfo::Make(width, height, colorType, alphaType, colorSpace);
341+
if (!SkImageInfoIsValid(info)) {
341342
return nullptr;
342343
}
343344

344-
if (width <= 0 || height <= 0) {
345+
if (!context) {
345346
return nullptr;
346347
}
347348

348-
if (!textureFulfillProc || !textureReleaseProc) {
349+
if (width <= 0 || height <= 0) {
349350
return nullptr;
350351
}
351352

352-
SkImageInfo info = SkImageInfo::Make(width, height, colorType, alphaType, colorSpace);
353-
if (!SkImageInfoIsValid(info)) {
354-
return nullptr;
355-
}
356353
GrPixelConfig config = kUnknown_GrPixelConfig;
357354
if (!context->contextPriv().caps()->getConfigFromBackendFormat(backendFormat, colorType,
358355
&config)) {
359356
return nullptr;
360357
}
361358

362-
if (mipMapped == GrMipMapped::kYes &&
363-
GrTextureTypeHasRestrictedSampling(backendFormat.textureType())) {
364-
// It is invalid to have a GL_TEXTURE_EXTERNAL or GL_TEXTURE_RECTANGLE and have mips as
365-
// well.
366-
return nullptr;
367-
}
368-
369-
GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
370-
371-
GrSurfaceDesc desc;
372-
desc.fWidth = width;
373-
desc.fHeight = height;
374-
desc.fConfig = config;
375-
376-
// We pass kReadOnly here since we should treat content of the client's texture as immutable.
377-
sk_sp<GrTextureProxy> proxy = proxyProvider->createLazyProxy(
378-
[promiseHelper, config](GrResourceProvider* resourceProvider) mutable {
379-
if (!resourceProvider) {
380-
promiseHelper.reset();
381-
return sk_sp<GrTexture>();
382-
}
383-
384-
return promiseHelper.getTexture(resourceProvider, config);
385-
},
386-
backendFormat, desc, origin, mipMapped, GrInternalSurfaceFlags::kReadOnly,
387-
SkBackingFit::kExact, SkBudgeted::kNo,
388-
GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
389-
359+
callDone.clear();
360+
auto proxy = MakePromiseImageLazyProxy(context, width, height, origin, config, backendFormat,
361+
mipMapped, textureFulfillProc, textureReleaseProc,
362+
promiseDoneProc, textureContext);
390363
if (!proxy) {
391364
return nullptr;
392365
}
393-
394366
return sk_make_sp<SkImage_Gpu>(sk_ref_sp(context), kNeedNewImageUniqueID, alphaType,
395367
std::move(proxy), std::move(colorSpace), SkBudgeted::kNo);
396368
}

src/image/SkImage_GpuBase.cpp

Lines changed: 142 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -381,53 +381,151 @@ bool SkImage_GpuBase::RenderYUVAToRGBA(GrContext* ctx, GrRenderTargetContext* re
381381
return true;
382382
}
383383

384-
/////////////////////////////////////////////////////////////////////////////////////////////////
385-
sk_sp<GrTexture> SkPromiseImageHelper::getTexture(GrResourceProvider* resourceProvider,
386-
GrPixelConfig config) {
387-
// Releases the promise helper if there are no outstanding hard refs. This means that we
388-
// don't have any ReleaseProcs waiting to be called so we will need to do a fulfill.
389-
if (fReleaseHelper && fReleaseHelper->weak_expired()) {
390-
this->resetReleaseHelper();
391-
}
392-
393-
sk_sp<GrTexture> tex;
394-
if (!fReleaseHelper) {
395-
fFulfillProc(fContext, &fBackendTex);
396-
fBackendTex.fConfig = config;
397-
if (!fBackendTex.isValid()) {
398-
// Even though the GrBackendTexture is not valid, we must call the release
399-
// proc to keep our contract of always calling Fulfill and Release in pairs.
400-
fReleaseProc(fContext);
401-
return sk_sp<GrTexture>();
384+
sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
385+
GrContext* context, int width, int height, GrSurfaceOrigin origin, GrPixelConfig config,
386+
GrBackendFormat backendFormat, GrMipMapped mipMapped,
387+
SkImage_GpuBase::TextureFulfillProc fulfillProc,
388+
SkImage_GpuBase::TextureReleaseProc releaseProc, SkImage_GpuBase::PromiseDoneProc doneProc,
389+
SkImage_GpuBase::TextureContext textureContext) {
390+
SkASSERT(context);
391+
SkASSERT(width > 0 && height > 0);
392+
SkASSERT(doneProc);
393+
394+
if (!fulfillProc || !releaseProc) {
395+
doneProc(textureContext);
396+
return nullptr;
397+
}
398+
399+
if (mipMapped == GrMipMapped::kYes &&
400+
GrTextureTypeHasRestrictedSampling(backendFormat.textureType())) {
401+
// It is invalid to have a GL_TEXTURE_EXTERNAL or GL_TEXTURE_RECTANGLE and have mips as
402+
// well.
403+
doneProc(textureContext);
404+
return nullptr;
405+
}
406+
407+
/**
408+
* This helper class manages the ref counting for the the ReleaseProc and DoneProc for promise
409+
* images. It holds a weak ref on the ReleaseProc (hard refs are owned by GrTextures). The weak
410+
* ref allows us to reuse an outstanding ReleaseProc (because we dropped our GrTexture but the
411+
* GrTexture isn't done on the GPU) without needing to call FulfillProc again. It also holds a
412+
* hard ref on the DoneProc. The idea is that after every flush we may call the ReleaseProc so
413+
* that the client can free up their GPU memory if they want to. The life time of the DoneProc
414+
* matches that of any outstanding ReleaseProc as well as the PromiseLazyInstantiateCallback.
415+
* Thus we won't call the DoneProc until all ReleaseProcs are finished and we are finished with
416+
* the PromiseImageHelper (i.e. won't call FulfillProc again).
417+
*/
418+
class PromiseLazyInstantiateCallback {
419+
public:
420+
PromiseLazyInstantiateCallback(SkImage_GpuBase::TextureFulfillProc fulfillProc,
421+
SkImage_GpuBase::TextureReleaseProc releaseProc,
422+
SkImage_GpuBase::PromiseDoneProc doneProc,
423+
SkImage_GpuBase::TextureContext context,
424+
GrPixelConfig config)
425+
: fFulfillProc(fulfillProc)
426+
, fReleaseProc(releaseProc)
427+
, fContext(context)
428+
, fConfig(config) {
429+
fDoneHelper.reset(new GrReleaseProcHelper(doneProc, context));
430+
}
431+
432+
sk_sp<GrSurface> operator()(GrResourceProvider* resourceProvider) {
433+
if (!resourceProvider) {
434+
this->reset();
435+
return sk_sp<GrTexture>();
436+
}
437+
438+
// Releases the promise helper if there are no outstanding hard refs. This means that we
439+
// don't have any ReleaseProcs waiting to be called so we will need to do a fulfill.
440+
if (fReleaseHelper && fReleaseHelper->weak_expired()) {
441+
this->resetReleaseHelper();
442+
}
443+
444+
sk_sp<GrTexture> tex;
445+
if (!fReleaseHelper) {
446+
fFulfillProc(fContext, &fBackendTex);
447+
fBackendTex.fConfig = fConfig;
448+
if (!fBackendTex.isValid()) {
449+
// Even though the GrBackendTexture is not valid, we must call the release
450+
// proc to keep our contract of always calling Fulfill and Release in pairs.
451+
fReleaseProc(fContext);
452+
return sk_sp<GrTexture>();
453+
}
454+
455+
tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership,
456+
kRead_GrIOType);
457+
if (!tex) {
458+
// Even though the GrBackendTexture is not valid, we must call the release
459+
// proc to keep our contract of always calling Fulfill and Release in pairs.
460+
fReleaseProc(fContext);
461+
return sk_sp<GrTexture>();
462+
}
463+
fReleaseHelper =
464+
new SkPromiseReleaseProcHelper(fReleaseProc, fContext, fDoneHelper);
465+
// Take a weak ref
466+
fReleaseHelper->weak_ref();
467+
} else {
468+
SkASSERT(fBackendTex.isValid());
469+
tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership,
470+
kRead_GrIOType);
471+
if (!tex) {
472+
// We weren't able to make a texture here, but since we are in this branch
473+
// of the calls (promiseHelper.fReleaseHelper is valid) there is already a
474+
// texture out there which will call the release proc so we don't need to
475+
// call it here.
476+
return sk_sp<GrTexture>();
477+
}
478+
479+
SkAssertResult(fReleaseHelper->try_ref());
480+
}
481+
SkASSERT(tex);
482+
// Pass the hard ref off to the texture
483+
tex->setRelease(sk_sp<GrReleaseProcHelper>(fReleaseHelper));
484+
return std::move(tex);
402485
}
403486

404-
tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership,
405-
kRead_GrIOType);
406-
if (!tex) {
407-
// Even though the GrBackendTexture is not valid, we must call the release
408-
// proc to keep our contract of always calling Fulfill and Release in pairs.
409-
fReleaseProc(fContext);
410-
return sk_sp<GrTexture>();
487+
private:
488+
void reset() {
489+
this->resetReleaseHelper();
490+
fDoneHelper.reset();
411491
}
412-
fReleaseHelper = new SkPromiseReleaseProcHelper(fReleaseProc, fContext, fDoneHelper);
413-
// Take a weak ref
414-
fReleaseHelper->weak_ref();
415-
} else {
416-
SkASSERT(fBackendTex.isValid());
417-
tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership,
418-
kRead_GrIOType);
419-
if (!tex) {
420-
// We weren't able to make a texture here, but since we are in this branch
421-
// of the calls (promiseHelper.fReleaseHelper is valid) there is already a
422-
// texture out there which will call the release proc so we don't need to
423-
// call it here.
424-
return sk_sp<GrTexture>();
492+
493+
// Weak unrefs fReleaseHelper and sets it to null
494+
void resetReleaseHelper() {
495+
if (fReleaseHelper) {
496+
fReleaseHelper->weak_unref();
497+
fReleaseHelper = nullptr;
498+
}
425499
}
426500

427-
SkAssertResult(fReleaseHelper->try_ref());
428-
}
429-
SkASSERT(tex);
430-
// Pass the hard ref off to the texture
431-
tex->setRelease(sk_sp<GrReleaseProcHelper>(fReleaseHelper));
432-
return tex;
501+
SkImage_GpuBase::TextureFulfillProc fFulfillProc;
502+
SkImage_GpuBase::TextureReleaseProc fReleaseProc;
503+
SkImage_GpuBase::TextureContext fContext;
504+
505+
GrPixelConfig fConfig;
506+
// We cache the GrBackendTexture so that if we deleted the GrTexture but the the release
507+
// proc has yet not been called (this can happen on Vulkan), then we can create a new
508+
// texture without needing to call the fulfill proc again.
509+
GrBackendTexture fBackendTex;
510+
// The fReleaseHelper is used to track a weak ref on the release proc. This helps us make
511+
// sure we are always pairing fulfill and release proc calls correctly.
512+
SkPromiseReleaseProcHelper* fReleaseHelper = nullptr;
513+
// We don't want to call the fDoneHelper until we are done with the PromiseImageHelper and
514+
// all ReleaseHelpers are finished. Thus we hold a hard ref here and we will pass a hard ref
515+
// to each fReleaseHelper we make.
516+
sk_sp<GrReleaseProcHelper> fDoneHelper;
517+
} callback(fulfillProc, releaseProc, doneProc, textureContext, config);
518+
519+
GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
520+
521+
GrSurfaceDesc desc;
522+
desc.fWidth = width;
523+
desc.fHeight = height;
524+
desc.fConfig = config;
525+
526+
// We pass kReadOnly here since we should treat content of the client's texture as immutable.
527+
return proxyProvider->createLazyProxy(std::move(callback), backendFormat, desc, origin,
528+
mipMapped, GrInternalSurfaceFlags::kReadOnly,
529+
SkBackingFit::kExact, SkBudgeted::kNo,
530+
GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
433531
}

src/image/SkImage_GpuBase.h

Lines changed: 9 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ class SkImage_GpuBase : public SkImage_Base {
7777
typedef void(*PromiseDoneProc)(TextureContext textureContext);
7878

7979
protected:
80+
// Helper for making a lazy proxy for a promise image. The PromiseDoneProc we be called,
81+
// if not null, immediately if this function fails. Othwerwise, it is installed in the
82+
// proxy along with the TextureFulfillProc and TextureReleaseProc. PromiseDoneProc must not
83+
// be null.
84+
static sk_sp<GrTextureProxy> MakePromiseImageLazyProxy(
85+
GrContext*, int width, int height, GrSurfaceOrigin, GrPixelConfig, GrBackendFormat,
86+
GrMipMapped, SkImage_GpuBase::TextureFulfillProc, SkImage_GpuBase::TextureReleaseProc,
87+
SkImage_GpuBase::PromiseDoneProc, SkImage_GpuBase::TextureContext);
88+
8089
static bool RenderYUVAToRGBA(GrContext* ctx, GrRenderTargetContext* renderTargetContext,
8190
const SkRect& rect, SkYUVColorSpace yuvColorSpace,
8291
const sk_sp<GrTextureProxy> proxies[4],
@@ -118,79 +127,4 @@ class SkPromiseReleaseProcHelper : public GrReleaseProcHelper {
118127
typedef GrReleaseProcHelper INHERITED;
119128
};
120129

121-
/**
122-
* This helper class manages the ref counting for the the ReleaseProc and DoneProc for promise
123-
* images. It holds a weak ref on the ReleaseProc (hard refs are owned by GrTextures). The weak ref
124-
* allows us to reuse an outstanding ReleaseProc (because we dropped our GrTexture but the GrTexture
125-
* isn't done on the GPU) without needing to call FulfillProc again. It also holds a hard ref on the
126-
* DoneProc. The idea is that after every flush we may call the ReleaseProc so that the client can
127-
* free up their GPU memory if they want to. The life time of the DoneProc matches that of any
128-
* outstanding ReleaseProc as well as the PromiseImageHelper. Thus we won't call the DoneProc until
129-
* all ReleaseProcs are finished and we are finished with the PromiseImageHelper (i.e. won't call
130-
* FulfillProc again).
131-
*/
132-
class SkPromiseImageHelper {
133-
public:
134-
SkPromiseImageHelper()
135-
: fFulfillProc(nullptr)
136-
, fReleaseProc(nullptr)
137-
, fContext(nullptr)
138-
, fDoneHelper(nullptr) {
139-
}
140-
141-
void set(SkImage_GpuBase::TextureFulfillProc fulfillProc,
142-
SkImage_GpuBase::TextureReleaseProc releaseProc,
143-
SkImage_GpuBase::PromiseDoneProc doneProc,
144-
SkImage_GpuBase::TextureContext context) {
145-
fFulfillProc = fulfillProc;
146-
fReleaseProc = releaseProc;
147-
fContext = context;
148-
fDoneHelper.reset(new GrReleaseProcHelper(doneProc, context));
149-
}
150-
151-
SkPromiseImageHelper(SkImage_GpuBase::TextureFulfillProc fulfillProc,
152-
SkImage_GpuBase::TextureReleaseProc releaseProc,
153-
SkImage_GpuBase::PromiseDoneProc doneProc,
154-
SkImage_GpuBase::TextureContext context)
155-
: fFulfillProc(fulfillProc)
156-
, fReleaseProc(releaseProc)
157-
, fContext(context)
158-
, fDoneHelper(new GrReleaseProcHelper(doneProc, context)) {
159-
}
160-
161-
bool isValid() { return SkToBool(fDoneHelper); }
162-
163-
void reset() {
164-
this->resetReleaseHelper();
165-
fDoneHelper.reset();
166-
}
167-
168-
sk_sp<GrTexture> getTexture(GrResourceProvider* resourceProvider, GrPixelConfig config);
169-
170-
private:
171-
// Weak unrefs fReleaseHelper and sets it to null
172-
void resetReleaseHelper() {
173-
if (fReleaseHelper) {
174-
fReleaseHelper->weak_unref();
175-
fReleaseHelper = nullptr;
176-
}
177-
}
178-
179-
SkImage_GpuBase::TextureFulfillProc fFulfillProc;
180-
SkImage_GpuBase::TextureReleaseProc fReleaseProc;
181-
SkImage_GpuBase::TextureContext fContext;
182-
183-
// We cache the GrBackendTexture so that if we deleted the GrTexture but the the release proc
184-
// has yet not been called (this can happen on Vulkan), then we can create a new texture without
185-
// needing to call the fulfill proc again.
186-
GrBackendTexture fBackendTex;
187-
// The fReleaseHelper is used to track a weak ref on the release proc. This helps us make sure
188-
// we are always pairing fulfill and release proc calls correctly.
189-
SkPromiseReleaseProcHelper* fReleaseHelper = nullptr;
190-
// We don't want to call the fDoneHelper until we are done with the PromiseImageHelper and all
191-
// ReleaseHelpers are finished. Thus we hold a hard ref here and we will pass a hard ref to each
192-
// fReleaseHelper we make.
193-
sk_sp<GrReleaseProcHelper> fDoneHelper;
194-
};
195-
196130
#endif

0 commit comments

Comments
 (0)