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 #include "GrBackendSurface.h" 10 #include "GrCaps.h" 11 #include "GrContextPriv.h" 12 #include "GrRenderTarget.h" 13 #include "GrRenderTargetContextPriv.h" 14 #include "GrRenderTargetProxyPriv.h" 15 #include "GrTexture.h" 16 #include "SkCanvas.h" 17 #include "SkDeferredDisplayList.h" 18 #include "SkGpuDevice.h" 19 #include "SkImagePriv.h" 20 #include "SkImage_Base.h" 21 #include "SkImage_Gpu.h" 22 #include "SkSurfaceCharacterization.h" 23 #include "SkSurface_Base.h" 24 25 #if SK_SUPPORT_GPU 26 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 33 SkSurface_Gpu::~SkSurface_Gpu() { 34 } 35 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 54 GrBackendTexture SkSurface_Gpu::onGetBackendTexture(BackendHandleAccess access) { 55 GrRenderTarget* rt = prepare_rt_for_external_access(this, access); 56 if (!rt) { 57 return GrBackendTexture(); // invalid 58 } 59 GrTexture* texture = rt->asTexture(); 60 if (texture) { 61 return texture->getBackendTexture(); 62 } 63 return GrBackendTexture(); // invalid 64 } 65 66 GrBackendRenderTarget SkSurface_Gpu::onGetBackendRenderTarget(BackendHandleAccess access) { 67 GrRenderTarget* rt = prepare_rt_for_external_access(this, access); 68 if (!rt) { 69 return GrBackendRenderTarget(); // invalid 70 } 71 72 return rt->getBackendRenderTarget(); 73 } 74 75 SkCanvas* SkSurface_Gpu::onNewCanvas() { return new SkCanvas(fDevice); } 76 77 sk_sp<SkSurface> SkSurface_Gpu::onNewSurface(const SkImageInfo& info) { 78 int sampleCount = fDevice->accessRenderTargetContext()->numColorSamples(); 79 GrSurfaceOrigin origin = fDevice->accessRenderTargetContext()->origin(); 80 // TODO: Make caller specify this (change virtual signature of onNewSurface). 81 static const SkBudgeted kBudgeted = SkBudgeted::kNo; 82 return SkSurface::MakeRenderTarget(fDevice->context(), kBudgeted, info, sampleCount, 83 origin, &this->props()); 84 } 85 86 sk_sp<SkImage> SkSurface_Gpu::onNewImageSnapshot(const SkIRect* subset) { 87 GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext(); 88 if (!rtc) { 89 return nullptr; 90 } 91 92 GrContext* ctx = fDevice->context(); 93 94 if (!rtc->asSurfaceProxy()) { 95 return nullptr; 96 } 97 98 SkBudgeted budgeted = rtc->asSurfaceProxy()->isBudgeted(); 99 100 sk_sp<GrTextureProxy> srcProxy = rtc->asTextureProxyRef(); 101 102 if (subset) { 103 srcProxy = GrSurfaceProxy::Copy(ctx, rtc->asSurfaceProxy(), rtc->mipMapped(), *subset, 104 SkBackingFit::kExact, budgeted); 105 } else if (!srcProxy || rtc->priv().refsWrappedObjects()) { 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 SkASSERT(rtc->origin() == rtc->asSurfaceProxy()->origin()); 110 111 srcProxy = GrSurfaceProxy::Copy(ctx, rtc->asSurfaceProxy(), rtc->mipMapped(), 112 SkBackingFit::kExact, 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>(sk_ref_sp(ctx), kNeedNewImageUniqueID, info.alphaType(), 122 std::move(srcProxy), info.refColorSpace()); 123 } 124 return image; 125 } 126 127 void SkSurface_Gpu::onWritePixels(const SkPixmap& src, int x, int y) { 128 fDevice->writePixels(src, x, y); 129 } 130 131 // Create a new render target and, if necessary, copy the contents of the old 132 // render target into it. Note that this flushes the SkGpuDevice but 133 // doesn't force an OpenGL flush. 134 void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) { 135 GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext(); 136 137 // are we sharing our backing proxy with the image? Note this call should never create a new 138 // image because onCopyOnWrite is only called when there is a cached image. 139 sk_sp<SkImage> image(this->refCachedImage()); 140 SkASSERT(image); 141 142 GrSurfaceProxy* imageProxy = ((SkImage_Base*) image.get())->peekProxy(); 143 SkASSERT(imageProxy); 144 145 if (rtc->asSurfaceProxy()->underlyingUniqueID() == imageProxy->underlyingUniqueID()) { 146 fDevice->replaceRenderTargetContext(SkSurface::kRetain_ContentChangeMode == mode); 147 } else if (kDiscard_ContentChangeMode == mode) { 148 this->SkSurface_Gpu::onDiscard(); 149 } 150 } 151 152 void SkSurface_Gpu::onDiscard() { 153 fDevice->accessRenderTargetContext()->discard(); 154 } 155 156 GrSemaphoresSubmitted SkSurface_Gpu::onFlush(int numSemaphores, 157 GrBackendSemaphore signalSemaphores[]) { 158 return fDevice->flushAndSignalSemaphores(numSemaphores, signalSemaphores); 159 } 160 161 bool SkSurface_Gpu::onWait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) { 162 return fDevice->wait(numSemaphores, waitSemaphores); 163 } 164 165 bool SkSurface_Gpu::onCharacterize(SkSurfaceCharacterization* characterization) const { 166 GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext(); 167 GrContext* ctx = fDevice->context(); 168 169 int maxResourceCount; 170 size_t maxResourceBytes; 171 ctx->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes); 172 173 bool mipmapped = rtc->asTextureProxy() ? GrMipMapped::kYes == rtc->asTextureProxy()->mipMapped() 174 : false; 175 176 // TODO: the addition of colorType to the surfaceContext should remove this calculation 177 SkColorType ct; 178 if (!GrPixelConfigToColorType(rtc->colorSpaceInfo().config(), &ct)) { 179 return false; 180 } 181 182 bool usesGLFBO0 = rtc->asRenderTargetProxy()->rtPriv().glRTFBOIDIs0(); 183 // We should never get in the situation where we have a texture render target that is also 184 // backend by FBO 0. 185 SkASSERT(!usesGLFBO0 || !SkToBool(rtc->asTextureProxy())); 186 187 SkImageInfo ii = SkImageInfo::Make(rtc->width(), rtc->height(), ct, kPremul_SkAlphaType, 188 rtc->colorSpaceInfo().refColorSpace()); 189 190 characterization->set(ctx->threadSafeProxy(), maxResourceBytes, ii, rtc->origin(), 191 rtc->colorSpaceInfo().config(), rtc->fsaaType(), rtc->numStencilSamples(), 192 SkSurfaceCharacterization::Textureable(SkToBool(rtc->asTextureProxy())), 193 SkSurfaceCharacterization::MipMapped(mipmapped), 194 SkSurfaceCharacterization::UsesGLFBO0(usesGLFBO0), 195 SkSurfaceCharacterization::VulkanSecondaryCBCompatible(false), 196 this->props()); 197 198 return true; 199 } 200 201 bool SkSurface_Gpu::isCompatible(const SkSurfaceCharacterization& characterization) const { 202 GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext(); 203 GrContext* ctx = fDevice->context(); 204 205 if (!characterization.isValid()) { 206 return false; 207 } 208 209 // As long as the current state if the context allows for greater or equal resources, 210 // we allow the DDL to be replayed. 211 // DDL TODO: should we just remove the resource check and ignore the cache limits on playback? 212 int maxResourceCount; 213 size_t maxResourceBytes; 214 ctx->getResourceCacheLimits(&maxResourceCount, &maxResourceBytes); 215 216 if (characterization.isTextureable()) { 217 if (!rtc->asTextureProxy()) { 218 // If the characterization was textureable we require the replay dest to also be 219 // textureable. If the characterized surface wasn't textureable we allow the replay 220 // dest to be textureable. 221 return false; 222 } 223 224 if (characterization.isMipMapped() && 225 GrMipMapped::kNo == rtc->asTextureProxy()->mipMapped()) { 226 // Fail if the DDL's surface was mipmapped but the replay surface is not. 227 // Allow drawing to proceed if the DDL was not mipmapped but the replay surface is. 228 return false; 229 } 230 } 231 232 if (characterization.usesGLFBO0() != rtc->asRenderTargetProxy()->rtPriv().glRTFBOIDIs0()) { 233 return false; 234 } 235 236 // TODO: the addition of colorType to the surfaceContext should remove this calculation 237 SkColorType rtcColorType; 238 if (!GrPixelConfigToColorType(rtc->colorSpaceInfo().config(), &rtcColorType)) { 239 return false; 240 } 241 242 return characterization.contextInfo() && characterization.contextInfo()->matches(ctx) && 243 characterization.cacheMaxResourceBytes() <= maxResourceBytes && 244 characterization.origin() == rtc->origin() && 245 characterization.config() == rtc->colorSpaceInfo().config() && 246 characterization.width() == rtc->width() && 247 characterization.height() == rtc->height() && 248 characterization.colorType() == rtcColorType && 249 characterization.fsaaType() == rtc->fsaaType() && 250 characterization.stencilCount() == rtc->numStencilSamples() && 251 SkColorSpace::Equals(characterization.colorSpace(), 252 rtc->colorSpaceInfo().colorSpace()) && 253 characterization.surfaceProps() == rtc->surfaceProps(); 254 } 255 256 bool SkSurface_Gpu::onDraw(const SkDeferredDisplayList* ddl) { 257 if (!ddl || !this->isCompatible(ddl->characterization())) { 258 return false; 259 } 260 261 GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext(); 262 GrContext* ctx = fDevice->context(); 263 264 ctx->contextPriv().copyOpListsFromDDL(ddl, rtc->asRenderTargetProxy()); 265 return true; 266 } 267 268 269 /////////////////////////////////////////////////////////////////////////////// 270 271 bool SkSurface_Gpu::Valid(const SkImageInfo& info) { 272 return true; 273 } 274 275 bool SkSurface_Gpu::Valid(const GrCaps* caps, GrPixelConfig config, SkColorSpace* colorSpace) { 276 switch (config) { 277 case kSRGBA_8888_GrPixelConfig: 278 case kSBGRA_8888_GrPixelConfig: 279 return caps->srgbSupport(); 280 default: 281 return true; 282 } 283 } 284 285 sk_sp<SkSurface> SkSurface::MakeRenderTarget(GrContext* context, 286 const SkSurfaceCharacterization& c, 287 SkBudgeted budgeted) { 288 if (!context || !c.isValid()) { 289 return nullptr; 290 } 291 292 if (c.usesGLFBO0()) { 293 // If we are making the surface we will never use FBO0. 294 return nullptr; 295 } 296 297 if (!SkSurface_Gpu::Valid(context->contextPriv().caps(), c.config(), c.colorSpace())) { 298 return nullptr; 299 } 300 301 // In order to ensure compatibility we have to match the backend format (i.e. the GrPixelConfig 302 // of the characterization) 303 GrSurfaceDesc desc; 304 desc.fFlags = kRenderTarget_GrSurfaceFlag; 305 desc.fWidth = c.width(); 306 desc.fHeight = c.height(); 307 desc.fConfig = c.config(); 308 desc.fSampleCnt = c.stencilCount(); 309 310 const GrBackendFormat format = 311 context->contextPriv().caps()->getBackendFormatFromColorType(c.colorType()); 312 313 sk_sp<GrSurfaceContext> sc( 314 context->contextPriv().makeDeferredSurfaceContext(format, desc, c.origin(), 315 GrMipMapped(c.isMipMapped()), 316 SkBackingFit::kExact, budgeted, 317 c.refColorSpace(), 318 &c.surfaceProps())); 319 if (!sc || !sc->asRenderTargetContext()) { 320 return nullptr; 321 } 322 323 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, sk_ref_sp(sc->asRenderTargetContext()), 324 c.width(), c.height(), 325 SkGpuDevice::kClear_InitContents)); 326 if (!device) { 327 return nullptr; 328 } 329 330 sk_sp<SkSurface> s = sk_make_sp<SkSurface_Gpu>(std::move(device)); 331 #ifdef SK_DEBUG 332 if (s) { 333 SkSurface_Gpu* gpuSurface = static_cast<SkSurface_Gpu*>(s.get()); 334 SkASSERT(gpuSurface->isCompatible(c)); 335 } 336 #endif 337 338 return s; 339 } 340 341 342 sk_sp<SkSurface> SkSurface::MakeRenderTarget(GrContext* ctx, SkBudgeted budgeted, 343 const SkImageInfo& info, int sampleCount, 344 GrSurfaceOrigin origin, const SkSurfaceProps* props, 345 bool shouldCreateWithMips) { 346 if (!ctx) { 347 return nullptr; 348 } 349 if (!SkSurface_Gpu::Valid(info)) { 350 return nullptr; 351 } 352 sampleCount = SkTMax(1, sampleCount); 353 GrMipMapped mipMapped = shouldCreateWithMips ? GrMipMapped::kYes : GrMipMapped::kNo; 354 355 if (!ctx->contextPriv().caps()->mipMapSupport()) { 356 mipMapped = GrMipMapped::kNo; 357 } 358 359 sk_sp<SkGpuDevice> device(SkGpuDevice::Make( 360 ctx, budgeted, info, sampleCount, origin, props, mipMapped, 361 SkGpuDevice::kClear_InitContents)); 362 if (!device) { 363 return nullptr; 364 } 365 return sk_make_sp<SkSurface_Gpu>(std::move(device)); 366 } 367 368 sk_sp<SkSurface> SkSurface_Gpu::MakeWrappedRenderTarget(GrContext* context, 369 sk_sp<GrRenderTargetContext> rtc) { 370 if (!context) { 371 return nullptr; 372 } 373 374 int w = rtc->width(); 375 int h = rtc->height(); 376 sk_sp<SkGpuDevice> device( 377 SkGpuDevice::Make(context, std::move(rtc), w, h, SkGpuDevice::kUninit_InitContents)); 378 if (!device) { 379 return nullptr; 380 } 381 382 return sk_make_sp<SkSurface_Gpu>(std::move(device)); 383 } 384 385 bool validate_backend_texture(GrContext* ctx, const GrBackendTexture& tex, GrPixelConfig* config, 386 int sampleCnt, SkColorType ct, sk_sp<SkColorSpace> cs, 387 bool texturable) { 388 if (!tex.isValid()) { 389 return false; 390 } 391 // TODO: Create a SkImageColorInfo struct for color, alpha, and color space so we don't need to 392 // create a fake image info here. 393 SkImageInfo info = SkImageInfo::Make(1, 1, ct, kPremul_SkAlphaType, cs); 394 395 if (!SkSurface_Gpu::Valid(info)) { 396 return false; 397 } 398 399 GrBackendFormat backendFormat = tex.getBackendFormat(); 400 if (!backendFormat.isValid()) { 401 return false; 402 } 403 *config = ctx->contextPriv().caps()->getConfigFromBackendFormat(backendFormat, ct); 404 if (*config == kUnknown_GrPixelConfig) { 405 return false; 406 } 407 408 // We don't require that the client gave us an exact valid sample cnt. However, it must be 409 // less than the max supported sample count and 1 if MSAA is unsupported for the color type. 410 if (!ctx->contextPriv().caps()->getRenderTargetSampleCount(sampleCnt, *config)) { 411 return false; 412 } 413 414 if (texturable && !ctx->contextPriv().caps()->isConfigTexturable(*config)) { 415 return false; 416 } 417 return true; 418 } 419 420 sk_sp<SkSurface> SkSurface::MakeFromBackendTexture(GrContext* context, const GrBackendTexture& tex, 421 GrSurfaceOrigin origin, int sampleCnt, 422 SkColorType colorType, 423 sk_sp<SkColorSpace> colorSpace, 424 const SkSurfaceProps* props) { 425 if (!context) { 426 return nullptr; 427 } 428 sampleCnt = SkTMax(1, sampleCnt); 429 GrBackendTexture texCopy = tex; 430 if (!validate_backend_texture(context, texCopy, &texCopy.fConfig, 431 sampleCnt, colorType, colorSpace, true)) { 432 return nullptr; 433 } 434 435 if (!context) { 436 return nullptr; 437 } 438 if (!SkSurface_Gpu::Valid(context->contextPriv().caps(), texCopy.config(), colorSpace.get())) { 439 return nullptr; 440 } 441 sampleCnt = SkTMax(1, sampleCnt); 442 443 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeBackendTextureRenderTargetContext( 444 texCopy, 445 origin, 446 sampleCnt, 447 std::move(colorSpace), 448 props)); 449 if (!rtc) { 450 return nullptr; 451 } 452 453 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), texCopy.width(), 454 texCopy.height(), 455 SkGpuDevice::kUninit_InitContents)); 456 if (!device) { 457 return nullptr; 458 } 459 return sk_make_sp<SkSurface_Gpu>(std::move(device)); 460 } 461 462 bool validate_backend_render_target(GrContext* ctx, const GrBackendRenderTarget& rt, 463 GrPixelConfig* config, SkColorType ct, sk_sp<SkColorSpace> cs) { 464 // TODO: Create a SkImageColorInfo struct for color, alpha, and color space so we don't need to 465 // create a fake image info here. 466 SkImageInfo info = SkImageInfo::Make(1, 1, ct, kPremul_SkAlphaType, cs); 467 468 if (!SkSurface_Gpu::Valid(info)) { 469 return false; 470 } 471 472 *config = ctx->contextPriv().caps()->validateBackendRenderTarget(rt, ct); 473 if (*config == kUnknown_GrPixelConfig) { 474 return false; 475 } 476 477 if (rt.sampleCnt() > 1) { 478 if (ctx->contextPriv().caps()->maxRenderTargetSampleCount(*config) <= 1) { 479 return false; 480 } 481 } else if (!ctx->contextPriv().caps()->isConfigRenderable(*config)) { 482 return false; 483 } 484 485 return true; 486 } 487 488 sk_sp<SkSurface> SkSurface::MakeFromBackendRenderTarget(GrContext* context, 489 const GrBackendRenderTarget& rt, 490 GrSurfaceOrigin origin, 491 SkColorType colorType, 492 sk_sp<SkColorSpace> colorSpace, 493 const SkSurfaceProps* props) { 494 if (!context) { 495 return nullptr; 496 } 497 498 GrBackendRenderTarget rtCopy = rt; 499 if (!validate_backend_render_target(context, rtCopy, &rtCopy.fConfig, colorType, colorSpace)) { 500 return nullptr; 501 } 502 if (!SkSurface_Gpu::Valid(context->contextPriv().caps(), rtCopy.config(), colorSpace.get())) { 503 return nullptr; 504 } 505 506 if (!context) { 507 return nullptr; 508 } 509 510 sk_sp<GrRenderTargetContext> rtc( 511 context->contextPriv().makeBackendRenderTargetRenderTargetContext( 512 rtCopy, origin, std::move(colorSpace), props)); 513 if (!rtc) { 514 return nullptr; 515 } 516 517 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), rtCopy.width(), 518 rtCopy.height(), 519 SkGpuDevice::kUninit_InitContents)); 520 if (!device) { 521 return nullptr; 522 } 523 524 return sk_make_sp<SkSurface_Gpu>(std::move(device)); 525 } 526 527 sk_sp<SkSurface> SkSurface::MakeFromBackendTextureAsRenderTarget(GrContext* context, 528 const GrBackendTexture& tex, 529 GrSurfaceOrigin origin, 530 int sampleCnt, 531 SkColorType colorType, 532 sk_sp<SkColorSpace> colorSpace, 533 const SkSurfaceProps* props) { 534 if (!context) { 535 return nullptr; 536 } 537 538 sampleCnt = SkTMax(1, sampleCnt); 539 GrBackendTexture texCopy = tex; 540 if (!validate_backend_texture(context, texCopy, &texCopy.fConfig, 541 sampleCnt, colorType, colorSpace, false)) { 542 return nullptr; 543 } 544 545 if (!SkSurface_Gpu::Valid(context->contextPriv().caps(), texCopy.config(), colorSpace.get())) { 546 return nullptr; 547 } 548 549 sk_sp<GrRenderTargetContext> rtc( 550 context->contextPriv().makeBackendTextureAsRenderTargetRenderTargetContext( 551 texCopy, 552 origin, 553 sampleCnt, 554 std::move(colorSpace), 555 props)); 556 if (!rtc) { 557 return nullptr; 558 } 559 560 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), tex.width(), tex.height(), 561 SkGpuDevice::kUninit_InitContents)); 562 if (!device) { 563 return nullptr; 564 } 565 return sk_make_sp<SkSurface_Gpu>(std::move(device)); 566 } 567 568 #endif 569