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