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 "SkSurface_Gpu.h"
9
10#include "GrBackendSurface.h"
11#include "GrContextPriv.h"
12#include "GrRenderTarget.h"
13#include "GrRenderTargetContextPriv.h"
14#include "GrTexture.h"
15
16#include "SkCanvas.h"
17#include "SkDeferredDisplayList.h"
18#include "SkGpuDevice.h"
19#include "SkImage_Base.h"
20#include "SkImage_Gpu.h"
21#include "SkImagePriv.h"
22#include "SkSurface_Base.h"
23#include "SkSurfaceCharacterization.h"
24
25#if SK_SUPPORT_GPU
26
27SkSurface_Gpu::SkSurface_Gpu(sk_sp<SkGpuDevice> device)
28    : INHERITED(device->width(), device->height(), &device->surfaceProps())
29    , fDevice(std::move(device)) {
30    SkASSERT(fDevice->accessRenderTargetContext()->asSurfaceProxy()->priv().isExact());
31}
32
33SkSurface_Gpu::~SkSurface_Gpu() {
34}
35
36static GrRenderTarget* prepare_rt_for_external_access(SkSurface_Gpu* surface,
37                                                      SkSurface::BackendHandleAccess access) {
38    switch (access) {
39        case SkSurface::kFlushRead_BackendHandleAccess:
40            break;
41        case SkSurface::kFlushWrite_BackendHandleAccess:
42        case SkSurface::kDiscardWrite_BackendHandleAccess:
43            // for now we don't special-case on Discard, but we may in the future.
44            surface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
45            break;
46    }
47
48    // Grab the render target *after* firing notifications, as it may get switched if CoW kicks in.
49    surface->getDevice()->flush();
50    GrRenderTargetContext* rtc = surface->getDevice()->accessRenderTargetContext();
51    return rtc->accessRenderTarget();
52}
53
54GrBackendObject SkSurface_Gpu::onGetTextureHandle(BackendHandleAccess access) {
55    GrRenderTarget* rt = prepare_rt_for_external_access(this, access);
56    if (!rt) {
57        return 0;
58    }
59    GrTexture* texture = rt->asTexture();
60    if (texture) {
61        return texture->getTextureHandle();
62    }
63    return 0;
64}
65
66bool SkSurface_Gpu::onGetRenderTargetHandle(GrBackendObject* obj, BackendHandleAccess access) {
67    GrRenderTarget* rt = prepare_rt_for_external_access(this, access);
68    if (!rt) {
69        return false;
70    }
71    *obj = rt->getRenderTargetHandle();
72    return true;
73}
74
75SkCanvas* SkSurface_Gpu::onNewCanvas() {
76    SkCanvas::InitFlags flags = SkCanvas::kDefault_InitFlags;
77    flags = static_cast<SkCanvas::InitFlags>(flags | SkCanvas::kConservativeRasterClip_InitFlag);
78
79    return new SkCanvas(fDevice.get(), flags);
80}
81
82sk_sp<SkSurface> SkSurface_Gpu::onNewSurface(const SkImageInfo& info) {
83    int sampleCount = fDevice->accessRenderTargetContext()->numColorSamples();
84    GrSurfaceOrigin origin = fDevice->accessRenderTargetContext()->origin();
85    // TODO: Make caller specify this (change virtual signature of onNewSurface).
86    static const SkBudgeted kBudgeted = SkBudgeted::kNo;
87    return SkSurface::MakeRenderTarget(fDevice->context(), kBudgeted, info, sampleCount,
88                                       origin, &this->props());
89}
90
91sk_sp<SkImage> SkSurface_Gpu::onNewImageSnapshot() {
92    GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
93    if (!rtc) {
94        return nullptr;
95    }
96
97    GrContext* ctx = fDevice->context();
98
99    if (!rtc->asSurfaceProxy()) {
100        return nullptr;
101    }
102
103    SkBudgeted budgeted = rtc->asSurfaceProxy()->isBudgeted();
104
105    sk_sp<GrTextureProxy> srcProxy = rtc->asTextureProxyRef();
106    // If the original render target is a buffer originally created by the client, then we don't
107    // want to ever retarget the SkSurface at another buffer we create. Force a copy now to avoid
108    // copy-on-write.
109    if (!srcProxy || rtc->priv().refsWrappedObjects()) {
110        SkASSERT(rtc->origin() == rtc->asSurfaceProxy()->origin());
111
112        srcProxy = GrSurfaceProxy::Copy(ctx, rtc->asSurfaceProxy(), rtc->mipMapped(), budgeted);
113    }
114
115    const SkImageInfo info = fDevice->imageInfo();
116    sk_sp<SkImage> image;
117    if (srcProxy) {
118        // The renderTargetContext coming out of SkGpuDevice should always be exact and the
119        // above copy creates a kExact surfaceContext.
120        SkASSERT(srcProxy->priv().isExact());
121        image = sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
122                                        info.alphaType(), std::move(srcProxy),
123                                        info.refColorSpace(), budgeted);
124    }
125    return image;
126}
127
128void SkSurface_Gpu::onWritePixels(const SkPixmap& src, int x, int y) {
129    fDevice->writePixels(src, x, y);
130}
131
132// Create a new render target and, if necessary, copy the contents of the old
133// render target into it. Note that this flushes the SkGpuDevice but
134// doesn't force an OpenGL flush.
135void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
136    GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
137
138    // are we sharing our backing proxy with the image? Note this call should never create a new
139    // image because onCopyOnWrite is only called when there is a cached image.
140    sk_sp<SkImage> image(this->refCachedImage());
141    SkASSERT(image);
142
143    GrSurfaceProxy* imageProxy = ((SkImage_Base*) image.get())->peekProxy();
144    SkASSERT(imageProxy);
145
146    if (rtc->asSurfaceProxy()->underlyingUniqueID() == imageProxy->underlyingUniqueID()) {
147        fDevice->replaceRenderTargetContext(SkSurface::kRetain_ContentChangeMode == mode);
148    } else if (kDiscard_ContentChangeMode == mode) {
149        this->SkSurface_Gpu::onDiscard();
150    }
151}
152
153void SkSurface_Gpu::onDiscard() {
154    fDevice->accessRenderTargetContext()->discard();
155}
156
157GrSemaphoresSubmitted SkSurface_Gpu::onFlush(int numSemaphores,
158                                             GrBackendSemaphore signalSemaphores[]) {
159    return fDevice->flushAndSignalSemaphores(numSemaphores, signalSemaphores);
160}
161
162bool SkSurface_Gpu::onWait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) {
163    return fDevice->wait(numSemaphores, waitSemaphores);
164}
165
166bool SkSurface_Gpu::onCharacterize(SkSurfaceCharacterization* data) const {
167    GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
168    GrContext* ctx = fDevice->context();
169
170    int maxResourceCount;
171    size_t maxResourceBytes;
172    ctx->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes);
173
174    bool mipmapped = rtc->asTextureProxy() ? GrMipMapped::kYes == rtc->asTextureProxy()->mipMapped()
175                                           : false;
176
177    data->set(ctx->threadSafeProxy(), maxResourceBytes,
178              rtc->origin(), rtc->width(), rtc->height(),
179              rtc->colorSpaceInfo().config(), rtc->fsaaType(), rtc->numStencilSamples(),
180              SkSurfaceCharacterization::Textureable(SkToBool(rtc->asTextureProxy())),
181              SkSurfaceCharacterization::MipMapped(mipmapped),
182              rtc->colorSpaceInfo().refColorSpace(), this->props());
183
184    return true;
185}
186
187bool SkSurface_Gpu::isCompatible(const SkSurfaceCharacterization& data) const {
188    GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
189    GrContext* ctx = fDevice->context();
190
191    if (!data.isValid()) {
192        return false;
193    }
194
195    // As long as the current state if the context allows for greater or equal resources,
196    // we allow the DDL to be replayed.
197    // DDL TODO: should we just remove the resource check and ignore the cache limits on playback?
198    int maxResourceCount;
199    size_t maxResourceBytes;
200    ctx->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes);
201
202    if (data.isTextureable()) {
203        if (!rtc->asTextureProxy()) {
204            // If the characterization was textureable we require the replay dest to also be
205            // textureable. If the characterized surface wasn't textureable we allow the replay
206            // dest to be textureable.
207            return false;
208        }
209
210        if (data.isMipMapped() && GrMipMapped::kNo == rtc->asTextureProxy()->mipMapped()) {
211            // Fail if the DDL's surface was mipmapped but the replay surface is not.
212            // Allow drawing to proceed if the DDL was not mipmapped but the replay surface is.
213            return false;
214        }
215    }
216
217    return data.contextInfo() && data.contextInfo()->matches(ctx) &&
218           data.cacheMaxResourceBytes() <= maxResourceBytes &&
219           data.origin() == rtc->origin() && data.width() == rtc->width() &&
220           data.height() == rtc->height() && data.config() == rtc->colorSpaceInfo().config() &&
221           data.fsaaType() == rtc->fsaaType() && data.stencilCount() == rtc->numStencilSamples() &&
222           SkColorSpace::Equals(data.colorSpace(), rtc->colorSpaceInfo().colorSpace()) &&
223           data.surfaceProps() == rtc->surfaceProps();
224}
225
226bool SkSurface_Gpu::onDraw(const SkDeferredDisplayList* ddl) {
227    if (!ddl || !this->isCompatible(ddl->characterization())) {
228        return false;
229    }
230
231#ifdef SK_RASTER_RECORDER_IMPLEMENTATION
232    // Ultimately need to pass opLists from the DeferredDisplayList on to the
233    // SkGpuDevice's renderTargetContext.
234    return ddl->draw(this);
235#else
236    GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
237    GrContext* ctx = fDevice->context();
238
239    ctx->contextPriv().copyOpListsFromDDL(ddl, rtc->asRenderTargetProxy());
240    return true;
241#endif
242}
243
244
245///////////////////////////////////////////////////////////////////////////////
246
247bool SkSurface_Gpu::Valid(const SkImageInfo& info) {
248    switch (info.colorType()) {
249        case kRGBA_F16_SkColorType:
250            return (!info.colorSpace()) || info.colorSpace()->gammaIsLinear();
251        case kRGBA_8888_SkColorType:
252        case kBGRA_8888_SkColorType:
253            return !info.colorSpace() || info.colorSpace()->gammaCloseToSRGB();
254        default:
255            return !info.colorSpace();
256    }
257}
258
259bool SkSurface_Gpu::Valid(GrContext* context, GrPixelConfig config, SkColorSpace* colorSpace) {
260    switch (config) {
261        case kRGBA_half_GrPixelConfig:
262            return (!colorSpace) || colorSpace->gammaIsLinear();
263        case kSRGBA_8888_GrPixelConfig:
264        case kSBGRA_8888_GrPixelConfig:
265            return context->caps()->srgbSupport() && colorSpace && colorSpace->gammaCloseToSRGB();
266        case kRGBA_8888_GrPixelConfig:
267        case kBGRA_8888_GrPixelConfig:
268            // We may get here with either a linear-gamma color space or with a sRGB-gamma color
269            // space when we lack GPU sRGB support.
270            return !colorSpace ||
271                   (colorSpace->gammaCloseToSRGB() && !context->caps()->srgbSupport()) ||
272                   colorSpace->gammaIsLinear();
273        default:
274            return !colorSpace;
275    }
276}
277
278sk_sp<SkSurface> SkSurface::MakeRenderTarget(GrContext* ctx, SkBudgeted budgeted,
279                                             const SkImageInfo& info, int sampleCount,
280                                             GrSurfaceOrigin origin, const SkSurfaceProps* props,
281                                             bool shouldCreateWithMips) {
282    if (!ctx) {
283        return nullptr;
284    }
285    if (!SkSurface_Gpu::Valid(info)) {
286        return nullptr;
287    }
288    sampleCount = SkTMax(1, sampleCount);
289    GrMipMapped mipMapped = shouldCreateWithMips ? GrMipMapped::kYes : GrMipMapped::kNo;
290
291    if (!ctx->caps()->mipMapSupport()) {
292        mipMapped = GrMipMapped::kNo;
293    }
294
295    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(
296            ctx, budgeted, info, sampleCount, origin, props, mipMapped,
297            SkGpuDevice::kClear_InitContents));
298    if (!device) {
299        return nullptr;
300    }
301    return sk_make_sp<SkSurface_Gpu>(std::move(device));
302}
303
304sk_sp<SkSurface> SkSurface_Gpu::MakeWrappedRenderTarget(GrContext* context,
305                                                        sk_sp<GrRenderTargetContext> rtc) {
306    if (!context) {
307        return nullptr;
308    }
309
310    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc),
311                                                rtc->width(), rtc->height(),
312                                                SkGpuDevice::kUninit_InitContents));
313    if (!device) {
314        return nullptr;
315    }
316
317    return sk_make_sp<SkSurface_Gpu>(std::move(device));
318}
319
320
321sk_sp<SkSurface> SkSurface::MakeFromBackendTexture(GrContext* context, const GrBackendTexture& tex,
322                                                   GrSurfaceOrigin origin, int sampleCnt,
323                                                   sk_sp<SkColorSpace> colorSpace,
324                                                   const SkSurfaceProps* props) {
325    if (!context) {
326        return nullptr;
327    }
328    if (!SkSurface_Gpu::Valid(context, tex.config(), colorSpace.get())) {
329        return nullptr;
330    }
331    sampleCnt = SkTMax(1, sampleCnt);
332
333    sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeBackendTextureRenderTargetContext(
334                                                                    tex,
335                                                                    origin,
336                                                                    sampleCnt,
337                                                                    std::move(colorSpace),
338                                                                    props));
339    if (!rtc) {
340        return nullptr;
341    }
342
343    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), tex.width(), tex.height(),
344                                                SkGpuDevice::kUninit_InitContents));
345    if (!device) {
346        return nullptr;
347    }
348    return sk_make_sp<SkSurface_Gpu>(std::move(device));
349}
350
351bool validate_backend_texture(GrContext* ctx, const GrBackendTexture& tex, GrPixelConfig* config,
352                              int sampleCnt, SkColorType ct, sk_sp<SkColorSpace> cs,
353                              bool texturable) {
354    // TODO: Create a SkImageColorInfo struct for color, alpha, and color space so we don't need to
355    // create a fake image info here.
356    SkImageInfo info = SkImageInfo::Make(1, 1, ct, kPremul_SkAlphaType, cs);
357
358    if (!SkSurface_Gpu::Valid(info)) {
359        return false;
360    }
361
362    if (!ctx->caps()->validateBackendTexture(tex, ct, config)) {
363        return false;
364    }
365
366    // We don't require that the client gave us an exact valid sample cnt. However, it must be
367    // less than the max supported sample count and 1 if MSAA is unsupported for the color type.
368    if (!ctx->caps()->getRenderTargetSampleCount(sampleCnt, *config)) {
369        return false;
370    }
371
372    if (texturable && !ctx->caps()->isConfigTexturable(*config)) {
373        return false;
374    }
375    return true;
376}
377
378sk_sp<SkSurface> SkSurface::MakeFromBackendTexture(GrContext* context, const GrBackendTexture& tex,
379                                                   GrSurfaceOrigin origin, int sampleCnt,
380                                                   SkColorType colorType,
381                                                   sk_sp<SkColorSpace> colorSpace,
382                                                   const SkSurfaceProps* props) {
383    if (!context) {
384        return nullptr;
385    }
386    sampleCnt = SkTMax(1, sampleCnt);
387    GrBackendTexture texCopy = tex;
388    if (!validate_backend_texture(context, texCopy, &texCopy.fConfig,
389                                  sampleCnt, colorType, colorSpace, true)) {
390        return nullptr;
391    }
392
393    return MakeFromBackendTexture(context, texCopy, origin, sampleCnt, colorSpace, props);
394}
395
396sk_sp<SkSurface> SkSurface::MakeFromBackendRenderTarget(GrContext* context,
397                                                        const GrBackendRenderTarget& backendRT,
398                                                        GrSurfaceOrigin origin,
399                                                        sk_sp<SkColorSpace> colorSpace,
400                                                        const SkSurfaceProps* props) {
401    if (!context) {
402        return nullptr;
403    }
404    if (!SkSurface_Gpu::Valid(context, backendRT.config(), colorSpace.get())) {
405        return nullptr;
406    }
407
408    sk_sp<GrRenderTargetContext> rtc(
409        context->contextPriv().makeBackendRenderTargetRenderTargetContext(backendRT,
410                                                                          origin,
411                                                                          std::move(colorSpace),
412                                                                          props));
413    if (!rtc) {
414        return nullptr;
415    }
416
417    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc),
418                                                backendRT.width(), backendRT.height(),
419                                                SkGpuDevice::kUninit_InitContents));
420    if (!device) {
421        return nullptr;
422    }
423
424    return sk_make_sp<SkSurface_Gpu>(std::move(device));
425}
426
427bool validate_backend_render_target(GrContext* ctx, const GrBackendRenderTarget& rt,
428                                    GrPixelConfig* config, SkColorType ct, sk_sp<SkColorSpace> cs) {
429    // TODO: Create a SkImageColorInfo struct for color, alpha, and color space so we don't need to
430    // create a fake image info here.
431    SkImageInfo info = SkImageInfo::Make(1, 1, ct, kPremul_SkAlphaType, cs);
432
433    if (!SkSurface_Gpu::Valid(info)) {
434        return false;
435    }
436
437    if (!ctx->caps()->validateBackendRenderTarget(rt, ct, config)) {
438        return false;
439    }
440
441    if (rt.sampleCnt() > 1) {
442        if (ctx->caps()->maxRenderTargetSampleCount(*config) <= 1) {
443            return false;
444        }
445    } else if (!ctx->caps()->isConfigRenderable(*config)) {
446        return false;
447    }
448
449    return true;
450}
451
452sk_sp<SkSurface> SkSurface::MakeFromBackendRenderTarget(GrContext* context,
453                                                        const GrBackendRenderTarget& rt,
454                                                        GrSurfaceOrigin origin,
455                                                        SkColorType colorType,
456                                                        sk_sp<SkColorSpace> colorSpace,
457                                                        const SkSurfaceProps* props) {
458    if (!context) {
459        return nullptr;
460    }
461    GrBackendRenderTarget rtCopy = rt;
462    if (!validate_backend_render_target(context, rtCopy, &rtCopy.fConfig, colorType, colorSpace)) {
463        return nullptr;
464    }
465
466    return MakeFromBackendRenderTarget(context, rtCopy, origin, colorSpace, props);
467}
468
469sk_sp<SkSurface> SkSurface::MakeFromBackendTextureAsRenderTarget(GrContext* context,
470                                                                 const GrBackendTexture& tex,
471                                                                 GrSurfaceOrigin origin,
472                                                                 int sampleCnt,
473                                                                 sk_sp<SkColorSpace> colorSpace,
474                                                                 const SkSurfaceProps* props) {
475    if (!context) {
476        return nullptr;
477    }
478    if (!SkSurface_Gpu::Valid(context, tex.config(), colorSpace.get())) {
479        return nullptr;
480    }
481    sampleCnt = SkTMax(1, sampleCnt);
482
483    sk_sp<GrRenderTargetContext> rtc(
484        context->contextPriv().makeBackendTextureAsRenderTargetRenderTargetContext(
485                                                                              tex,
486                                                                              origin,
487                                                                              sampleCnt,
488                                                                              std::move(colorSpace),
489                                                                              props));
490    if (!rtc) {
491        return nullptr;
492    }
493
494    sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), tex.width(), tex.height(),
495                                                SkGpuDevice::kUninit_InitContents));
496    if (!device) {
497        return nullptr;
498    }
499    return sk_make_sp<SkSurface_Gpu>(std::move(device));
500}
501
502sk_sp<SkSurface> SkSurface::MakeFromBackendTextureAsRenderTarget(GrContext* context,
503                                                                 const GrBackendTexture& tex,
504                                                                 GrSurfaceOrigin origin,
505                                                                 int sampleCnt,
506                                                                 SkColorType colorType,
507                                                                 sk_sp<SkColorSpace> colorSpace,
508                                                                 const SkSurfaceProps* props) {
509    if (!context) {
510        return nullptr;
511    }
512    sampleCnt = SkTMax(1, sampleCnt);
513    GrBackendTexture texCopy = tex;
514    if (!validate_backend_texture(context, texCopy, &texCopy.fConfig,
515                                  sampleCnt, colorType, colorSpace, false)) {
516        return nullptr;
517    }
518
519    return MakeFromBackendTextureAsRenderTarget(context, texCopy, origin, sampleCnt, colorSpace,
520                                                props);
521}
522
523#endif
524