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 "src/gpu/mtl/GrMtlGpu.h" 9 10#include "src/core/SkConvertPixels.h" 11#include "src/core/SkMipMap.h" 12#include "src/gpu/GrDataUtils.h" 13#include "src/gpu/GrRenderTargetPriv.h" 14#include "src/gpu/GrTexturePriv.h" 15#include "src/gpu/mtl/GrMtlBuffer.h" 16#include "src/gpu/mtl/GrMtlCommandBuffer.h" 17#include "src/gpu/mtl/GrMtlGpuCommandBuffer.h" 18#include "src/gpu/mtl/GrMtlTexture.h" 19#include "src/gpu/mtl/GrMtlTextureRenderTarget.h" 20#include "src/gpu/mtl/GrMtlUtil.h" 21#include "src/sksl/SkSLCompiler.h" 22 23#import <simd/simd.h> 24 25#if !__has_feature(objc_arc) 26#error This file must be compiled with Arc. Use -fobjc-arc flag 27#endif 28 29static bool get_feature_set(id<MTLDevice> device, MTLFeatureSet* featureSet) { 30 // Mac OSX 31#ifdef SK_BUILD_FOR_MAC 32 if ([device supportsFeatureSet:MTLFeatureSet_OSX_GPUFamily1_v2]) { 33 *featureSet = MTLFeatureSet_OSX_GPUFamily1_v2; 34 return true; 35 } 36 if ([device supportsFeatureSet:MTLFeatureSet_OSX_GPUFamily1_v1]) { 37 *featureSet = MTLFeatureSet_OSX_GPUFamily1_v1; 38 return true; 39 } 40#endif 41 42 // iOS Family group 3 43#ifdef SK_BUILD_FOR_IOS 44 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2]) { 45 *featureSet = MTLFeatureSet_iOS_GPUFamily3_v2; 46 return true; 47 } 48 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) { 49 *featureSet = MTLFeatureSet_iOS_GPUFamily3_v1; 50 return true; 51 } 52 53 // iOS Family group 2 54 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v3]) { 55 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v3; 56 return true; 57 } 58 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2]) { 59 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v2; 60 return true; 61 } 62 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1]) { 63 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v1; 64 return true; 65 } 66 67 // iOS Family group 1 68 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v3]) { 69 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v3; 70 return true; 71 } 72 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) { 73 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v2; 74 return true; 75 } 76 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v1]) { 77 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v1; 78 return true; 79 } 80#endif 81 // No supported feature sets were found 82 return false; 83} 84 85sk_sp<GrGpu> GrMtlGpu::Make(GrContext* context, const GrContextOptions& options, 86 id<MTLDevice> device, id<MTLCommandQueue> queue) { 87 if (!device || !queue) { 88 return nullptr; 89 } 90 MTLFeatureSet featureSet; 91 if (!get_feature_set(device, &featureSet)) { 92 return nullptr; 93 } 94 return sk_sp<GrGpu>(new GrMtlGpu(context, options, device, queue, featureSet)); 95} 96 97GrMtlGpu::GrMtlGpu(GrContext* context, const GrContextOptions& options, 98 id<MTLDevice> device, id<MTLCommandQueue> queue, MTLFeatureSet featureSet) 99 : INHERITED(context) 100 , fDevice(device) 101 , fQueue(queue) 102 , fCmdBuffer(nullptr) 103 , fCompiler(new SkSL::Compiler()) 104 , fResourceProvider(this) 105 , fDisconnected(false) { 106 fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet)); 107 fCaps = fMtlCaps; 108} 109 110GrMtlGpu::~GrMtlGpu() { 111 if (!fDisconnected) { 112 this->destroyResources(); 113 } 114} 115 116void GrMtlGpu::disconnect(DisconnectType type) { 117 INHERITED::disconnect(type); 118 119 if (DisconnectType::kCleanup == type) { 120 this->destroyResources(); 121 } else { 122 delete fCmdBuffer; 123 fCmdBuffer = nullptr; 124 125 fResourceProvider.destroyResources(); 126 127 fQueue = nil; 128 fDevice = nil; 129 130 fDisconnected = true; 131 } 132} 133 134void GrMtlGpu::destroyResources() { 135 // Will implicitly delete the command buffer 136 this->submitCommandBuffer(SyncQueue::kForce_SyncQueue); 137 fResourceProvider.destroyResources(); 138 139 fQueue = nil; 140 fDevice = nil; 141} 142 143GrGpuRTCommandBuffer* GrMtlGpu::getCommandBuffer( 144 GrRenderTarget* renderTarget, GrSurfaceOrigin origin, const SkRect& bounds, 145 const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo, 146 const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo) { 147 return new GrMtlGpuRTCommandBuffer(this, renderTarget, origin, bounds, colorInfo, stencilInfo); 148} 149 150GrGpuTextureCommandBuffer* GrMtlGpu::getCommandBuffer(GrTexture* texture, 151 GrSurfaceOrigin origin) { 152 return new GrMtlGpuTextureCommandBuffer(this, texture, origin); 153} 154 155void GrMtlGpu::submit(GrGpuCommandBuffer* buffer) { 156 GrMtlGpuRTCommandBuffer* mtlRTCmdBuffer = 157 reinterpret_cast<GrMtlGpuRTCommandBuffer*>(buffer->asRTCommandBuffer()); 158 if (mtlRTCmdBuffer) { 159 mtlRTCmdBuffer->submit(); 160 } 161 delete buffer; 162} 163 164GrMtlCommandBuffer* GrMtlGpu::commandBuffer() { 165 if (!fCmdBuffer) { 166 fCmdBuffer = GrMtlCommandBuffer::Create(fQueue); 167 } 168 return fCmdBuffer; 169} 170 171void GrMtlGpu::submitCommandBuffer(SyncQueue sync) { 172 if (fCmdBuffer) { 173 fResourceProvider.addBufferCompletionHandler(fCmdBuffer); 174 fCmdBuffer->commit(SyncQueue::kForce_SyncQueue == sync); 175 delete fCmdBuffer; 176 fCmdBuffer = nullptr; 177 } 178} 179 180sk_sp<GrGpuBuffer> GrMtlGpu::onCreateBuffer(size_t size, GrGpuBufferType type, 181 GrAccessPattern accessPattern, const void* data) { 182 return GrMtlBuffer::Make(this, size, type, accessPattern, data); 183} 184 185static bool check_max_blit_width(int widthInPixels) { 186 if (widthInPixels > 32767) { 187 SkASSERT(false); // surfaces should not be this wide anyway 188 return false; 189 } 190 return true; 191} 192 193bool GrMtlGpu::uploadToTexture(GrMtlTexture* tex, int left, int top, int width, int height, 194 GrColorType dataColorType, const GrMipLevel texels[], 195 int mipLevelCount) { 196 SkASSERT(this->caps()->isFormatTexturable(tex->backendFormat())); 197 // The assumption is either that we have no mipmaps, or that our rect is the entire texture 198 SkASSERT(1 == mipLevelCount || 199 (0 == left && 0 == top && width == tex->width() && height == tex->height())); 200 201 // We assume that if the texture has mip levels, we either upload to all the levels or just the 202 // first. 203 SkASSERT(1 == mipLevelCount || mipLevelCount == (tex->texturePriv().maxMipMapLevel() + 1)); 204 205 if (!check_max_blit_width(width)) { 206 return false; 207 } 208 if (width == 0 || height == 0) { 209 return false; 210 } 211 if (GrPixelConfigToColorType(tex->config()) != dataColorType) { 212 return false; 213 } 214 215 id<MTLTexture> mtlTexture = tex->mtlTexture(); 216 SkASSERT(mtlTexture); 217 // Either upload only the first miplevel or all miplevels 218 SkASSERT(1 == mipLevelCount || mipLevelCount == (int)mtlTexture.mipmapLevelCount); 219 220 if (1 == mipLevelCount && !texels[0].fPixels) { 221 return true; // no data to upload 222 } 223 224 for (int i = 0; i < mipLevelCount; ++i) { 225 // We do not allow any gaps in the mip data 226 if (!texels[i].fPixels) { 227 return false; 228 } 229 } 230 231 // TODO: implement some way of reusing transfer buffers? 232 size_t bpp = GrColorTypeBytesPerPixel(dataColorType); 233 234 SkTArray<size_t> individualMipOffsets(mipLevelCount); 235 size_t combinedBufferSize = GrComputeTightCombinedBufferSize(bpp, width, height, 236 &individualMipOffsets, 237 mipLevelCount); 238 SkASSERT(combinedBufferSize); 239 240 size_t bufferOffset; 241 id<MTLBuffer> transferBuffer = this->resourceProvider().getDynamicBuffer(combinedBufferSize, 242 &bufferOffset); 243 if (!transferBuffer) { 244 return false; 245 } 246 char* buffer = (char*) transferBuffer.contents + bufferOffset; 247 248 int currentWidth = width; 249 int currentHeight = height; 250 int layerHeight = tex->height(); 251 MTLOrigin origin = MTLOriginMake(left, top, 0); 252 253 id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder(); 254 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { 255 if (texels[currentMipLevel].fPixels) { 256 SkASSERT(1 == mipLevelCount || currentHeight == layerHeight); 257 const size_t trimRowBytes = currentWidth * bpp; 258 const size_t rowBytes = texels[currentMipLevel].fRowBytes; 259 260 // copy data into the buffer, skipping any trailing bytes 261 char* dst = buffer + individualMipOffsets[currentMipLevel]; 262 const char* src = (const char*)texels[currentMipLevel].fPixels; 263 SkRectMemcpy(dst, trimRowBytes, src, rowBytes, trimRowBytes, currentHeight); 264 265 [blitCmdEncoder copyFromBuffer: transferBuffer 266 sourceOffset: bufferOffset + individualMipOffsets[currentMipLevel] 267 sourceBytesPerRow: trimRowBytes 268 sourceBytesPerImage: trimRowBytes*currentHeight 269 sourceSize: MTLSizeMake(currentWidth, currentHeight, 1) 270 toTexture: mtlTexture 271 destinationSlice: 0 272 destinationLevel: currentMipLevel 273 destinationOrigin: origin]; 274 } 275 currentWidth = SkTMax(1, currentWidth/2); 276 currentHeight = SkTMax(1, currentHeight/2); 277 layerHeight = currentHeight; 278 } 279#ifdef SK_BUILD_FOR_MAC 280 [transferBuffer didModifyRange: NSMakeRange(bufferOffset, combinedBufferSize)]; 281#endif 282 283 if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) { 284 tex->texturePriv().markMipMapsDirty(); 285 } 286 287 return true; 288} 289 290bool GrMtlGpu::clearTexture(GrMtlTexture* tex, GrColorType dataColorType, uint32_t levelMask) { 291 SkASSERT(this->caps()->isFormatTexturableAndUploadable(dataColorType, tex->backendFormat())); 292 293 if (!levelMask) { 294 return true; 295 } 296 297 id<MTLTexture> mtlTexture = tex->mtlTexture(); 298 SkASSERT(mtlTexture); 299 // Either upload only the first miplevel or all miplevels 300 int mipLevelCount = (int)mtlTexture.mipmapLevelCount; 301 302 // TODO: implement some way of reusing transfer buffers? 303 size_t bpp = GrColorTypeBytesPerPixel(dataColorType); 304 305 SkTArray<size_t> individualMipOffsets(mipLevelCount); 306 size_t combinedBufferSize = 0; 307 int currentWidth = tex->width(); 308 int currentHeight = tex->height(); 309 310 // The alignment must be at least 4 bytes and a multiple of the bytes per pixel of the image 311 // config. This works with the assumption that the bytes in pixel config is always a power of 2. 312 // TODO: can we just copy from a single buffer the size of the largest cleared level w/o a perf 313 // penalty? 314 SkASSERT((bpp & (bpp - 1)) == 0); 315 const size_t alignmentMask = 0x3 | (bpp - 1); 316 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { 317 if (levelMask & (1 << currentMipLevel)) { 318 const size_t trimmedSize = currentWidth * bpp * currentHeight; 319 const size_t alignmentDiff = combinedBufferSize & alignmentMask; 320 if (alignmentDiff != 0) { 321 combinedBufferSize += alignmentMask - alignmentDiff + 1; 322 } 323 individualMipOffsets.push_back(combinedBufferSize); 324 combinedBufferSize += trimmedSize; 325 } 326 currentWidth = SkTMax(1, currentWidth/2); 327 currentHeight = SkTMax(1, currentHeight/2); 328 } 329 SkASSERT(combinedBufferSize > 0 && !individualMipOffsets.empty()); 330 331 // TODO: Create GrMtlTransferBuffer 332 id<MTLBuffer> transferBuffer = [fDevice newBufferWithLength: combinedBufferSize 333 options: MTLResourceStorageModePrivate]; 334 if (nil == transferBuffer) { 335 return false; 336 } 337 338 id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder(); 339 // clear the buffer to transparent black 340 NSRange clearRange; 341 clearRange.location = 0; 342 clearRange.length = combinedBufferSize; 343 [blitCmdEncoder fillBuffer: transferBuffer 344 range: clearRange 345 value: 0]; 346 347 // now copy buffer to texture 348 currentWidth = tex->width(); 349 currentHeight = tex->height(); 350 MTLOrigin origin = MTLOriginMake(0, 0, 0); 351 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { 352 if (levelMask & (1 << currentMipLevel)) { 353 const size_t rowBytes = currentWidth * bpp; 354 355 [blitCmdEncoder copyFromBuffer: transferBuffer 356 sourceOffset: individualMipOffsets[currentMipLevel] 357 sourceBytesPerRow: rowBytes 358 sourceBytesPerImage: rowBytes * currentHeight 359 sourceSize: MTLSizeMake(currentWidth, currentHeight, 1) 360 toTexture: mtlTexture 361 destinationSlice: 0 362 destinationLevel: currentMipLevel 363 destinationOrigin: origin]; 364 } 365 currentWidth = SkTMax(1, currentWidth/2); 366 currentHeight = SkTMax(1, currentHeight/2); 367 } 368 369 if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) { 370 tex->texturePriv().markMipMapsDirty(); 371 } 372 373 return true; 374} 375 376GrStencilAttachment* GrMtlGpu::createStencilAttachmentForRenderTarget( 377 const GrRenderTarget* rt, int width, int height, int numStencilSamples) { 378 SkASSERT(numStencilSamples == rt->numSamples()); 379 SkASSERT(width >= rt->width()); 380 SkASSERT(height >= rt->height()); 381 382 int samples = rt->numSamples(); 383 384 const GrMtlCaps::StencilFormat& sFmt = this->mtlCaps().preferredStencilFormat(); 385 386 GrMtlStencilAttachment* stencil(GrMtlStencilAttachment::Create(this, 387 width, 388 height, 389 samples, 390 sFmt)); 391 fStats.incStencilAttachmentCreates(); 392 return stencil; 393} 394 395sk_sp<GrTexture> GrMtlGpu::onCreateTexture(const GrSurfaceDesc& desc, 396 const GrBackendFormat& format, 397 GrRenderable renderable, 398 int renderTargetSampleCnt, 399 SkBudgeted budgeted, 400 GrProtected isProtected, 401 const GrMipLevel texels[], 402 int mipLevelCount) { 403 // We don't support protected textures in Metal. 404 if (isProtected == GrProtected::kYes) { 405 return nullptr; 406 } 407 int mipLevels = !mipLevelCount ? 1 : mipLevelCount; 408 409 MTLPixelFormat mtlPixelFormat = GrBackendFormatAsMTLPixelFormat(format); 410 SkASSERT(mtlPixelFormat != MTLPixelFormatInvalid); 411 SkASSERT(!this->caps()->isFormatCompressed(format)); 412 413 sk_sp<GrMtlTexture> tex; 414 // This TexDesc refers to the texture that will be read by the client. Thus even if msaa is 415 // requested, this TexDesc describes the resolved texture. Therefore we always have samples 416 // set to 1. 417 MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init]; 418 texDesc.textureType = MTLTextureType2D; 419 texDesc.pixelFormat = mtlPixelFormat; 420 texDesc.width = desc.fWidth; 421 texDesc.height = desc.fHeight; 422 texDesc.depth = 1; 423 texDesc.mipmapLevelCount = mipLevels; 424 texDesc.sampleCount = 1; 425 texDesc.arrayLength = 1; 426 // Make all textures have private gpu only access. We can use transfer buffers or textures 427 // to copy to them. 428 texDesc.storageMode = MTLStorageModePrivate; 429 texDesc.usage = MTLTextureUsageShaderRead; 430 texDesc.usage |= (renderable == GrRenderable::kYes) ? MTLTextureUsageRenderTarget : 0; 431 432 GrMipMapsStatus mipMapsStatus = GrMipMapsStatus::kNotAllocated; 433 if (mipLevels > 1) { 434 mipMapsStatus = GrMipMapsStatus::kValid; 435 for (int i = 0; i < mipLevels; ++i) { 436 if (!texels[i].fPixels) { 437 mipMapsStatus = GrMipMapsStatus::kDirty; 438 break; 439 } 440 } 441 } 442 443 if (renderable == GrRenderable::kYes) { 444 tex = GrMtlTextureRenderTarget::MakeNewTextureRenderTarget(this, budgeted, 445 desc, renderTargetSampleCnt, 446 texDesc, mipMapsStatus); 447 } else { 448 tex = GrMtlTexture::MakeNewTexture(this, budgeted, desc, texDesc, mipMapsStatus); 449 } 450 451 if (!tex) { 452 return nullptr; 453 } 454 455 auto colorType = GrPixelConfigToColorType(desc.fConfig); 456 if (mipLevelCount && texels[0].fPixels) { 457 if (!this->uploadToTexture(tex.get(), 0, 0, desc.fWidth, desc.fHeight, colorType, texels, 458 mipLevelCount)) { 459 tex->unref(); 460 return nullptr; 461 } 462 } 463 464 if (this->caps()->shouldInitializeTextures()) { 465 uint32_t levelMask = ~0; 466 SkASSERT(mipLevelCount < 32); 467 for (int i = 0; i < mipLevelCount; ++i) { 468 if (!texels[i].fPixels) { 469 levelMask &= ~(1 << i); 470 } 471 } 472 this->clearTexture(tex.get(), colorType, levelMask); 473 } 474 475 return tex; 476} 477 478static id<MTLTexture> get_texture_from_backend(const GrBackendTexture& backendTex) { 479 GrMtlTextureInfo textureInfo; 480 if (!backendTex.getMtlTextureInfo(&textureInfo)) { 481 return nil; 482 } 483 return GrGetMTLTexture(textureInfo.fTexture.get()); 484} 485 486static id<MTLTexture> get_texture_from_backend(const GrBackendRenderTarget& backendRT) { 487 GrMtlTextureInfo textureInfo; 488 if (!backendRT.getMtlTextureInfo(&textureInfo)) { 489 return nil; 490 } 491 return GrGetMTLTexture(textureInfo.fTexture.get()); 492} 493 494static inline void init_surface_desc(GrSurfaceDesc* surfaceDesc, id<MTLTexture> mtlTexture, 495 GrRenderable renderable, GrPixelConfig config) { 496 if (renderable == GrRenderable::kYes) { 497 SkASSERT(MTLTextureUsageRenderTarget & mtlTexture.usage); 498 } 499 surfaceDesc->fWidth = mtlTexture.width; 500 surfaceDesc->fHeight = mtlTexture.height; 501 surfaceDesc->fConfig = config; 502} 503 504sk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendTex, 505 GrColorType grColorType, 506 GrWrapOwnership, 507 GrWrapCacheable cacheable, GrIOType ioType) { 508 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex); 509 if (!mtlTexture) { 510 return nullptr; 511 } 512 513 GrPixelConfig config = this->caps()->getConfigFromBackendFormat(backendTex.getBackendFormat(), 514 grColorType); 515 SkASSERT(kUnknown_GrPixelConfig != config); 516 517 GrSurfaceDesc surfDesc; 518 init_surface_desc(&surfDesc, mtlTexture, GrRenderable::kNo, config); 519 520 return GrMtlTexture::MakeWrappedTexture(this, surfDesc, mtlTexture, cacheable, ioType); 521} 522 523sk_sp<GrTexture> GrMtlGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex, 524 int sampleCnt, 525 GrColorType colorType, 526 GrWrapOwnership, 527 GrWrapCacheable cacheable) { 528 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex); 529 if (!mtlTexture) { 530 return nullptr; 531 } 532 533 const GrMtlCaps& caps = this->mtlCaps(); 534 535 MTLPixelFormat format = mtlTexture.pixelFormat; 536 if (!caps.isFormatRenderable(format, sampleCnt)) { 537 return nullptr; 538 } 539 540 GrPixelConfig config = caps.getConfigFromBackendFormat(backendTex.getBackendFormat(), 541 colorType); 542 SkASSERT(kUnknown_GrPixelConfig != config); 543 544 GrSurfaceDesc surfDesc; 545 init_surface_desc(&surfDesc, mtlTexture, GrRenderable::kYes, config); 546 547 sampleCnt = caps.getRenderTargetSampleCount(sampleCnt, format); 548 SkASSERT(sampleCnt); 549 550 return GrMtlTextureRenderTarget::MakeWrappedTextureRenderTarget(this, surfDesc, sampleCnt, 551 mtlTexture, cacheable); 552} 553 554sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& backendRT, 555 GrColorType grColorType) { 556 // TODO: Revisit this when the Metal backend is completed. It may support MSAA render targets. 557 if (backendRT.sampleCnt() > 1) { 558 return nullptr; 559 } 560 id<MTLTexture> mtlTexture = get_texture_from_backend(backendRT); 561 if (!mtlTexture) { 562 return nullptr; 563 } 564 565 GrPixelConfig config = this->caps()->getConfigFromBackendFormat(backendRT.getBackendFormat(), 566 grColorType); 567 SkASSERT(kUnknown_GrPixelConfig != config); 568 569 GrSurfaceDesc surfDesc; 570 init_surface_desc(&surfDesc, mtlTexture, GrRenderable::kYes, config); 571 572 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, backendRT.sampleCnt(), 573 mtlTexture); 574} 575 576sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendTextureAsRenderTarget( 577 const GrBackendTexture& backendTex, int sampleCnt, GrColorType grColorType) { 578 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex); 579 if (!mtlTexture) { 580 return nullptr; 581 } 582 583 MTLPixelFormat format = mtlTexture.pixelFormat; 584 if (!this->mtlCaps().isFormatRenderable(format, sampleCnt)) { 585 return nullptr; 586 } 587 588 GrPixelConfig config = this->caps()->getConfigFromBackendFormat(backendTex.getBackendFormat(), 589 grColorType); 590 SkASSERT(kUnknown_GrPixelConfig != config); 591 592 GrSurfaceDesc surfDesc; 593 init_surface_desc(&surfDesc, mtlTexture, GrRenderable::kYes, config); 594 sampleCnt = this->mtlCaps().getRenderTargetSampleCount(sampleCnt, format); 595 if (!sampleCnt) { 596 return nullptr; 597 } 598 599 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, sampleCnt, mtlTexture); 600} 601 602bool GrMtlGpu::onRegenerateMipMapLevels(GrTexture* texture) { 603 GrMtlTexture* grMtlTexture = static_cast<GrMtlTexture*>(texture); 604 id<MTLTexture> mtlTexture = grMtlTexture->mtlTexture(); 605 606 // Automatic mipmap generation is only supported by color-renderable formats 607 if (!fMtlCaps->isFormatRenderable(mtlTexture.pixelFormat, 1) && 608 // We have pixel configs marked as textureable-only that use RGBA8 as the internal format 609 MTLPixelFormatRGBA8Unorm != mtlTexture.pixelFormat) { 610 return false; 611 } 612 613 id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder(); 614 [blitCmdEncoder generateMipmapsForTexture: mtlTexture]; 615 616 return true; 617} 618 619static GrPixelConfig mtl_format_to_pixelconfig(MTLPixelFormat format) { 620 switch(format) { 621 case MTLPixelFormatA8Unorm: return kAlpha_8_GrPixelConfig; 622 case MTLPixelFormatR8Unorm: return kAlpha_8_GrPixelConfig; 623 624#ifdef SK_BUILD_FOR_IOS 625 case MTLPixelFormatB5G6R5Unorm: return kRGB_565_GrPixelConfig; 626 case MTLPixelFormatABGR4Unorm: return kRGBA_4444_GrPixelConfig; 627#endif 628 case MTLPixelFormatRGBA8Unorm: return kRGBA_8888_GrPixelConfig; 629 case MTLPixelFormatRGBA8Unorm_sRGB: return kSRGBA_8888_GrPixelConfig; 630 631#ifdef SK_BUILD_FOR_IOS 632 case MTLPixelFormatETC2_RGB8: return kRGB_ETC1_GrPixelConfig; 633#endif 634 case MTLPixelFormatRG8Unorm: return kRG_88_GrPixelConfig; 635 case MTLPixelFormatBGRA8Unorm: return kBGRA_8888_GrPixelConfig; 636 case MTLPixelFormatRGB10A2Unorm: return kRGBA_1010102_GrPixelConfig; 637 case MTLPixelFormatR16Float: return kAlpha_half_GrPixelConfig; 638 case MTLPixelFormatRGBA16Float: return kRGBA_half_GrPixelConfig; 639 case MTLPixelFormatRGBA32Float: return kRGBA_float_GrPixelConfig; 640 case MTLPixelFormatR16Unorm: return kR_16_GrPixelConfig; 641 case MTLPixelFormatRG16Unorm: return kRG_1616_GrPixelConfig; 642 case MTLPixelFormatRGBA16Unorm: return kRGBA_16161616_GrPixelConfig; 643 case MTLPixelFormatRG16Float: return kRG_half_GrPixelConfig; 644 default: return kUnknown_GrPixelConfig; 645 } 646 647 SkUNREACHABLE; 648} 649 650bool GrMtlGpu::createTestingOnlyMtlTextureInfo(MTLPixelFormat format, 651 int w, int h, bool texturable, 652 bool renderable, GrMipMapped mipMapped, 653 const void* srcData, size_t srcRowBytes, 654 const SkColor4f* color, GrMtlTextureInfo* info) { 655 SkASSERT(texturable || renderable); 656 if (!texturable) { 657 SkASSERT(GrMipMapped::kNo == mipMapped); 658 SkASSERT(!srcData); 659 } 660 661 if (texturable && !fMtlCaps->isFormatTexturable(format)) { 662 return false; 663 } 664 if (renderable && !fMtlCaps->isFormatRenderable(format, 1)) { 665 return false; 666 } 667 // Currently we don't support uploading pixel data when mipped. 668 if (srcData && GrMipMapped::kYes == mipMapped) { 669 return false; 670 } 671 if(!check_max_blit_width(w)) { 672 return false; 673 } 674 675 // TODO: allow uninitialized textures to be truly uninitialized 676 if (!color) { 677 color = &SkColors::kTransparent; 678 } 679 680 int mipLevelCount = 1; 681 if (GrMipMapped::kYes == mipMapped) { 682 mipLevelCount = SkMipMap::ComputeLevelCount(w, h) + 1; 683 } 684 685 bool mipmapped = mipMapped == GrMipMapped::kYes ? true : false; 686 MTLTextureDescriptor* desc = 687 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format 688 width: w 689 height: h 690 mipmapped: mipmapped]; 691 desc.cpuCacheMode = MTLCPUCacheModeWriteCombined; 692 desc.storageMode = MTLStorageModePrivate; 693 desc.usage = texturable ? MTLTextureUsageShaderRead : 0; 694 desc.usage |= renderable ? MTLTextureUsageRenderTarget : 0; 695 id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor: desc]; 696 697 size_t bytesPerPixel = GrMtlBytesPerFormat(format); 698 699 if (!srcRowBytes) { 700 srcRowBytes = w * bytesPerPixel; 701#ifdef SK_BUILD_FOR_MAC 702 if (!srcData) { 703 // On MacOS, the fillBuffer command needs a range with a multiple of 4 bytes 704 srcRowBytes = ((srcRowBytes + 3) & (~3)); 705 } 706#endif 707 } 708 709 size_t combinedBufferSize = 0; 710 SkTArray<size_t> individualMipOffsets(mipLevelCount); 711 if (srcData) { 712 SkASSERT(1 == mipLevelCount); 713 individualMipOffsets.push_back(0); 714 715 combinedBufferSize = srcRowBytes * h; 716 } else if (color) { 717 combinedBufferSize = GrComputeTightCombinedBufferSize(bytesPerPixel, w, h, 718 &individualMipOffsets, 719 mipLevelCount); 720 } 721 722 NSUInteger options = 0; // TODO: consider other options here 723#ifdef SK_BUILD_FOR_MAC 724 options |= MTLResourceStorageModeManaged; 725#else 726 options |= MTLResourceStorageModeShared; 727#endif 728 729 GrPixelConfig config = mtl_format_to_pixelconfig(format); 730 SkASSERT(kUnknown_GrPixelConfig != config); 731 732 id<MTLBuffer> transferBuffer = [fDevice newBufferWithLength: combinedBufferSize 733 options: options]; 734 if (nil == transferBuffer) { 735 return false; 736 } 737 738 char* buffer = (char*) transferBuffer.contents; 739 740 if (srcData) { 741 const size_t trimRowBytes = w * bytesPerPixel; 742 743 SkASSERT(1 == mipLevelCount); 744 if (!srcRowBytes) { 745 srcRowBytes = trimRowBytes; 746 } 747 748 // copy data into the buffer, skipping the trailing bytes 749 const char* src = (const char*) srcData; 750 SkRectMemcpy(buffer, trimRowBytes, src, srcRowBytes, trimRowBytes, h); 751 } else if (color) { 752 GrFillInData(config, w, h, individualMipOffsets, buffer, *color); 753 } 754 755 int currentWidth = w; 756 int currentHeight = h; 757 MTLOrigin origin = MTLOriginMake(0, 0, 0); 758 759 id<MTLCommandBuffer> cmdBuffer = [fQueue commandBuffer]; 760 id<MTLBlitCommandEncoder> blitCmdEncoder = [cmdBuffer blitCommandEncoder]; 761 762 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { 763 const size_t trimRowBytes = currentWidth * bytesPerPixel; 764 765 // TODO: can this all be done in one go? 766 [blitCmdEncoder copyFromBuffer: transferBuffer 767 sourceOffset: individualMipOffsets[currentMipLevel] 768 sourceBytesPerRow: trimRowBytes 769 sourceBytesPerImage: trimRowBytes*currentHeight 770 sourceSize: MTLSizeMake(currentWidth, currentHeight, 1) 771 toTexture: testTexture 772 destinationSlice: 0 773 destinationLevel: currentMipLevel 774 destinationOrigin: origin]; 775 776 currentWidth = SkTMax(1, currentWidth/2); 777 currentHeight = SkTMax(1, currentHeight/2); 778 } 779#ifdef SK_BUILD_FOR_MAC 780 [transferBuffer didModifyRange: NSMakeRange(0, combinedBufferSize)]; 781#endif 782 783 [blitCmdEncoder endEncoding]; 784 [cmdBuffer commit]; 785 [cmdBuffer waitUntilCompleted]; 786 transferBuffer = nil; 787 788 info->fTexture.reset(GrRetainPtrFromId(testTexture)); 789 790 return true; 791} 792 793GrBackendTexture GrMtlGpu::createBackendTexture(int w, int h, 794 const GrBackendFormat& format, 795 GrMipMapped mipMapped, 796 GrRenderable renderable, 797 const void* pixels, size_t rowBytes, 798 const SkColor4f* color, GrProtected isProtected) { 799 if (w > this->caps()->maxTextureSize() || h > this->caps()->maxTextureSize()) { 800 return GrBackendTexture(); 801 } 802 803 const MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format); 804 GrMtlTextureInfo info; 805 if (!this->createTestingOnlyMtlTextureInfo(mtlFormat, 806 w, h, true, 807 GrRenderable::kYes == renderable, mipMapped, 808 pixels, rowBytes, color, &info)) { 809 return {}; 810 } 811 812 GrBackendTexture backendTex(w, h, mipMapped, info); 813 return backendTex; 814} 815 816void GrMtlGpu::deleteBackendTexture(const GrBackendTexture& tex) { 817 SkASSERT(GrBackendApi::kMetal == tex.backend()); 818 // Nothing to do here, will get cleaned up when the GrBackendTexture object goes away 819} 820 821#if GR_TEST_UTILS 822bool GrMtlGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const { 823 SkASSERT(GrBackendApi::kMetal == tex.backend()); 824 825 GrMtlTextureInfo info; 826 if (!tex.getMtlTextureInfo(&info)) { 827 return false; 828 } 829 id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture.get()); 830 if (!mtlTexture) { 831 return false; 832 } 833 return mtlTexture.usage & MTLTextureUsageShaderRead; 834} 835 836GrBackendRenderTarget GrMtlGpu::createTestingOnlyBackendRenderTarget(int w, int h, GrColorType ct) { 837 if (w > this->caps()->maxRenderTargetSize() || h > this->caps()->maxRenderTargetSize()) { 838 return GrBackendRenderTarget(); 839 } 840 841 GrPixelConfig config = GrColorTypeToPixelConfig(ct); 842 843 MTLPixelFormat format; 844 if (!GrPixelConfigToMTLFormat(config, &format)) { 845 return GrBackendRenderTarget(); 846 } 847 848 GrMtlTextureInfo info; 849 if (!this->createTestingOnlyMtlTextureInfo(format, w, h, false, true, 850 GrMipMapped::kNo, nullptr, 0, nullptr, &info)) { 851 return {}; 852 } 853 854 GrBackendRenderTarget backendRT(w, h, 1, info); 855 return backendRT; 856} 857 858void GrMtlGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget& rt) { 859 SkASSERT(GrBackendApi::kMetal == rt.backend()); 860 861 GrMtlTextureInfo info; 862 if (rt.getMtlTextureInfo(&info)) { 863 this->testingOnly_flushGpuAndSync(); 864 // Nothing else to do here, will get cleaned up when the GrBackendRenderTarget 865 // is deleted. 866 } 867} 868 869void GrMtlGpu::testingOnly_flushGpuAndSync() { 870 this->submitCommandBuffer(kForce_SyncQueue); 871} 872#endif // GR_TEST_UTILS 873 874static int get_surface_sample_cnt(GrSurface* surf) { 875 if (const GrRenderTarget* rt = surf->asRenderTarget()) { 876 return rt->numSamples(); 877 } 878 return 0; 879} 880 881void GrMtlGpu::copySurfaceAsResolve(GrSurface* dst, GrSurface* src) { 882 // TODO: Add support for subrectangles 883 GrMtlRenderTarget* srcRT = static_cast<GrMtlRenderTarget*>(src->asRenderTarget()); 884 GrRenderTarget* dstRT = dst->asRenderTarget(); 885 id<MTLTexture> dstTexture; 886 if (dstRT) { 887 GrMtlRenderTarget* mtlRT = static_cast<GrMtlRenderTarget*>(dstRT); 888 dstTexture = mtlRT->mtlColorTexture(); 889 } else { 890 SkASSERT(dst->asTexture()); 891 dstTexture = static_cast<GrMtlTexture*>(dst->asTexture())->mtlTexture(); 892 } 893 894 this->resolveTexture(dstTexture, srcRT->mtlColorTexture()); 895} 896 897void GrMtlGpu::copySurfaceAsBlit(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, 898 const SkIPoint& dstPoint) { 899 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst); 900 id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src); 901 902#ifdef SK_DEBUG 903 int dstSampleCnt = get_surface_sample_cnt(dst); 904 int srcSampleCnt = get_surface_sample_cnt(src); 905 SkASSERT(this->mtlCaps().canCopyAsBlit(dstTex.pixelFormat, dstSampleCnt, srcTex.pixelFormat, 906 srcSampleCnt, srcRect, dstPoint, dst == src)); 907#endif 908 id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder(); 909 [blitCmdEncoder copyFromTexture: srcTex 910 sourceSlice: 0 911 sourceLevel: 0 912 sourceOrigin: MTLOriginMake(srcRect.x(), srcRect.y(), 0) 913 sourceSize: MTLSizeMake(srcRect.width(), srcRect.height(), 1) 914 toTexture: dstTex 915 destinationSlice: 0 916 destinationLevel: 0 917 destinationOrigin: MTLOriginMake(dstPoint.fX, dstPoint.fY, 0)]; 918} 919 920bool GrMtlGpu::onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, 921 const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) { 922 SkASSERT(!src->isProtected() && !dst->isProtected()); 923 924 MTLPixelFormat dstFormat = GrBackendFormatAsMTLPixelFormat(dst->backendFormat()); 925 MTLPixelFormat srcFormat = GrBackendFormatAsMTLPixelFormat(src->backendFormat()); 926 927 int dstSampleCnt = get_surface_sample_cnt(dst); 928 int srcSampleCnt = get_surface_sample_cnt(src); 929 930 bool success = false; 931 if (this->mtlCaps().canCopyAsResolve(dst, dstSampleCnt, src, srcSampleCnt, srcRect, dstPoint)) { 932 this->copySurfaceAsResolve(dst, src); 933 success = true; 934 } else if (this->mtlCaps().canCopyAsBlit(dstFormat, dstSampleCnt, srcFormat, srcSampleCnt, 935 srcRect, dstPoint, dst == src)) { 936 this->copySurfaceAsBlit(dst, src, srcRect, dstPoint); 937 success = true; 938 } 939 if (success) { 940 SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.x(), dstPoint.y(), 941 srcRect.width(), srcRect.height()); 942 // The rect is already in device space so we pass in kTopLeft so no flip is done. 943 this->didWriteToSurface(dst, kTopLeft_GrSurfaceOrigin, &dstRect); 944 } 945 return success; 946} 947 948bool GrMtlGpu::onWritePixels(GrSurface* surface, int left, int top, int width, int height, 949 GrColorType surfaceColorType, GrColorType srcColorType, 950 const GrMipLevel texels[], int mipLevelCount) { 951 GrMtlTexture* mtlTexture = static_cast<GrMtlTexture*>(surface->asTexture()); 952 // TODO: In principle we should be able to support pure rendertargets as well, but 953 // until we find a use case we'll only support texture rendertargets. 954 if (!mtlTexture) { 955 return false; 956 } 957 if (!mipLevelCount) { 958 return false; 959 } 960#ifdef SK_DEBUG 961 for (int i = 0; i < mipLevelCount; i++) { 962 SkASSERT(texels[i].fPixels); 963 } 964#endif 965 return this->uploadToTexture(mtlTexture, left, top, width, height, srcColorType, texels, 966 mipLevelCount); 967} 968 969bool GrMtlGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int height, 970 GrColorType surfaceColorType, GrColorType dstColorType, void* buffer, 971 size_t rowBytes) { 972 SkASSERT(surface); 973 if (!check_max_blit_width(width)) { 974 return false; 975 } 976 if (GrPixelConfigToColorType(surface->config()) != dstColorType) { 977 return false; 978 } 979 980 int bpp = GrColorTypeBytesPerPixel(dstColorType); 981 size_t transBufferRowBytes = bpp * width; 982 983 id<MTLTexture> mtlTexture; 984 GrMtlRenderTarget* rt = static_cast<GrMtlRenderTarget*>(surface->asRenderTarget()); 985 if (rt) { 986 // resolve the render target if necessary 987 switch (rt->getResolveType()) { 988 case GrMtlRenderTarget::kCantResolve_ResolveType: 989 return false; 990 case GrMtlRenderTarget::kAutoResolves_ResolveType: 991 mtlTexture = rt->mtlColorTexture(); 992 break; 993 case GrMtlRenderTarget::kCanResolve_ResolveType: 994 this->resolveRenderTargetNoFlush(rt); 995 mtlTexture = rt->mtlResolveTexture(); 996 break; 997 default: 998 SK_ABORT("Unknown resolve type"); 999 } 1000 } else { 1001 GrMtlTexture* texture = static_cast<GrMtlTexture*>(surface->asTexture()); 1002 if (texture) { 1003 mtlTexture = texture->mtlTexture(); 1004 } 1005 } 1006 1007 if (!mtlTexture) { 1008 return false; 1009 } 1010 1011 size_t transBufferImageBytes = transBufferRowBytes * height; 1012 1013 // TODO: implement some way of reusing buffers instead of making a new one every time. 1014 NSUInteger options = 0; 1015#ifdef SK_BUILD_FOR_MAC 1016 options |= MTLResourceStorageModeManaged; 1017#else 1018 options |= MTLResourceStorageModeShared; 1019#endif 1020 id<MTLBuffer> transferBuffer = [fDevice newBufferWithLength: transBufferImageBytes 1021 options: options]; 1022 1023 id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder(); 1024 [blitCmdEncoder copyFromTexture: mtlTexture 1025 sourceSlice: 0 1026 sourceLevel: 0 1027 sourceOrigin: MTLOriginMake(left, top, 0) 1028 sourceSize: MTLSizeMake(width, height, 1) 1029 toBuffer: transferBuffer 1030 destinationOffset: 0 1031 destinationBytesPerRow: transBufferRowBytes 1032 destinationBytesPerImage: transBufferImageBytes]; 1033#ifdef SK_BUILD_FOR_MAC 1034 // Sync GPU data back to the CPU 1035 [blitCmdEncoder synchronizeResource: transferBuffer]; 1036#endif 1037 1038 this->submitCommandBuffer(kForce_SyncQueue); 1039 const void* mappedMemory = transferBuffer.contents; 1040 1041 SkRectMemcpy(buffer, rowBytes, mappedMemory, transBufferRowBytes, transBufferRowBytes, height); 1042 1043 return true; 1044} 1045 1046void GrMtlGpu::internalResolveRenderTarget(GrRenderTarget* target, bool requiresSubmit) { 1047 if (target->needsResolve()) { 1048 this->resolveTexture(static_cast<GrMtlRenderTarget*>(target)->mtlResolveTexture(), 1049 static_cast<GrMtlRenderTarget*>(target)->mtlColorTexture()); 1050 target->flagAsResolved(); 1051 1052 if (requiresSubmit) { 1053 this->submitCommandBuffer(kSkip_SyncQueue); 1054 } 1055 } 1056} 1057 1058void GrMtlGpu::resolveTexture(id<MTLTexture> resolveTexture, id<MTLTexture> colorTexture) { 1059 auto renderPassDesc = [MTLRenderPassDescriptor renderPassDescriptor]; 1060 renderPassDesc.colorAttachments[0].texture = colorTexture; 1061 renderPassDesc.colorAttachments[0].slice = 0; 1062 renderPassDesc.colorAttachments[0].level = 0; 1063 renderPassDesc.colorAttachments[0].resolveTexture = resolveTexture; 1064 renderPassDesc.colorAttachments[0].slice = 0; 1065 renderPassDesc.colorAttachments[0].level = 0; 1066 renderPassDesc.colorAttachments[0].loadAction = MTLLoadActionLoad; 1067 renderPassDesc.colorAttachments[0].storeAction = MTLStoreActionMultisampleResolve; 1068 1069 id<MTLRenderCommandEncoder> cmdEncoder = 1070 this->commandBuffer()->getRenderCommandEncoder(renderPassDesc, nullptr, nullptr); 1071 SkASSERT(nil != cmdEncoder); 1072 cmdEncoder.label = @"resolveTexture"; 1073} 1074