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
SkSurface_Gpu(sk_sp<SkGpuDevice> device)27 SkSurface_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
~SkSurface_Gpu()33 SkSurface_Gpu::~SkSurface_Gpu() {
34 }
35
prepare_rt_for_external_access(SkSurface_Gpu * surface,SkSurface::BackendHandleAccess access)36 static 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
onGetTextureHandle(BackendHandleAccess access)54 GrBackendObject 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
onGetRenderTargetHandle(GrBackendObject * obj,BackendHandleAccess access)66 bool 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
onNewCanvas()75 SkCanvas* 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
onNewSurface(const SkImageInfo & info)82 sk_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
onNewImageSnapshot()91 sk_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
128 // Create a new render target and, if necessary, copy the contents of the old
129 // render target into it. Note that this flushes the SkGpuDevice but
130 // doesn't force an OpenGL flush.
onCopyOnWrite(ContentChangeMode mode)131 void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
132 GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
133
134 // are we sharing our backing proxy with the image? Note this call should never create a new
135 // image because onCopyOnWrite is only called when there is a cached image.
136 sk_sp<SkImage> image(this->refCachedImage());
137 SkASSERT(image);
138
139 GrSurfaceProxy* imageProxy = ((SkImage_Base*) image.get())->peekProxy();
140 SkASSERT(imageProxy);
141
142 if (rtc->asSurfaceProxy()->underlyingUniqueID() == imageProxy->underlyingUniqueID()) {
143 fDevice->replaceRenderTargetContext(SkSurface::kRetain_ContentChangeMode == mode);
144 } else if (kDiscard_ContentChangeMode == mode) {
145 this->SkSurface_Gpu::onDiscard();
146 }
147 }
148
onDiscard()149 void SkSurface_Gpu::onDiscard() {
150 fDevice->accessRenderTargetContext()->discard();
151 }
152
onFlush(int numSemaphores,GrBackendSemaphore signalSemaphores[])153 GrSemaphoresSubmitted SkSurface_Gpu::onFlush(int numSemaphores,
154 GrBackendSemaphore signalSemaphores[]) {
155 return fDevice->flushAndSignalSemaphores(numSemaphores, signalSemaphores);
156 }
157
onWait(int numSemaphores,const GrBackendSemaphore * waitSemaphores)158 bool SkSurface_Gpu::onWait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) {
159 return fDevice->wait(numSemaphores, waitSemaphores);
160 }
161
onCharacterize(SkSurfaceCharacterization * data) const162 bool SkSurface_Gpu::onCharacterize(SkSurfaceCharacterization* data) const {
163 GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
164 GrContext* ctx = fDevice->context();
165
166 int maxResourceCount;
167 size_t maxResourceBytes;
168 ctx->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes);
169
170 data->set(ctx->threadSafeProxy(), maxResourceCount, maxResourceBytes,
171 rtc->origin(), rtc->width(), rtc->height(),
172 rtc->colorSpaceInfo().config(), rtc->fsaaType(), rtc->numStencilSamples(),
173 rtc->colorSpaceInfo().refColorSpace(), this->props());
174
175 return true;
176 }
177
isCompatible(const SkSurfaceCharacterization & data) const178 bool SkSurface_Gpu::isCompatible(const SkSurfaceCharacterization& data) const {
179 GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
180 GrContext* ctx = fDevice->context();
181
182 // As long as the current state if the context allows for greater or equal resources,
183 // we allow the DDL to be replayed.
184 int maxResourceCount;
185 size_t maxResourceBytes;
186 ctx->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes);
187
188 return data.contextInfo() && data.contextInfo()->matches(ctx) &&
189 data.cacheMaxResourceCount() <= maxResourceCount &&
190 data.cacheMaxResourceBytes() <= maxResourceBytes &&
191 data.origin() == rtc->origin() && data.width() == rtc->width() &&
192 data.height() == rtc->height() && data.config() == rtc->colorSpaceInfo().config() &&
193 data.fsaaType() == rtc->fsaaType() && data.stencilCount() == rtc->numStencilSamples() &&
194 SkColorSpace::Equals(data.colorSpace(), rtc->colorSpaceInfo().colorSpace()) &&
195 data.surfaceProps() == rtc->surfaceProps();
196 }
197
onDraw(const SkDeferredDisplayList * ddl)198 bool SkSurface_Gpu::onDraw(const SkDeferredDisplayList* ddl) {
199 if (!this->isCompatible(ddl->characterization())) {
200 return false;
201 }
202
203 #ifdef SK_RASTER_RECORDER_IMPLEMENTATION
204 // Ultimately need to pass opLists from the DeferredDisplayList on to the
205 // SkGpuDevice's renderTargetContext.
206 return ddl->draw(this);
207 #else
208 GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
209 GrContext* ctx = fDevice->context();
210
211 ctx->contextPriv().copyOpListsFromDDL(ddl, rtc->asRenderTargetProxy());
212 return true;
213 #endif
214 }
215
216
217 ///////////////////////////////////////////////////////////////////////////////
218
Valid(const SkImageInfo & info)219 bool SkSurface_Gpu::Valid(const SkImageInfo& info) {
220 switch (info.colorType()) {
221 case kRGBA_F16_SkColorType:
222 return (!info.colorSpace()) || info.colorSpace()->gammaIsLinear();
223 case kRGBA_8888_SkColorType:
224 case kBGRA_8888_SkColorType:
225 return !info.colorSpace() || info.colorSpace()->gammaCloseToSRGB();
226 default:
227 return !info.colorSpace();
228 }
229 }
230
Valid(GrContext * context,GrPixelConfig config,SkColorSpace * colorSpace)231 bool SkSurface_Gpu::Valid(GrContext* context, GrPixelConfig config, SkColorSpace* colorSpace) {
232 switch (config) {
233 case kRGBA_half_GrPixelConfig:
234 return (!colorSpace) || colorSpace->gammaIsLinear();
235 case kSRGBA_8888_GrPixelConfig:
236 case kSBGRA_8888_GrPixelConfig:
237 return context->caps()->srgbSupport() && colorSpace && colorSpace->gammaCloseToSRGB();
238 case kRGBA_8888_GrPixelConfig:
239 case kBGRA_8888_GrPixelConfig:
240 // If we don't have sRGB support, we may get here with a color space. It still needs
241 // to be sRGB-like (so that the application will work correctly on sRGB devices.)
242 return !colorSpace ||
243 (colorSpace->gammaCloseToSRGB() && !context->caps()->srgbSupport());
244 default:
245 return !colorSpace;
246 }
247 }
248
MakeRenderTarget(GrContext * ctx,SkBudgeted budgeted,const SkImageInfo & info,int sampleCount,GrSurfaceOrigin origin,const SkSurfaceProps * props,bool shouldCreateWithMips)249 sk_sp<SkSurface> SkSurface::MakeRenderTarget(GrContext* ctx, SkBudgeted budgeted,
250 const SkImageInfo& info, int sampleCount,
251 GrSurfaceOrigin origin, const SkSurfaceProps* props,
252 bool shouldCreateWithMips) {
253 if (!ctx) {
254 return nullptr;
255 }
256 if (!SkSurface_Gpu::Valid(info)) {
257 return nullptr;
258 }
259 sampleCount = SkTMax(1, sampleCount);
260 GrMipMapped mipMapped = shouldCreateWithMips ? GrMipMapped::kYes : GrMipMapped::kNo;
261
262 if (!ctx->caps()->mipMapSupport()) {
263 mipMapped = GrMipMapped::kNo;
264 }
265
266 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(
267 ctx, budgeted, info, sampleCount, origin, props, mipMapped,
268 SkGpuDevice::kClear_InitContents));
269 if (!device) {
270 return nullptr;
271 }
272 return sk_make_sp<SkSurface_Gpu>(std::move(device));
273 }
274
MakeWrappedRenderTarget(GrContext * context,sk_sp<GrRenderTargetContext> rtc)275 sk_sp<SkSurface> SkSurface_Gpu::MakeWrappedRenderTarget(GrContext* context,
276 sk_sp<GrRenderTargetContext> rtc) {
277 if (!context) {
278 return nullptr;
279 }
280
281 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc),
282 rtc->width(), rtc->height(),
283 SkGpuDevice::kUninit_InitContents));
284 if (!device) {
285 return nullptr;
286 }
287
288 return sk_make_sp<SkSurface_Gpu>(std::move(device));
289 }
290
291
MakeFromBackendTexture(GrContext * context,const GrBackendTexture & tex,GrSurfaceOrigin origin,int sampleCnt,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props)292 sk_sp<SkSurface> SkSurface::MakeFromBackendTexture(GrContext* context, const GrBackendTexture& tex,
293 GrSurfaceOrigin origin, int sampleCnt,
294 sk_sp<SkColorSpace> colorSpace,
295 const SkSurfaceProps* props) {
296 if (!context) {
297 return nullptr;
298 }
299 if (!SkSurface_Gpu::Valid(context, tex.config(), colorSpace.get())) {
300 return nullptr;
301 }
302 sampleCnt = SkTMax(1, sampleCnt);
303
304 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeBackendTextureRenderTargetContext(
305 tex,
306 origin,
307 sampleCnt,
308 std::move(colorSpace),
309 props));
310 if (!rtc) {
311 return nullptr;
312 }
313
314 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), tex.width(), tex.height(),
315 SkGpuDevice::kUninit_InitContents));
316 if (!device) {
317 return nullptr;
318 }
319 return sk_make_sp<SkSurface_Gpu>(std::move(device));
320 }
321
validate_backend_texture(GrContext * ctx,const GrBackendTexture & tex,GrPixelConfig * config,int sampleCnt,SkColorType ct,sk_sp<SkColorSpace> cs,bool texturable)322 bool validate_backend_texture(GrContext* ctx, const GrBackendTexture& tex, GrPixelConfig* config,
323 int sampleCnt, SkColorType ct, sk_sp<SkColorSpace> cs,
324 bool texturable) {
325 // TODO: Create a SkImageColorInfo struct for color, alpha, and color space so we don't need to
326 // create a fake image info here.
327 SkImageInfo info = SkImageInfo::Make(1, 1, ct, kPremul_SkAlphaType, cs);
328
329 if (!SkSurface_Gpu::Valid(info)) {
330 return false;
331 }
332
333 if (!ctx->caps()->validateBackendTexture(tex, ct, config)) {
334 return false;
335 }
336
337 if (!ctx->caps()->isConfigRenderable(*config, sampleCnt > 1)) {
338 return false;
339 }
340
341 if (ctx->caps()->getSampleCount(sampleCnt, *config) != sampleCnt) {
342 return false;
343 }
344
345 if (texturable && !ctx->caps()->isConfigTexturable(*config)) {
346 return false;
347 }
348 return true;
349 }
350
MakeFromBackendTexture(GrContext * context,const GrBackendTexture & tex,GrSurfaceOrigin origin,int sampleCnt,SkColorType colorType,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props)351 sk_sp<SkSurface> SkSurface::MakeFromBackendTexture(GrContext* context, const GrBackendTexture& tex,
352 GrSurfaceOrigin origin, int sampleCnt,
353 SkColorType colorType,
354 sk_sp<SkColorSpace> colorSpace,
355 const SkSurfaceProps* props) {
356 if (!context) {
357 return nullptr;
358 }
359 sampleCnt = SkTMax(1, sampleCnt);
360 GrBackendTexture texCopy = tex;
361 if (!validate_backend_texture(context, texCopy, &texCopy.fConfig,
362 sampleCnt, colorType, colorSpace, true)) {
363 return nullptr;
364 }
365
366 return MakeFromBackendTexture(context, texCopy, origin, sampleCnt, colorSpace, props);
367 }
368
MakeFromBackendRenderTarget(GrContext * context,const GrBackendRenderTarget & backendRT,GrSurfaceOrigin origin,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props)369 sk_sp<SkSurface> SkSurface::MakeFromBackendRenderTarget(GrContext* context,
370 const GrBackendRenderTarget& backendRT,
371 GrSurfaceOrigin origin,
372 sk_sp<SkColorSpace> colorSpace,
373 const SkSurfaceProps* props) {
374 if (!context) {
375 return nullptr;
376 }
377 if (!SkSurface_Gpu::Valid(context, backendRT.config(), colorSpace.get())) {
378 return nullptr;
379 }
380
381 sk_sp<GrRenderTargetContext> rtc(
382 context->contextPriv().makeBackendRenderTargetRenderTargetContext(backendRT,
383 origin,
384 std::move(colorSpace),
385 props));
386 if (!rtc) {
387 return nullptr;
388 }
389
390 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc),
391 backendRT.width(), backendRT.height(),
392 SkGpuDevice::kUninit_InitContents));
393 if (!device) {
394 return nullptr;
395 }
396
397 return sk_make_sp<SkSurface_Gpu>(std::move(device));
398 }
399
validate_backend_render_target(GrContext * ctx,const GrBackendRenderTarget & rt,GrPixelConfig * config,SkColorType ct,sk_sp<SkColorSpace> cs)400 bool validate_backend_render_target(GrContext* ctx, const GrBackendRenderTarget& rt,
401 GrPixelConfig* config, SkColorType ct, sk_sp<SkColorSpace> cs) {
402 // TODO: Create a SkImageColorInfo struct for color, alpha, and color space so we don't need to
403 // create a fake image info here.
404 SkImageInfo info = SkImageInfo::Make(1, 1, ct, kPremul_SkAlphaType, cs);
405
406 if (!SkSurface_Gpu::Valid(info)) {
407 return false;
408 }
409
410 if (!ctx->caps()->validateBackendRenderTarget(rt, ct, config)) {
411 return false;
412 }
413
414 if (!ctx->caps()->isConfigRenderable(*config, false)) {
415 return false;
416 }
417
418 return true;
419 }
420
MakeFromBackendRenderTarget(GrContext * context,const GrBackendRenderTarget & rt,GrSurfaceOrigin origin,SkColorType colorType,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props)421 sk_sp<SkSurface> SkSurface::MakeFromBackendRenderTarget(GrContext* context,
422 const GrBackendRenderTarget& rt,
423 GrSurfaceOrigin origin,
424 SkColorType colorType,
425 sk_sp<SkColorSpace> colorSpace,
426 const SkSurfaceProps* props) {
427 if (!context) {
428 return nullptr;
429 }
430 GrBackendRenderTarget rtCopy = rt;
431 if (!validate_backend_render_target(context, rtCopy, &rtCopy.fConfig, colorType, colorSpace)) {
432 return nullptr;
433 }
434
435 return MakeFromBackendRenderTarget(context, rtCopy, origin, colorSpace, props);
436 }
437
MakeFromBackendTextureAsRenderTarget(GrContext * context,const GrBackendTexture & tex,GrSurfaceOrigin origin,int sampleCnt,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props)438 sk_sp<SkSurface> SkSurface::MakeFromBackendTextureAsRenderTarget(GrContext* context,
439 const GrBackendTexture& tex,
440 GrSurfaceOrigin origin,
441 int sampleCnt,
442 sk_sp<SkColorSpace> colorSpace,
443 const SkSurfaceProps* props) {
444 if (!context) {
445 return nullptr;
446 }
447 if (!SkSurface_Gpu::Valid(context, tex.config(), colorSpace.get())) {
448 return nullptr;
449 }
450 sampleCnt = SkTMax(1, sampleCnt);
451
452 sk_sp<GrRenderTargetContext> rtc(
453 context->contextPriv().makeBackendTextureAsRenderTargetRenderTargetContext(
454 tex,
455 origin,
456 sampleCnt,
457 std::move(colorSpace),
458 props));
459 if (!rtc) {
460 return nullptr;
461 }
462
463 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), tex.width(), tex.height(),
464 SkGpuDevice::kUninit_InitContents));
465 if (!device) {
466 return nullptr;
467 }
468 return sk_make_sp<SkSurface_Gpu>(std::move(device));
469 }
470
MakeFromBackendTextureAsRenderTarget(GrContext * context,const GrBackendTexture & tex,GrSurfaceOrigin origin,int sampleCnt,SkColorType colorType,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props)471 sk_sp<SkSurface> SkSurface::MakeFromBackendTextureAsRenderTarget(GrContext* context,
472 const GrBackendTexture& tex,
473 GrSurfaceOrigin origin,
474 int sampleCnt,
475 SkColorType colorType,
476 sk_sp<SkColorSpace> colorSpace,
477 const SkSurfaceProps* props) {
478 if (!context) {
479 return nullptr;
480 }
481 sampleCnt = SkTMax(1, sampleCnt);
482 GrBackendTexture texCopy = tex;
483 if (!validate_backend_texture(context, texCopy, &texCopy.fConfig,
484 sampleCnt, colorType, colorSpace, false)) {
485 return nullptr;
486 }
487
488 return MakeFromBackendTextureAsRenderTarget(context, texCopy, origin, sampleCnt, colorSpace,
489 props);
490 }
491
492 #endif
493