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