1/* 2 * Copyright 2017 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 "GrMtlGpu.h" 9 10#include "GrMtlBuffer.h" 11#include "GrMtlGpuCommandBuffer.h" 12#include "GrMtlTexture.h" 13#include "GrMtlTextureRenderTarget.h" 14#include "GrMtlUtil.h" 15#include "GrRenderTargetPriv.h" 16#include "GrTexturePriv.h" 17#include "SkConvertPixels.h" 18#include "SkSLCompiler.h" 19 20#import <simd/simd.h> 21 22#if !__has_feature(objc_arc) 23#error This file must be compiled with Arc. Use -fobjc-arc flag 24#endif 25 26static bool get_feature_set(id<MTLDevice> device, MTLFeatureSet* featureSet) { 27 // Mac OSX 28#ifdef SK_BUILD_FOR_MAC 29 if ([device supportsFeatureSet:MTLFeatureSet_OSX_GPUFamily1_v2]) { 30 *featureSet = MTLFeatureSet_OSX_GPUFamily1_v2; 31 return true; 32 } 33 if ([device supportsFeatureSet:MTLFeatureSet_OSX_GPUFamily1_v1]) { 34 *featureSet = MTLFeatureSet_OSX_GPUFamily1_v1; 35 return true; 36 } 37#endif 38 39 // iOS Family group 3 40#ifdef SK_BUILD_FOR_IOS 41 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2]) { 42 *featureSet = MTLFeatureSet_iOS_GPUFamily3_v2; 43 return true; 44 } 45 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) { 46 *featureSet = MTLFeatureSet_iOS_GPUFamily3_v1; 47 return true; 48 } 49 50 // iOS Family group 2 51 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v3]) { 52 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v3; 53 return true; 54 } 55 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2]) { 56 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v2; 57 return true; 58 } 59 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1]) { 60 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v1; 61 return true; 62 } 63 64 // iOS Family group 1 65 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v3]) { 66 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v3; 67 return true; 68 } 69 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) { 70 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v2; 71 return true; 72 } 73 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v1]) { 74 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v1; 75 return true; 76 } 77#endif 78 // No supported feature sets were found 79 return false; 80} 81 82sk_sp<GrGpu> GrMtlGpu::Make(GrContext* context, const GrContextOptions& options, 83 id<MTLDevice> device, id<MTLCommandQueue> queue) { 84 if (!device || !queue) { 85 return nullptr; 86 } 87 MTLFeatureSet featureSet; 88 if (!get_feature_set(device, &featureSet)) { 89 return nullptr; 90 } 91 return sk_sp<GrGpu>(new GrMtlGpu(context, options, device, queue, featureSet)); 92} 93 94GrMtlGpu::GrMtlGpu(GrContext* context, const GrContextOptions& options, 95 id<MTLDevice> device, id<MTLCommandQueue> queue, MTLFeatureSet featureSet) 96 : INHERITED(context) 97 , fDevice(device) 98 , fQueue(queue) 99 , fCompiler(new SkSL::Compiler()) 100 , fCopyManager(this) 101 , fResourceProvider(this) { 102 103 fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet)); 104 fCaps = fMtlCaps; 105 106 fCmdBuffer = [fQueue commandBuffer]; 107} 108 109GrGpuRTCommandBuffer* GrMtlGpu::getCommandBuffer( 110 GrRenderTarget* renderTarget, GrSurfaceOrigin origin, const SkRect& bounds, 111 const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo, 112 const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo) { 113 return new GrMtlGpuRTCommandBuffer(this, renderTarget, origin, bounds, colorInfo, stencilInfo); 114} 115 116GrGpuTextureCommandBuffer* GrMtlGpu::getCommandBuffer(GrTexture* texture, 117 GrSurfaceOrigin origin) { 118 return new GrMtlGpuTextureCommandBuffer(this, texture, origin); 119} 120 121void GrMtlGpu::submit(GrGpuCommandBuffer* buffer) { 122 delete buffer; 123} 124 125void GrMtlGpu::submitCommandBuffer(SyncQueue sync) { 126 SkASSERT(fCmdBuffer); 127 [fCmdBuffer commit]; 128 if (SyncQueue::kForce_SyncQueue == sync) { 129 [fCmdBuffer waitUntilCompleted]; 130 } 131 fCmdBuffer = [fQueue commandBuffer]; 132} 133 134sk_sp<GrBuffer> GrMtlGpu::onCreateBuffer(size_t size, GrBufferType type, 135 GrAccessPattern accessPattern, const void* data) { 136 return GrMtlBuffer::Make(this, size, type, accessPattern, data); 137} 138 139static bool check_max_blit_width(int widthInPixels) { 140 if (widthInPixels > 32767) { 141 SkASSERT(false); // surfaces should not be this wide anyway 142 return false; 143 } 144 return true; 145} 146 147bool GrMtlGpu::uploadToTexture(GrMtlTexture* tex, int left, int top, int width, int height, 148 GrColorType dataColorType, const GrMipLevel texels[], 149 int mipLevelCount) { 150 SkASSERT(this->caps()->isConfigTexturable(tex->config())); 151 if (!check_max_blit_width(width)) { 152 return false; 153 } 154 if (width == 0 || height == 0) { 155 return false; 156 } 157 if (GrPixelConfigToColorType(tex->config()) != dataColorType) { 158 return false; 159 } 160 161 id<MTLTexture> mtlTexture = tex->mtlTexture(); 162 SkASSERT(mtlTexture); 163 // Either upload only the first miplevel or all miplevels 164 SkASSERT(1 == mipLevelCount || mipLevelCount == (int)mtlTexture.mipmapLevelCount); 165 166 MTLTextureDescriptor* transferDesc = GrGetMTLTextureDescriptor(mtlTexture); 167 transferDesc.mipmapLevelCount = mipLevelCount; 168 transferDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined; 169#ifdef SK_BUILD_FOR_MAC 170 transferDesc.storageMode = MTLStorageModeManaged; 171#else 172 transferDesc.storageMode = MTLStorageModeShared; 173#endif 174 // TODO: implement some way of reusing transfer textures 175 id<MTLTexture> transferTexture = [fDevice newTextureWithDescriptor:transferDesc]; 176 SkASSERT(transferTexture); 177 178 int currentWidth = width; 179 int currentHeight = height; 180 size_t bpp = GrColorTypeBytesPerPixel(dataColorType); 181 MTLOrigin origin = MTLOriginMake(left, top, 0); 182 183 SkASSERT(mtlTexture.pixelFormat == transferTexture.pixelFormat); 184 SkASSERT(mtlTexture.sampleCount == transferTexture.sampleCount); 185 186 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder]; 187 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { 188 size_t rowBytes = texels[currentMipLevel].fRowBytes ? texels[currentMipLevel].fRowBytes 189 : bpp * currentWidth; 190 SkASSERT(texels[currentMipLevel].fPixels); 191 if (rowBytes < bpp * currentWidth || rowBytes % bpp) { 192 return false; 193 } 194 [transferTexture replaceRegion: MTLRegionMake2D(left, top, width, height) 195 mipmapLevel: currentMipLevel 196 withBytes: texels[currentMipLevel].fPixels 197 bytesPerRow: rowBytes]; 198 199 [blitCmdEncoder copyFromTexture: transferTexture 200 sourceSlice: 0 201 sourceLevel: currentMipLevel 202 sourceOrigin: origin 203 sourceSize: MTLSizeMake(width, height, 1) 204 toTexture: mtlTexture 205 destinationSlice: 0 206 destinationLevel: currentMipLevel 207 destinationOrigin: origin]; 208 currentWidth = SkTMax(1, currentWidth/2); 209 currentHeight = SkTMax(1, currentHeight/2); 210 } 211 [blitCmdEncoder endEncoding]; 212 213 if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) { 214 tex->texturePriv().markMipMapsDirty(); 215 } 216 return true; 217} 218 219GrStencilAttachment* GrMtlGpu::createStencilAttachmentForRenderTarget(const GrRenderTarget* rt, 220 int width, 221 int height) { 222 SkASSERT(width >= rt->width()); 223 SkASSERT(height >= rt->height()); 224 225 int samples = rt->numStencilSamples(); 226 227 const GrMtlCaps::StencilFormat& sFmt = this->mtlCaps().preferredStencilFormat(); 228 229 GrMtlStencilAttachment* stencil(GrMtlStencilAttachment::Create(this, 230 width, 231 height, 232 samples, 233 sFmt)); 234 fStats.incStencilAttachmentCreates(); 235 return stencil; 236} 237 238sk_sp<GrTexture> GrMtlGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted, 239 const GrMipLevel texels[], int mipLevelCount) { 240 int mipLevels = !mipLevelCount ? 1 : mipLevelCount; 241 242 if (!fMtlCaps->isConfigTexturable(desc.fConfig)) { 243 return nullptr; 244 } 245 MTLPixelFormat format; 246 if (!GrPixelConfigToMTLFormat(desc.fConfig, &format)) { 247 return nullptr; 248 } 249 250 if (GrPixelConfigIsCompressed(desc.fConfig)) { 251 return nullptr; // TODO: add compressed texture support 252 } 253 254 bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag); 255 256 // This TexDesc refers to the texture that will be read by the client. Thus even if msaa is 257 // requested, this TexDesc describes the resolved texture. Therefore we always have samples set 258 // to 1. 259 MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init]; 260 texDesc.textureType = MTLTextureType2D; 261 texDesc.pixelFormat = format; 262 texDesc.width = desc.fWidth; 263 texDesc.height = desc.fHeight; 264 texDesc.depth = 1; 265 texDesc.mipmapLevelCount = mipLevels; 266 texDesc.sampleCount = 1; 267 texDesc.arrayLength = 1; 268 texDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined; 269 // Make all textures have private gpu only access. We can use transfer buffers or textures 270 // to copy to them. 271 texDesc.storageMode = MTLStorageModePrivate; 272 texDesc.usage = MTLTextureUsageShaderRead; 273 texDesc.usage |= renderTarget ? MTLTextureUsageRenderTarget : 0; 274 275 GrMipMapsStatus mipMapsStatus = GrMipMapsStatus::kNotAllocated; 276 if (mipLevels > 1) { 277 mipMapsStatus = texels[0].fPixels ? GrMipMapsStatus::kValid : GrMipMapsStatus::kDirty; 278#ifdef SK_DEBUG 279 for (int i = 1; i < mipLevels; ++i) { 280 if (mipMapsStatus == GrMipMapsStatus::kValid) { 281 SkASSERT(texels[i].fPixels); 282 } else { 283 SkASSERT(!texels[i].fPixels); 284 } 285 } 286#endif 287 } 288 sk_sp<GrMtlTexture> tex; 289 if (renderTarget) { 290 tex = GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this, budgeted, 291 desc, texDesc, mipMapsStatus); 292 } else { 293 tex = GrMtlTexture::CreateNewTexture(this, budgeted, desc, texDesc, mipMapsStatus); 294 } 295 296 if (!tex) { 297 return nullptr; 298 } 299 300 auto colorType = GrPixelConfigToColorType(desc.fConfig); 301 if (mipLevelCount && texels[0].fPixels) { 302 if (!this->uploadToTexture(tex.get(), 0, 0, desc.fWidth, desc.fHeight, colorType, texels, 303 mipLevelCount)) { 304 tex->unref(); 305 return nullptr; 306 } 307 } 308 309 if (desc.fFlags & kPerformInitialClear_GrSurfaceFlag) { 310 // Do initial clear of the texture 311 } 312 return std::move(tex); 313} 314 315static id<MTLTexture> get_texture_from_backend(const GrBackendTexture& backendTex, 316 GrWrapOwnership ownership) { 317 GrMtlTextureInfo textureInfo; 318 if (!backendTex.getMtlTextureInfo(&textureInfo)) { 319 return nil; 320 } 321 return GrGetMTLTexture(textureInfo.fTexture, ownership); 322} 323 324static id<MTLTexture> get_texture_from_backend(const GrBackendRenderTarget& backendRT) { 325 GrMtlTextureInfo textureInfo; 326 if (!backendRT.getMtlTextureInfo(&textureInfo)) { 327 return nil; 328 } 329 return GrGetMTLTexture(textureInfo.fTexture, GrWrapOwnership::kBorrow_GrWrapOwnership); 330} 331 332static inline void init_surface_desc(GrSurfaceDesc* surfaceDesc, id<MTLTexture> mtlTexture, 333 bool isRenderTarget, GrPixelConfig config) { 334 if (isRenderTarget) { 335 SkASSERT(MTLTextureUsageRenderTarget & mtlTexture.usage); 336 } 337 surfaceDesc->fFlags = isRenderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags; 338 surfaceDesc->fWidth = mtlTexture.width; 339 surfaceDesc->fHeight = mtlTexture.height; 340 surfaceDesc->fConfig = config; 341 surfaceDesc->fSampleCnt = 1; 342} 343 344sk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendTex, 345 GrWrapOwnership ownership, 346 GrWrapCacheable cacheable, GrIOType ioType) { 347 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership); 348 if (!mtlTexture) { 349 return nullptr; 350 } 351 352 GrSurfaceDesc surfDesc; 353 init_surface_desc(&surfDesc, mtlTexture, false, backendTex.config()); 354 355 return GrMtlTexture::MakeWrappedTexture(this, surfDesc, mtlTexture, cacheable, ioType); 356} 357 358sk_sp<GrTexture> GrMtlGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex, 359 int sampleCnt, 360 GrWrapOwnership ownership, 361 GrWrapCacheable cacheable) { 362 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership); 363 if (!mtlTexture) { 364 return nullptr; 365 } 366 367 GrSurfaceDesc surfDesc; 368 init_surface_desc(&surfDesc, mtlTexture, true, backendTex.config()); 369 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig); 370 if (!surfDesc.fSampleCnt) { 371 return nullptr; 372 } 373 374 return GrMtlTextureRenderTarget::MakeWrappedTextureRenderTarget(this, surfDesc, mtlTexture, 375 cacheable); 376} 377 378sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& backendRT) { 379 // TODO: Revisit this when the Metal backend is completed. It may support MSAA render targets. 380 if (backendRT.sampleCnt() > 1) { 381 return nullptr; 382 } 383 id<MTLTexture> mtlTexture = get_texture_from_backend(backendRT); 384 if (!mtlTexture) { 385 return nullptr; 386 } 387 388 GrSurfaceDesc surfDesc; 389 init_surface_desc(&surfDesc, mtlTexture, true, backendRT.config()); 390 391 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture); 392} 393 394sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendTextureAsRenderTarget( 395 const GrBackendTexture& backendTex, int sampleCnt) { 396 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, 397 GrWrapOwnership::kBorrow_GrWrapOwnership); 398 if (!mtlTexture) { 399 return nullptr; 400 } 401 402 GrSurfaceDesc surfDesc; 403 init_surface_desc(&surfDesc, mtlTexture, true, backendTex.config()); 404 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig); 405 if (!surfDesc.fSampleCnt) { 406 return nullptr; 407 } 408 409 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture); 410} 411 412#ifdef GR_TEST_UTILS 413bool GrMtlGpu::createTestingOnlyMtlTextureInfo(GrColorType colorType, int w, int h, bool texturable, 414 bool renderable, GrMipMapped mipMapped, 415 const void* srcData, size_t srcRowBytes, 416 GrMtlTextureInfo* info) { 417 SkASSERT(texturable || renderable); 418 if (!texturable) { 419 SkASSERT(GrMipMapped::kNo == mipMapped); 420 SkASSERT(!srcData); 421 } 422 423 GrPixelConfig config = GrColorTypeToPixelConfig(colorType, GrSRGBEncoded::kNo); 424 425 MTLPixelFormat format; 426 if (!GrPixelConfigToMTLFormat(config, &format)) { 427 return false; 428 } 429 if (texturable && !fMtlCaps->isConfigTexturable(config)) { 430 return false; 431 } 432 if (renderable && !fMtlCaps->isConfigRenderable(config)) { 433 return false; 434 } 435 // Currently we don't support uploading pixel data when mipped. 436 if (srcData && GrMipMapped::kYes == mipMapped) { 437 return false; 438 } 439 if(!check_max_blit_width(w)) { 440 return false; 441 } 442 443 bool mipmapped = mipMapped == GrMipMapped::kYes ? true : false; 444 MTLTextureDescriptor* desc = 445 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format 446 width: w 447 height: h 448 mipmapped: mipmapped]; 449 desc.cpuCacheMode = MTLCPUCacheModeWriteCombined; 450 desc.storageMode = MTLStorageModePrivate; 451 desc.usage = texturable ? MTLTextureUsageShaderRead : 0; 452 desc.usage |= renderable ? MTLTextureUsageRenderTarget : 0; 453 id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor: desc]; 454 455 SkAutoTMalloc<GrColor> srcBuffer; 456 if (!srcData) { 457 srcBuffer.reset(w * h); 458 memset(srcBuffer, 0, w * h * sizeof(GrColor)); 459 srcData = srcBuffer; 460 } 461 SkASSERT(srcData); 462#ifdef SK_BUILD_FOR_MAC 463 desc.storageMode = MTLStorageModeManaged; 464#else 465 desc.storageMode = MTLStorageModeShared; 466#endif 467 id<MTLTexture> transferTexture = [fDevice newTextureWithDescriptor: desc]; 468 size_t trimRowBytes = w * GrColorTypeBytesPerPixel(colorType); 469 if (!srcRowBytes) { 470 srcRowBytes = trimRowBytes; 471 } 472 473 MTLOrigin origin = MTLOriginMake(0, 0, 0); 474 475 SkASSERT(testTexture.pixelFormat == transferTexture.pixelFormat); 476 SkASSERT(testTexture.sampleCount == transferTexture.sampleCount); 477 478 id<MTLCommandBuffer> cmdBuffer = [fQueue commandBuffer]; 479 id<MTLBlitCommandEncoder> blitCmdEncoder = [cmdBuffer blitCommandEncoder]; 480 int currentWidth = w; 481 int currentHeight = h; 482 for (int mipLevel = 0; mipLevel < (int)testTexture.mipmapLevelCount; mipLevel++) { 483 [transferTexture replaceRegion: MTLRegionMake2D(0, 0, currentWidth, currentHeight) 484 mipmapLevel: mipLevel 485 withBytes: srcData 486 bytesPerRow: srcRowBytes]; 487 488 [blitCmdEncoder copyFromTexture: transferTexture 489 sourceSlice: 0 490 sourceLevel: mipLevel 491 sourceOrigin: origin 492 sourceSize: MTLSizeMake(currentWidth, currentHeight, 1) 493 toTexture: testTexture 494 destinationSlice: 0 495 destinationLevel: mipLevel 496 destinationOrigin: origin]; 497 currentWidth = SkTMax(1, currentWidth/2); 498 currentHeight = SkTMax(1, currentHeight/2); 499 } 500 [blitCmdEncoder endEncoding]; 501 [cmdBuffer commit]; 502 [cmdBuffer waitUntilCompleted]; 503 504 info->fTexture = GrReleaseId(testTexture); 505 return true; 506} 507 508GrBackendTexture GrMtlGpu::createTestingOnlyBackendTexture(const void* pixels, int w, int h, 509 GrColorType colorType, bool isRT, 510 GrMipMapped mipMapped, size_t rowBytes) { 511 if (w > this->caps()->maxTextureSize() || h > this->caps()->maxTextureSize()) { 512 return GrBackendTexture(); 513 } 514 GrMtlTextureInfo info; 515 if (!this->createTestingOnlyMtlTextureInfo(colorType, w, h, true, isRT, mipMapped, pixels, 516 rowBytes, &info)) { 517 return {}; 518 } 519 520 GrPixelConfig config = GrColorTypeToPixelConfig(colorType, GrSRGBEncoded::kNo); 521 522 GrBackendTexture backendTex(w, h, mipMapped, info); 523 backendTex.fConfig = config; 524 return backendTex; 525} 526 527bool GrMtlGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const { 528 SkASSERT(GrBackendApi::kMetal == tex.backend()); 529 530 GrMtlTextureInfo info; 531 if (!tex.getMtlTextureInfo(&info)) { 532 return false; 533 } 534 id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture, 535 GrWrapOwnership::kBorrow_GrWrapOwnership); 536 if (!mtlTexture) { 537 return false; 538 } 539 return mtlTexture.usage & MTLTextureUsageShaderRead; 540} 541 542void GrMtlGpu::deleteTestingOnlyBackendTexture(const GrBackendTexture& tex) { 543 SkASSERT(GrBackendApi::kMetal == tex.fBackend); 544 545 GrMtlTextureInfo info; 546 if (tex.getMtlTextureInfo(&info)) { 547 // Adopts the metal texture so that ARC will clean it up. 548 GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership); 549 } 550} 551 552GrBackendRenderTarget GrMtlGpu::createTestingOnlyBackendRenderTarget(int w, int h, GrColorType ct) { 553 if (w > this->caps()->maxRenderTargetSize() || h > this->caps()->maxRenderTargetSize()) { 554 return GrBackendRenderTarget(); 555 } 556 557 GrMtlTextureInfo info; 558 if (!this->createTestingOnlyMtlTextureInfo(ct, w, h, false, true, GrMipMapped::kNo, nullptr, 559 0, &info)) { 560 return {}; 561 } 562 563 GrPixelConfig config = GrColorTypeToPixelConfig(ct, GrSRGBEncoded::kNo); 564 565 GrBackendRenderTarget backendRT(w, h, 1, info); 566 backendRT.fConfig = config; 567 return backendRT; 568} 569 570void GrMtlGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget& rt) { 571 SkASSERT(GrBackendApi::kMetal == rt.fBackend); 572 573 GrMtlTextureInfo info; 574 if (rt.getMtlTextureInfo(&info)) { 575 this->testingOnly_flushGpuAndSync(); 576 // Adopts the metal texture so that ARC will clean it up. 577 GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership); 578 } 579} 580 581void GrMtlGpu::testingOnly_flushGpuAndSync() { 582 this->submitCommandBuffer(kForce_SyncQueue); 583} 584#endif // GR_TEST_UTILS 585 586static int get_surface_sample_cnt(GrSurface* surf) { 587 if (const GrRenderTarget* rt = surf->asRenderTarget()) { 588 return rt->numColorSamples(); 589 } 590 return 0; 591} 592 593bool GrMtlGpu::copySurfaceAsBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin, 594 GrSurface* src, GrSurfaceOrigin srcOrigin, 595 const SkIRect& srcRect, const SkIPoint& dstPoint) { 596#ifdef SK_DEBUG 597 int dstSampleCnt = get_surface_sample_cnt(dst); 598 int srcSampleCnt = get_surface_sample_cnt(src); 599 SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin, 600 src->config(), srcSampleCnt, srcOrigin, 601 srcRect, dstPoint, dst == src)); 602#endif 603 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false); 604 id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src, false); 605 606 // Flip rect if necessary 607 SkIRect srcMtlRect; 608 srcMtlRect.fLeft = srcRect.fLeft; 609 srcMtlRect.fRight = srcRect.fRight; 610 SkIRect dstRect; 611 dstRect.fLeft = dstPoint.fX; 612 dstRect.fRight = dstPoint.fX + srcRect.width(); 613 614 if (kBottomLeft_GrSurfaceOrigin == srcOrigin) { 615 srcMtlRect.fTop = srcTex.height - srcRect.fBottom; 616 srcMtlRect.fBottom = srcTex.height - srcRect.fTop; 617 } else { 618 srcMtlRect.fTop = srcRect.fTop; 619 srcMtlRect.fBottom = srcRect.fBottom; 620 } 621 622 if (kBottomLeft_GrSurfaceOrigin == dstOrigin) { 623 dstRect.fTop = dstTex.height - dstPoint.fY - srcMtlRect.height(); 624 } else { 625 dstRect.fTop = dstPoint.fY; 626 } 627 dstRect.fBottom = dstRect.fTop + srcMtlRect.height(); 628 629 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder]; 630 [blitCmdEncoder copyFromTexture: srcTex 631 sourceSlice: 0 632 sourceLevel: 0 633 sourceOrigin: MTLOriginMake(srcMtlRect.x(), srcMtlRect.y(), 0) 634 sourceSize: MTLSizeMake(srcMtlRect.width(), srcMtlRect.height(), 1) 635 toTexture: dstTex 636 destinationSlice: 0 637 destinationLevel: 0 638 destinationOrigin: MTLOriginMake(dstRect.x(), dstRect.y(), 0)]; 639 [blitCmdEncoder endEncoding]; 640 641 return true; 642} 643 644bool GrMtlGpu::copySurfaceAsDrawThenBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin, 645 GrSurface* src, GrSurfaceOrigin srcOrigin, 646 const SkIRect& srcRect, const SkIPoint& dstPoint) { 647#ifdef SK_DEBUG 648 int dstSampleCnt = get_surface_sample_cnt(dst); 649 int srcSampleCnt = get_surface_sample_cnt(src); 650 SkASSERT(dstSampleCnt == 0); // dst shouldn't be a render target 651 SkASSERT(!this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin, 652 src->config(), srcSampleCnt, srcOrigin, 653 srcRect, dstPoint, dst == src)); 654 SkASSERT(!this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()), 655 src->config(), SkToBool(src->asTexture()))); 656 SkASSERT(this->mtlCaps().canCopyAsDrawThenBlit(dst->config(),src->config(), 657 SkToBool(src->asTexture()))); 658#endif 659 GrSurfaceDesc surfDesc; 660 surfDesc.fFlags = kRenderTarget_GrSurfaceFlag; 661 surfDesc.fWidth = srcRect.width(); 662 surfDesc.fHeight = srcRect.height(); 663 surfDesc.fConfig = dst->config(); 664 surfDesc.fSampleCnt = 1; 665 666 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false); 667 MTLTextureDescriptor* textureDesc = GrGetMTLTextureDescriptor(dstTex); 668 textureDesc.width = srcRect.width(); 669 textureDesc.height = srcRect.height(); 670 textureDesc.mipmapLevelCount = 1; 671 textureDesc.usage |= MTLTextureUsageRenderTarget; 672 673 sk_sp<GrMtlTexture> transferTexture = 674 GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this, 675 SkBudgeted::kYes, 676 surfDesc, 677 textureDesc, 678 GrMipMapsStatus::kNotAllocated); 679 680 GrSurfaceOrigin transferOrigin = dstOrigin; 681 SkASSERT(this->mtlCaps().canCopyAsDraw(transferTexture->config(), 682 SkToBool(transferTexture->asRenderTarget()), 683 src->config(), 684 SkToBool(src->asTexture()))); 685 // TODO: Eventually we will need to handle resolves either in this function or make a separate 686 // copySurfaceAsResolveThenBlit(). 687 if (!this->copySurface(transferTexture.get(), transferOrigin, 688 src, srcOrigin, 689 srcRect, SkIPoint::Make(0, 0))) { 690 return false; 691 } 692 693 SkIRect transferRect = SkIRect::MakeXYWH(0, 0, srcRect.width(), srcRect.height()); 694 SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(), 695 get_surface_sample_cnt(dst), 696 dstOrigin, 697 transferTexture->config(), 698 get_surface_sample_cnt(transferTexture.get()), 699 transferOrigin, 700 transferRect, dstPoint, false)); 701 if (!this->copySurface(dst, dstOrigin, 702 transferTexture.get(), transferOrigin, 703 transferRect, dstPoint)) { 704 return false; 705 } 706 return true; 707} 708 709bool GrMtlGpu::onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin, 710 GrSurface* src, GrSurfaceOrigin srcOrigin, 711 const SkIRect& srcRect, 712 const SkIPoint& dstPoint, 713 bool canDiscardOutsideDstRect) { 714 715 GrPixelConfig dstConfig = dst->config(); 716 GrPixelConfig srcConfig = src->config(); 717 718 int dstSampleCnt = get_surface_sample_cnt(dst); 719 int srcSampleCnt = get_surface_sample_cnt(src); 720 721 if (dstSampleCnt > 1 || srcSampleCnt > 1) { 722 SkASSERT(false); // Currently dont support MSAA. TODO: add copySurfaceAsResolve(). 723 return false; 724 } 725 726 bool success = false; 727 if (this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()), 728 src->config(), SkToBool(src->asTexture()))) { 729 success = fCopyManager.copySurfaceAsDraw(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint, 730 canDiscardOutsideDstRect); 731 } else if (this->mtlCaps().canCopyAsBlit(dstConfig, dstSampleCnt, dstOrigin, 732 srcConfig, srcSampleCnt, srcOrigin, 733 srcRect, dstPoint, dst == src)) { 734 success = this->copySurfaceAsBlit(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint); 735 } else if (this->mtlCaps().canCopyAsDrawThenBlit(dst->config(), src->config(), 736 SkToBool(src->asTexture()))) { 737 success = this->copySurfaceAsDrawThenBlit(dst, dstOrigin, src, srcOrigin, 738 srcRect, dstPoint); 739 } 740 if (success) { 741 SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.x(), dstPoint.y(), 742 srcRect.width(), srcRect.height()); 743 this->didWriteToSurface(dst, dstOrigin, &dstRect); 744 } 745 return success; 746} 747 748bool GrMtlGpu::onWritePixels(GrSurface* surface, int left, int top, int width, int height, 749 GrColorType srcColorType, const GrMipLevel texels[], 750 int mipLevelCount) { 751 GrMtlTexture* mtlTexture = static_cast<GrMtlTexture*>(surface->asTexture()); 752 if (!mtlTexture) { 753 return false; 754 } 755 if (!mipLevelCount) { 756 return false; 757 } 758#ifdef SK_DEBUG 759 for (int i = 0; i < mipLevelCount; i++) { 760 SkASSERT(texels[i].fPixels); 761 } 762#endif 763 return this->uploadToTexture(mtlTexture, left, top, width, height, srcColorType, texels, 764 mipLevelCount); 765} 766 767bool GrMtlGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int height, 768 GrColorType dstColorType, void* buffer, size_t rowBytes) { 769 SkASSERT(surface); 770 if (!check_max_blit_width(width)) { 771 return false; 772 } 773 if (GrPixelConfigToColorType(surface->config()) != dstColorType) { 774 return false; 775 } 776 777 bool doResolve = get_surface_sample_cnt(surface) > 1; 778 id<MTLTexture> mtlTexture = GrGetMTLTextureFromSurface(surface, doResolve); 779 if (!mtlTexture) { 780 return false; 781 } 782 783 int bpp = GrColorTypeBytesPerPixel(dstColorType); 784 size_t transBufferRowBytes = bpp * width; 785 size_t transBufferImageBytes = transBufferRowBytes * height; 786 787 // TODO: implement some way of reusing buffers instead of making a new one every time. 788 id<MTLBuffer> transferBuffer = [fDevice newBufferWithLength: transBufferImageBytes 789 options: MTLResourceStorageModeShared]; 790 791 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder]; 792 [blitCmdEncoder copyFromTexture: mtlTexture 793 sourceSlice: 0 794 sourceLevel: 0 795 sourceOrigin: MTLOriginMake(left, top, 0) 796 sourceSize: MTLSizeMake(width, height, 1) 797 toBuffer: transferBuffer 798 destinationOffset: 0 799 destinationBytesPerRow: transBufferRowBytes 800 destinationBytesPerImage: transBufferImageBytes]; 801 [blitCmdEncoder endEncoding]; 802 803 this->submitCommandBuffer(kForce_SyncQueue); 804 const void* mappedMemory = transferBuffer.contents; 805 806 SkRectMemcpy(buffer, rowBytes, mappedMemory, transBufferRowBytes, transBufferRowBytes, height); 807 return true; 808} 809 810