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/ganesh/mtl/GrMtlGpu.h" 9 10#include "include/core/SkColorSpace.h" 11#include "include/core/SkTextureCompressionType.h" 12#include "include/gpu/GpuTypes.h" 13#include "include/gpu/ganesh/mtl/GrMtlBackendSemaphore.h" 14#include "include/gpu/ganesh/mtl/GrMtlBackendSurface.h" 15#include "include/private/gpu/ganesh/GrTypesPriv.h" 16#include "src/base/SkMathPriv.h" 17#include "src/base/SkRectMemcpy.h" 18#include "src/core/SkCompressedDataUtils.h" 19#include "src/core/SkMipmap.h" 20#include "src/gpu/DataUtils.h" 21#include "src/gpu/ganesh/GrBackendUtils.h" 22#include "src/gpu/ganesh/GrDataUtils.h" 23#include "src/gpu/ganesh/GrDirectContextPriv.h" 24#include "src/gpu/ganesh/GrImageInfo.h" 25#include "src/gpu/ganesh/GrPixmap.h" 26#include "src/gpu/ganesh/GrRenderTarget.h" 27#include "src/gpu/ganesh/GrResourceProvider.h" 28#include "src/gpu/ganesh/GrTexture.h" 29#include "src/gpu/ganesh/GrThreadSafePipelineBuilder.h" 30#include "src/gpu/ganesh/mtl/GrMtlBuffer.h" 31#include "src/gpu/ganesh/mtl/GrMtlCommandBuffer.h" 32#include "src/gpu/ganesh/mtl/GrMtlOpsRenderPass.h" 33#include "src/gpu/ganesh/mtl/GrMtlPipelineStateBuilder.h" 34#include "src/gpu/ganesh/mtl/GrMtlRenderCommandEncoder.h" 35#include "src/gpu/ganesh/mtl/GrMtlSemaphore.h" 36#include "src/gpu/ganesh/mtl/GrMtlTexture.h" 37#include "src/gpu/ganesh/mtl/GrMtlTextureRenderTarget.h" 38#include "src/gpu/ganesh/mtl/GrMtlUtil.h" 39#include "src/gpu/mtl/MtlUtilsPriv.h" 40 41#import <simd/simd.h> 42 43using namespace skia_private; 44 45#if !__has_feature(objc_arc) 46#error This file must be compiled with Arc. Use -fobjc-arc flag 47#endif 48 49GR_NORETAIN_BEGIN 50 51#if defined(GR_TEST_UTILS) 52// set to 1 if you want to do GPU capture of each commandBuffer 53#define GR_METAL_CAPTURE_COMMANDBUFFER 0 54#endif 55 56std::unique_ptr<GrGpu> GrMtlGpu::Make(const GrMtlBackendContext& context, 57 const GrContextOptions& options, 58 GrDirectContext* direct) { 59 if (!context.fDevice || !context.fQueue) { 60 return nullptr; 61 } 62 if (@available(macOS 10.14, iOS 10.0, tvOS 10.0, *)) { 63 // no warning needed 64 } else { 65 SkDebugf("*** Error ***: Skia's Metal backend no longer supports this OS version.\n"); 66#ifdef SK_BUILD_FOR_IOS 67 SkDebugf("Minimum supported version is iOS 10.0.\n"); 68#else 69 SkDebugf("Minimum supported version is MacOS 10.14.\n"); 70#endif 71 return nullptr; 72 } 73 74 id<MTLDevice> GR_NORETAIN device = (__bridge id<MTLDevice>)(context.fDevice.get()); 75 id<MTLCommandQueue> GR_NORETAIN queue = (__bridge id<MTLCommandQueue>)(context.fQueue.get()); 76 77 return std::unique_ptr<GrGpu>(new GrMtlGpu(direct, 78 options, 79 device, 80 queue)); 81} 82 83// This constant determines how many OutstandingCommandBuffers are allocated together as a block in 84// the deque. As such it needs to balance allocating too much memory vs. incurring 85// allocation/deallocation thrashing. It should roughly correspond to the max number of outstanding 86// command buffers we expect to see. 87static const int kDefaultOutstandingAllocCnt = 8; 88 89GrMtlGpu::GrMtlGpu(GrDirectContext* direct, const GrContextOptions& options, 90 id<MTLDevice> device, id<MTLCommandQueue> queue) 91 : INHERITED(direct) 92 , fDevice(device) 93 , fQueue(queue) 94 , fOutstandingCommandBuffers(sizeof(OutstandingCommandBuffer), kDefaultOutstandingAllocCnt) 95 , fResourceProvider(this) 96 , fStagingBufferManager(this) 97 , fUniformsRingBuffer(this, 128 * 1024, 256, GrGpuBufferType::kUniform) 98 , fDisconnected(false) { 99 fMtlCaps.reset(new GrMtlCaps(options, fDevice)); 100 this->initCaps(fMtlCaps); 101#if GR_METAL_CAPTURE_COMMANDBUFFER 102 this->testingOnly_startCapture(); 103#endif 104 fCurrentCmdBuffer = GrMtlCommandBuffer::Make(fQueue); 105} 106 107GrMtlGpu::~GrMtlGpu() { 108 if (!fDisconnected) { 109 this->destroyResources(); 110 } 111} 112 113void GrMtlGpu::disconnect(DisconnectType type) { 114 INHERITED::disconnect(type); 115 116 if (!fDisconnected) { 117 this->destroyResources(); 118 fDisconnected = true; 119 } 120} 121 122GrThreadSafePipelineBuilder* GrMtlGpu::pipelineBuilder() { 123 return nullptr; 124} 125 126sk_sp<GrThreadSafePipelineBuilder> GrMtlGpu::refPipelineBuilder() { 127 return nullptr; 128} 129 130void GrMtlGpu::destroyResources() { 131 this->submitCommandBuffer(SyncQueue::kForce_SyncQueue); 132 // if there's no work we won't release the command buffer, so we do it here 133 fCurrentCmdBuffer = nil; 134 135 // We used a placement new for each object in fOutstandingCommandBuffers, so we're responsible 136 // for calling the destructor on each of them as well. 137 while (!fOutstandingCommandBuffers.empty()) { 138 OutstandingCommandBuffer* buffer = 139 (OutstandingCommandBuffer*)fOutstandingCommandBuffers.front(); 140 // make sure we remove before deleting as deletion might try to kick off another submit 141 fOutstandingCommandBuffers.pop_front(); 142 buffer->~OutstandingCommandBuffer(); 143 } 144 145 fStagingBufferManager.reset(); 146 147 fResourceProvider.destroyResources(); 148 149 fQueue = nil; 150 fDevice = nil; 151} 152 153GrOpsRenderPass* GrMtlGpu::onGetOpsRenderPass( 154 GrRenderTarget* renderTarget, bool useMSAASurface, GrAttachment* stencil, 155 GrSurfaceOrigin origin, const SkIRect& bounds, 156 const GrOpsRenderPass::LoadAndStoreInfo& colorInfo, 157 const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilInfo, 158 const TArray<GrSurfaceProxy*, true>& sampledProxies, 159 GrXferBarrierFlags renderPassXferBarriers) { 160 // For the given render target and requested render pass features we need to find a compatible 161 // framebuffer to use. 162 GrMtlRenderTarget* mtlRT = static_cast<GrMtlRenderTarget*>(renderTarget); 163 164 // TODO: support DMSAA 165 SkASSERT(!useMSAASurface || 166 (renderTarget->numSamples() > 1)); 167 168 bool withResolve = false; 169 170 // Figure out if we can use a Resolve store action for this render pass. When we set up 171 // the render pass we'll update the color load/store ops since we don't want to ever load 172 // or store the msaa color attachment, but may need to for the resolve attachment. 173 if (useMSAASurface && this->mtlCaps().renderTargetSupportsDiscardableMSAA(mtlRT)) { 174 withResolve = true; 175 } 176 177 sk_sp<GrMtlFramebuffer> framebuffer = 178 sk_ref_sp(mtlRT->getFramebuffer(withResolve, SkToBool(stencil))); 179 if (!framebuffer) { 180 return nullptr; 181 } 182 183 return new GrMtlOpsRenderPass(this, renderTarget, std::move(framebuffer), origin, colorInfo, 184 stencilInfo); 185} 186 187GrMtlCommandBuffer* GrMtlGpu::commandBuffer() { 188 if (!fCurrentCmdBuffer) { 189#if GR_METAL_CAPTURE_COMMANDBUFFER 190 this->testingOnly_startCapture(); 191#endif 192 // Create a new command buffer for the next submit 193 fCurrentCmdBuffer = GrMtlCommandBuffer::Make(fQueue); 194 } 195 196 SkASSERT(fCurrentCmdBuffer); 197 return fCurrentCmdBuffer.get(); 198} 199 200void GrMtlGpu::takeOwnershipOfBuffer(sk_sp<GrGpuBuffer> buffer) { 201 SkASSERT(buffer); 202 this->commandBuffer()->addGrBuffer(std::move(buffer)); 203} 204 205void GrMtlGpu::submit(GrOpsRenderPass* renderPass) { 206 GrMtlOpsRenderPass* mtlRenderPass = reinterpret_cast<GrMtlOpsRenderPass*>(renderPass); 207 mtlRenderPass->submit(); 208 delete renderPass; 209} 210 211bool GrMtlGpu::submitCommandBuffer(SyncQueue sync) { 212 if (!fCurrentCmdBuffer || !fCurrentCmdBuffer->hasWork()) { 213 if (sync == SyncQueue::kForce_SyncQueue) { 214 this->finishOutstandingGpuWork(); 215 this->checkForFinishedCommandBuffers(); 216 } 217 // We need to manually call the finishedCallbacks since we don't add this 218 // to the OutstandingCommandBuffer list 219 if (fCurrentCmdBuffer) { 220 fCurrentCmdBuffer->callFinishedCallbacks(); 221 } 222 return true; 223 } 224 225 SkASSERT(fCurrentCmdBuffer); 226 bool didCommit = fCurrentCmdBuffer->commit(sync == SyncQueue::kForce_SyncQueue); 227 if (didCommit) { 228 new (fOutstandingCommandBuffers.push_back()) OutstandingCommandBuffer(fCurrentCmdBuffer); 229 } 230 231 // We don't create a new command buffer here because we may end up using it 232 // in the next frame, and that confuses the GPU debugger. Instead we 233 // create when we next need one. 234 fCurrentCmdBuffer.reset(); 235 236 // If the freeing of any resources held by a finished command buffer causes us to send 237 // a new command to the gpu we'll create the new command buffer in commandBuffer(), above. 238 this->checkForFinishedCommandBuffers(); 239 240#if GR_METAL_CAPTURE_COMMANDBUFFER 241 this->testingOnly_stopCapture(); 242#endif 243 return didCommit; 244} 245 246void GrMtlGpu::checkForFinishedCommandBuffers() { 247 // Iterate over all the outstanding command buffers to see if any have finished. The command 248 // buffers are in order from oldest to newest, so we start at the front to check if their fence 249 // has signaled. If so we pop it off and move onto the next. 250 // Repeat till we find a command list that has not finished yet (and all others afterwards are 251 // also guaranteed to not have finished). 252 OutstandingCommandBuffer* front = (OutstandingCommandBuffer*)fOutstandingCommandBuffers.front(); 253 while (front && (*front)->isCompleted()) { 254 // Make sure we remove before deleting as deletion might try to kick off another submit 255 fOutstandingCommandBuffers.pop_front(); 256 // Since we used placement new we are responsible for calling the destructor manually. 257 front->~OutstandingCommandBuffer(); 258 front = (OutstandingCommandBuffer*)fOutstandingCommandBuffers.front(); 259 } 260} 261 262void GrMtlGpu::finishOutstandingGpuWork() { 263 // wait for the last command buffer we've submitted to finish 264 OutstandingCommandBuffer* back = 265 (OutstandingCommandBuffer*)fOutstandingCommandBuffers.back(); 266 if (back) { 267 (*back)->waitUntilCompleted(); 268 } 269} 270 271void GrMtlGpu::addFinishedProc(GrGpuFinishedProc finishedProc, 272 GrGpuFinishedContext finishedContext) { 273 SkASSERT(finishedProc); 274 this->addFinishedCallback(skgpu::RefCntedCallback::Make(finishedProc, finishedContext)); 275} 276 277void GrMtlGpu::addFinishedCallback(sk_sp<skgpu::RefCntedCallback> finishedCallback) { 278 SkASSERT(finishedCallback); 279 // Besides the current commandbuffer, we also add the finishedCallback to the newest outstanding 280 // commandbuffer. Our contract for calling the proc is that all previous submitted cmdbuffers 281 // have finished when we call it. However, if our current command buffer has no work when it is 282 // flushed it will drop its ref to the callback immediately. But the previous work may not have 283 // finished. It is safe to only add the proc to the newest outstanding commandbuffer cause that 284 // must finish after all previously submitted command buffers. 285 OutstandingCommandBuffer* back = (OutstandingCommandBuffer*)fOutstandingCommandBuffers.back(); 286 if (back) { 287 (*back)->addFinishedCallback(finishedCallback); 288 } 289 commandBuffer()->addFinishedCallback(std::move(finishedCallback)); 290} 291 292bool GrMtlGpu::onSubmitToGpu(GrSyncCpu sync) { 293 if (sync == GrSyncCpu::kYes) { 294 return this->submitCommandBuffer(kForce_SyncQueue); 295 } else { 296 return this->submitCommandBuffer(kSkip_SyncQueue); 297 } 298} 299 300std::unique_ptr<GrSemaphore> GrMtlGpu::prepareTextureForCrossContextUsage(GrTexture*) { 301 this->submitToGpu(GrSyncCpu::kNo); 302 return nullptr; 303} 304 305sk_sp<GrGpuBuffer> GrMtlGpu::onCreateBuffer(size_t size, 306 GrGpuBufferType type, 307 GrAccessPattern accessPattern) { 308 return GrMtlBuffer::Make(this, size, type, accessPattern); 309} 310 311static bool check_max_blit_width(int widthInPixels) { 312 if (widthInPixels > 32767) { 313 SkASSERT(false); // surfaces should not be this wide anyway 314 return false; 315 } 316 return true; 317} 318 319bool GrMtlGpu::uploadToTexture(GrMtlTexture* tex, 320 SkIRect rect, 321 GrColorType dataColorType, 322 const GrMipLevel texels[], 323 int mipLevelCount) { 324 SkASSERT(this->mtlCaps().isFormatTexturable(tex->mtlTexture().pixelFormat)); 325 // The assumption is either that we have no mipmaps, or that our rect is the entire texture 326 SkASSERT(mipLevelCount == 1 || rect == SkIRect::MakeSize(tex->dimensions())); 327 328 // We assume that if the texture has mip levels, we either upload to all the levels or just the 329 // first. 330 SkASSERT(mipLevelCount == 1 || mipLevelCount == (tex->maxMipmapLevel() + 1)); 331 332 if (!check_max_blit_width(rect.width())) { 333 return false; 334 } 335 if (rect.isEmpty()) { 336 return false; 337 } 338 339 SkASSERT(this->mtlCaps().surfaceSupportsWritePixels(tex)); 340 SkASSERT(this->mtlCaps().areColorTypeAndFormatCompatible(dataColorType, tex->backendFormat())); 341 342 id<MTLTexture> GR_NORETAIN mtlTexture = tex->mtlTexture(); 343 SkASSERT(mtlTexture); 344 // Either upload only the first miplevel or all miplevels 345 SkASSERT(1 == mipLevelCount || mipLevelCount == (int)mtlTexture.mipmapLevelCount); 346 347 if (mipLevelCount == 1 && !texels[0].fPixels) { 348 return true; // no data to upload 349 } 350 351 for (int i = 0; i < mipLevelCount; ++i) { 352 // We do not allow any gaps in the mip data 353 if (!texels[i].fPixels) { 354 return false; 355 } 356 } 357 358 size_t bpp = GrColorTypeBytesPerPixel(dataColorType); 359 360 TArray<size_t> individualMipOffsets(mipLevelCount); 361 size_t combinedBufferSize = GrComputeTightCombinedBufferSize(bpp, 362 rect.size(), 363 &individualMipOffsets, 364 mipLevelCount); 365 SkASSERT(combinedBufferSize); 366 367 368 // offset value must be a multiple of the destination texture's pixel size in bytes 369 size_t alignment = std::max(bpp, this->mtlCaps().getMinBufferAlignment()); 370 GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice( 371 combinedBufferSize, alignment); 372 if (!slice.fBuffer) { 373 return false; 374 } 375 char* bufferData = (char*)slice.fOffsetMapPtr; 376 GrMtlBuffer* mtlBuffer = static_cast<GrMtlBuffer*>(slice.fBuffer); 377 378 int currentWidth = rect.width(); 379 int currentHeight = rect.height(); 380 SkDEBUGCODE(int layerHeight = tex->height()); 381 MTLOrigin origin = MTLOriginMake(rect.left(), rect.top(), 0); 382 383 auto cmdBuffer = this->commandBuffer(); 384 id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder(); 385 if (!blitCmdEncoder) { 386 return false; 387 } 388#ifdef SK_ENABLE_MTL_DEBUG_INFO 389 [blitCmdEncoder pushDebugGroup:@"uploadToTexture"]; 390#endif 391 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { 392 if (texels[currentMipLevel].fPixels) { 393 SkASSERT(1 == mipLevelCount || currentHeight == layerHeight); 394 const size_t trimRowBytes = currentWidth * bpp; 395 const size_t rowBytes = texels[currentMipLevel].fRowBytes; 396 397 // copy data into the buffer, skipping any trailing bytes 398 char* dst = bufferData + individualMipOffsets[currentMipLevel]; 399 const char* src = (const char*)texels[currentMipLevel].fPixels; 400 SkRectMemcpy(dst, trimRowBytes, src, rowBytes, trimRowBytes, currentHeight); 401 402 [blitCmdEncoder copyFromBuffer: mtlBuffer->mtlBuffer() 403 sourceOffset: slice.fOffset + individualMipOffsets[currentMipLevel] 404 sourceBytesPerRow: trimRowBytes 405 sourceBytesPerImage: trimRowBytes*currentHeight 406 sourceSize: MTLSizeMake(currentWidth, currentHeight, 1) 407 toTexture: mtlTexture 408 destinationSlice: 0 409 destinationLevel: currentMipLevel 410 destinationOrigin: origin]; 411 } 412 currentWidth = std::max(1, currentWidth/2); 413 currentHeight = std::max(1, currentHeight/2); 414 SkDEBUGCODE(layerHeight = currentHeight); 415 } 416#ifdef SK_BUILD_FOR_MAC 417 if (this->mtlCaps().isMac()) { 418 [mtlBuffer->mtlBuffer() didModifyRange: NSMakeRange(slice.fOffset, combinedBufferSize)]; 419 } 420#endif 421#ifdef SK_ENABLE_MTL_DEBUG_INFO 422 [blitCmdEncoder popDebugGroup]; 423#endif 424 425 if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) { 426 tex->markMipmapsDirty(); 427 } 428 429 return true; 430} 431 432bool GrMtlGpu::clearTexture(GrMtlTexture* tex, size_t bpp, uint32_t levelMask) { 433 SkASSERT(this->mtlCaps().isFormatTexturable(tex->mtlTexture().pixelFormat)); 434 435 if (!levelMask) { 436 return true; 437 } 438 439 id<MTLTexture> GR_NORETAIN mtlTexture = tex->mtlTexture(); 440 SkASSERT(mtlTexture); 441 // Either upload only the first miplevel or all miplevels 442 int mipLevelCount = (int)mtlTexture.mipmapLevelCount; 443 444 TArray<size_t> individualMipOffsets(mipLevelCount); 445 size_t combinedBufferSize = 0; 446 int currentWidth = tex->width(); 447 int currentHeight = tex->height(); 448 449 // The alignment must be at least 4 bytes and a multiple of the bytes per pixel of the image 450 // config. This works with the assumption that the bytes in pixel config is always a power of 2. 451 // TODO: can we just copy from a single buffer the size of the largest cleared level w/o a perf 452 // penalty? 453 SkASSERT((bpp & (bpp - 1)) == 0); 454 const size_t alignmentMask = 0x3 | (bpp - 1); 455 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { 456 if (levelMask & (1 << currentMipLevel)) { 457 const size_t trimmedSize = currentWidth * bpp * currentHeight; 458 const size_t alignmentDiff = combinedBufferSize & alignmentMask; 459 if (alignmentDiff != 0) { 460 combinedBufferSize += alignmentMask - alignmentDiff + 1; 461 } 462 individualMipOffsets.push_back(combinedBufferSize); 463 combinedBufferSize += trimmedSize; 464 } 465 currentWidth = std::max(1, currentWidth/2); 466 currentHeight = std::max(1, currentHeight/2); 467 } 468 SkASSERT(combinedBufferSize > 0 && !individualMipOffsets.empty()); 469 470 size_t alignment = std::max(bpp, this->mtlCaps().getMinBufferAlignment()); 471 GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice( 472 combinedBufferSize, alignment); 473 if (!slice.fBuffer) { 474 return false; 475 } 476 GrMtlBuffer* mtlBuffer = static_cast<GrMtlBuffer*>(slice.fBuffer); 477 id<MTLBuffer> transferBuffer = mtlBuffer->mtlBuffer(); 478 479 auto cmdBuffer = this->commandBuffer(); 480 id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder(); 481 if (!blitCmdEncoder) { 482 return false; 483 } 484#ifdef SK_ENABLE_MTL_DEBUG_INFO 485 [blitCmdEncoder pushDebugGroup:@"clearTexture"]; 486#endif 487 // clear the buffer to transparent black 488 NSRange clearRange; 489 clearRange.location = 0; 490 clearRange.length = combinedBufferSize; 491 [blitCmdEncoder fillBuffer: transferBuffer 492 range: clearRange 493 value: 0]; 494 495 // now copy buffer to texture 496 currentWidth = tex->width(); 497 currentHeight = tex->height(); 498 MTLOrigin origin = MTLOriginMake(0, 0, 0); 499 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { 500 if (levelMask & (1 << currentMipLevel)) { 501 const size_t rowBytes = currentWidth * bpp; 502 503 [blitCmdEncoder copyFromBuffer: transferBuffer 504 sourceOffset: individualMipOffsets[currentMipLevel] 505 sourceBytesPerRow: rowBytes 506 sourceBytesPerImage: rowBytes * currentHeight 507 sourceSize: MTLSizeMake(currentWidth, currentHeight, 1) 508 toTexture: mtlTexture 509 destinationSlice: 0 510 destinationLevel: currentMipLevel 511 destinationOrigin: origin]; 512 } 513 currentWidth = std::max(1, currentWidth/2); 514 currentHeight = std::max(1, currentHeight/2); 515 } 516 // Don't need didModifyRange: here because fillBuffer: happens on the GPU 517#ifdef SK_ENABLE_MTL_DEBUG_INFO 518 [blitCmdEncoder popDebugGroup]; 519#endif 520 521 if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) { 522 tex->markMipmapsDirty(); 523 } 524 525 return true; 526} 527 528sk_sp<GrAttachment> GrMtlGpu::makeStencilAttachment(const GrBackendFormat& /*colorFormat*/, 529 SkISize dimensions, int numStencilSamples) { 530 MTLPixelFormat sFmt = this->mtlCaps().preferredStencilFormat(); 531 532 fStats.incStencilAttachmentCreates(); 533 return GrMtlAttachment::GrMtlAttachment::MakeStencil(this, dimensions, numStencilSamples, sFmt); 534} 535 536sk_sp<GrAttachment> GrMtlGpu::makeMSAAAttachment(SkISize dimensions, 537 const GrBackendFormat& format, 538 int numSamples, 539 GrProtected isProtected, 540 GrMemoryless isMemoryless) { 541 // Metal doesn't support protected textures 542 SkASSERT(isProtected == GrProtected::kNo); 543 // TODO: add memoryless support 544 SkASSERT(isMemoryless == GrMemoryless::kNo); 545 546 MTLPixelFormat pixelFormat = (MTLPixelFormat)GrBackendFormats::AsMtlFormat(format); 547 SkASSERT(pixelFormat != MTLPixelFormatInvalid); 548 SkASSERT(!skgpu::MtlFormatIsCompressed(pixelFormat)); 549 SkASSERT(this->mtlCaps().isFormatRenderable(pixelFormat, numSamples)); 550 551 fStats.incMSAAAttachmentCreates(); 552 return GrMtlAttachment::MakeMSAA(this, dimensions, numSamples, pixelFormat); 553} 554 555sk_sp<GrTexture> GrMtlGpu::onCreateTexture(SkISize dimensions, 556 const GrBackendFormat& format, 557 GrRenderable renderable, 558 int renderTargetSampleCnt, 559 skgpu::Budgeted budgeted, 560 GrProtected isProtected, 561 int mipLevelCount, 562 uint32_t levelClearMask, 563 std::string_view label) { 564 // We don't support protected textures in Metal. 565 if (isProtected == GrProtected::kYes) { 566 return nullptr; 567 } 568 SkASSERT(mipLevelCount > 0); 569 570 MTLPixelFormat mtlPixelFormat = GrBackendFormatAsMTLPixelFormat(format); 571 SkASSERT(mtlPixelFormat != MTLPixelFormatInvalid); 572 SkASSERT(!this->caps()->isFormatCompressed(format)); 573 574 sk_sp<GrMtlTexture> tex; 575 GrMipmapStatus mipmapStatus = 576 mipLevelCount > 1 ? GrMipmapStatus::kDirty : GrMipmapStatus::kNotAllocated; 577 if (renderable == GrRenderable::kYes) { 578 tex = GrMtlTextureRenderTarget::MakeNewTextureRenderTarget( 579 this, budgeted, dimensions, renderTargetSampleCnt, mtlPixelFormat, mipLevelCount, 580 mipmapStatus, label); 581 } else { 582 tex = GrMtlTexture::MakeNewTexture(this, budgeted, dimensions, mtlPixelFormat, 583 mipLevelCount, mipmapStatus, label); 584 } 585 586 if (!tex) { 587 return nullptr; 588 } 589 590 if (levelClearMask) { 591 this->clearTexture(tex.get(), 592 skgpu::MtlFormatBytesPerBlock(mtlPixelFormat), 593 levelClearMask); 594 } 595 596 return std::move(tex); 597} 598 599sk_sp<GrTexture> GrMtlGpu::onCreateCompressedTexture(SkISize dimensions, 600 const GrBackendFormat& format, 601 skgpu::Budgeted budgeted, 602 skgpu::Mipmapped mipmapped, 603 GrProtected isProtected, 604 const void* data, 605 size_t dataSize) { 606 // We don't support protected textures in Metal. 607 if (isProtected == GrProtected::kYes) { 608 return nullptr; 609 } 610 611 SkASSERT(this->caps()->isFormatTexturable(format, GrTextureType::k2D)); 612 SkASSERT(data); 613 614 if (!check_max_blit_width(dimensions.width())) { 615 return nullptr; 616 } 617 618 MTLPixelFormat mtlPixelFormat = GrBackendFormatAsMTLPixelFormat(format); 619 SkASSERT(this->caps()->isFormatCompressed(format)); 620 621 int numMipLevels = 1; 622 if (mipmapped == skgpu::Mipmapped::kYes) { 623 numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1; 624 } 625 626 GrMipmapStatus mipmapStatus = (mipmapped == skgpu::Mipmapped::kYes) 627 ? GrMipmapStatus::kValid 628 : GrMipmapStatus::kNotAllocated; 629 630 auto tex = GrMtlTexture::MakeNewTexture(this, budgeted, dimensions, mtlPixelFormat, 631 numMipLevels, mipmapStatus, 632 /*label=*/"MtlGpu_CreateCompressedTexture"); 633 if (!tex) { 634 return nullptr; 635 } 636 637 // Upload to texture 638 id<MTLTexture> GR_NORETAIN mtlTexture = tex->mtlTexture(); 639 SkASSERT(mtlTexture); 640 641 auto compressionType = GrBackendFormatToCompressionType(format); 642 SkASSERT(compressionType != SkTextureCompressionType::kNone); 643 644 TArray<size_t> individualMipOffsets(numMipLevels); 645 SkDEBUGCODE(size_t combinedBufferSize =) 646 SkCompressedDataSize(compressionType, 647 dimensions, 648 &individualMipOffsets, 649 mipmapped == skgpu::Mipmapped::kYes); 650 SkASSERT(individualMipOffsets.size() == numMipLevels); 651 SkASSERT(dataSize == combinedBufferSize); 652 653 // offset value must be a multiple of the destination texture's pixel size in bytes 654 // for compressed textures, this is the block size 655 size_t alignment = SkCompressedBlockSize(compressionType); 656 GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice( 657 dataSize, alignment); 658 if (!slice.fBuffer) { 659 return nullptr; 660 } 661 char* bufferData = (char*)slice.fOffsetMapPtr; 662 GrMtlBuffer* mtlBuffer = static_cast<GrMtlBuffer*>(slice.fBuffer); 663 664 MTLOrigin origin = MTLOriginMake(0, 0, 0); 665 666 auto cmdBuffer = this->commandBuffer(); 667 id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder(); 668 if (!blitCmdEncoder) { 669 return nullptr; 670 } 671#ifdef SK_ENABLE_MTL_DEBUG_INFO 672 [blitCmdEncoder pushDebugGroup:@"onCreateCompressedTexture"]; 673#endif 674 675 // copy data into the buffer, skipping any trailing bytes 676 memcpy(bufferData, data, dataSize); 677 678 SkISize levelDimensions = dimensions; 679 for (int currentMipLevel = 0; currentMipLevel < numMipLevels; currentMipLevel++) { 680 const size_t levelRowBytes = skgpu::CompressedRowBytes(compressionType, 681 levelDimensions.width()); 682 size_t levelSize = SkCompressedDataSize(compressionType, levelDimensions, nullptr, false); 683 684 // TODO: can this all be done in one go? 685 [blitCmdEncoder copyFromBuffer: mtlBuffer->mtlBuffer() 686 sourceOffset: slice.fOffset + individualMipOffsets[currentMipLevel] 687 sourceBytesPerRow: levelRowBytes 688 sourceBytesPerImage: levelSize 689 sourceSize: MTLSizeMake(levelDimensions.width(), 690 levelDimensions.height(), 1) 691 toTexture: mtlTexture 692 destinationSlice: 0 693 destinationLevel: currentMipLevel 694 destinationOrigin: origin]; 695 696 levelDimensions = {std::max(1, levelDimensions.width() /2), 697 std::max(1, levelDimensions.height()/2)}; 698 } 699#ifdef SK_BUILD_FOR_MAC 700 if (this->mtlCaps().isMac()) { 701 [mtlBuffer->mtlBuffer() didModifyRange: NSMakeRange(slice.fOffset, dataSize)]; 702 } 703#endif 704#ifdef SK_ENABLE_MTL_DEBUG_INFO 705 [blitCmdEncoder popDebugGroup]; 706#endif 707 708 return std::move(tex); 709} 710 711// TODO: Extra retain/release can't be avoided here because of GetMtlTextureInfo copying the 712// sk_cfp. It would be useful to have a (possibly-internal-only?) API to get the raw pointer. 713static id<MTLTexture> get_texture_from_backend(const GrBackendTexture& backendTex) { 714 GrMtlTextureInfo textureInfo; 715 if (!GrBackendTextures::GetMtlTextureInfo(backendTex, &textureInfo)) { 716 return nil; 717 } 718 return GrGetMTLTexture(textureInfo.fTexture.get()); 719} 720 721static id<MTLTexture> get_texture_from_backend(const GrBackendRenderTarget& backendRT) { 722 GrMtlTextureInfo textureInfo; 723 if (!GrBackendRenderTargets::GetMtlTextureInfo(backendRT, &textureInfo)) { 724 return nil; 725 } 726 return GrGetMTLTexture(textureInfo.fTexture.get()); 727} 728 729sk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendTex, 730 GrWrapOwnership, 731 GrWrapCacheable cacheable, 732 GrIOType ioType) { 733 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex); 734 if (!mtlTexture) { 735 return nullptr; 736 } 737 // We don't currently support sampling from a MSAA texture in shaders. 738 if (mtlTexture.sampleCount != 1) { 739 return nullptr; 740 } 741 742 return GrMtlTexture::MakeWrappedTexture(this, backendTex.dimensions(), mtlTexture, cacheable, 743 ioType); 744} 745 746sk_sp<GrTexture> GrMtlGpu::onWrapCompressedBackendTexture(const GrBackendTexture& backendTex, 747 GrWrapOwnership, 748 GrWrapCacheable cacheable) { 749 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex); 750 if (!mtlTexture) { 751 return nullptr; 752 } 753 // We don't currently support sampling from a MSAA texture in shaders. 754 if (mtlTexture.sampleCount != 1) { 755 return nullptr; 756 } 757 758 return GrMtlTexture::MakeWrappedTexture(this, backendTex.dimensions(), mtlTexture, cacheable, 759 kRead_GrIOType); 760} 761 762sk_sp<GrTexture> GrMtlGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex, 763 int sampleCnt, 764 GrWrapOwnership, 765 GrWrapCacheable cacheable) { 766 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex); 767 if (!mtlTexture) { 768 return nullptr; 769 } 770 // We don't currently support sampling from a MSAA texture in shaders. 771 if (mtlTexture.sampleCount != 1) { 772 return nullptr; 773 } 774 775 const GrMtlCaps& caps = this->mtlCaps(); 776 777 MTLPixelFormat format = mtlTexture.pixelFormat; 778 if (!caps.isFormatRenderable(format, sampleCnt)) { 779 return nullptr; 780 } 781 782 if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { 783 SkASSERT(MTLTextureUsageRenderTarget & mtlTexture.usage); 784 } 785 786 sampleCnt = caps.getRenderTargetSampleCount(sampleCnt, format); 787 SkASSERT(sampleCnt); 788 789 return GrMtlTextureRenderTarget::MakeWrappedTextureRenderTarget( 790 this, backendTex.dimensions(), sampleCnt, mtlTexture, cacheable); 791} 792 793sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& backendRT) { 794 if (!this->caps()->isFormatRenderable(backendRT.getBackendFormat(), backendRT.sampleCnt())) { 795 return nullptr; 796 } 797 798 id<MTLTexture> mtlTexture = get_texture_from_backend(backendRT); 799 if (!mtlTexture) { 800 return nullptr; 801 } 802 803 if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { 804 SkASSERT(MTLTextureUsageRenderTarget & mtlTexture.usage); 805 } 806 807 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, backendRT.dimensions(), 808 backendRT.sampleCnt(), mtlTexture); 809} 810 811bool GrMtlGpu::onRegenerateMipMapLevels(GrTexture* texture) { 812 GrMtlTexture* grMtlTexture = static_cast<GrMtlTexture*>(texture); 813 id<MTLTexture> GR_NORETAIN mtlTexture = grMtlTexture->mtlTexture(); 814 815 // Automatic mipmap generation is only supported by color-renderable formats 816 if (!fMtlCaps->isFormatRenderable(mtlTexture.pixelFormat, 1) && 817 // We have pixel configs marked as textureable-only that use RGBA8 as the internal format 818 MTLPixelFormatRGBA8Unorm != mtlTexture.pixelFormat) { 819 return false; 820 } 821 822 auto cmdBuffer = this->commandBuffer(); 823 id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder(); 824 if (!blitCmdEncoder) { 825 return false; 826 } 827 [blitCmdEncoder generateMipmapsForTexture: mtlTexture]; 828 this->commandBuffer()->addGrSurface(sk_ref_sp<const GrSurface>(grMtlTexture->attachment())); 829 830 return true; 831} 832 833// Used to "clear" a backend texture to a constant color by transferring. 834static GrColorType mtl_format_to_backend_tex_clear_colortype(MTLPixelFormat format) { 835 switch(format) { 836 case MTLPixelFormatA8Unorm: return GrColorType::kAlpha_8; 837 case MTLPixelFormatR8Unorm: return GrColorType::kR_8; 838 case MTLPixelFormatB5G6R5Unorm: return GrColorType::kBGR_565; 839 case MTLPixelFormatABGR4Unorm: return GrColorType::kABGR_4444; 840 case MTLPixelFormatRGBA8Unorm: return GrColorType::kRGBA_8888; 841 case MTLPixelFormatRGBA8Unorm_sRGB: return GrColorType::kRGBA_8888_SRGB; 842 843 case MTLPixelFormatRG8Unorm: return GrColorType::kRG_88; 844 case MTLPixelFormatBGRA8Unorm: return GrColorType::kBGRA_8888; 845 case MTLPixelFormatRGB10A2Unorm: return GrColorType::kRGBA_1010102; 846 case MTLPixelFormatBGR10A2Unorm: return GrColorType::kBGRA_1010102; 847 case MTLPixelFormatR16Float: return GrColorType::kR_F16; 848 case MTLPixelFormatRGBA16Float: return GrColorType::kRGBA_F16; 849 case MTLPixelFormatR16Unorm: return GrColorType::kR_16; 850 case MTLPixelFormatRG16Unorm: return GrColorType::kRG_1616; 851 case MTLPixelFormatRGBA16Unorm: return GrColorType::kRGBA_16161616; 852 case MTLPixelFormatRG16Float: return GrColorType::kRG_F16; 853 default: return GrColorType::kUnknown; 854 } 855 856 SkUNREACHABLE; 857} 858 859void copy_src_data(char* dst, 860 size_t bytesPerPixel, 861 const TArray<size_t>& individualMipOffsets, 862 const GrPixmap srcData[], 863 int numMipLevels, 864 size_t bufferSize) { 865 SkASSERT(srcData && numMipLevels); 866 SkASSERT(individualMipOffsets.size() == numMipLevels); 867 868 for (int level = 0; level < numMipLevels; ++level) { 869 const size_t trimRB = srcData[level].width() * bytesPerPixel; 870 SkASSERT(individualMipOffsets[level] + trimRB * srcData[level].height() <= bufferSize); 871 SkRectMemcpy(dst + individualMipOffsets[level], trimRB, 872 srcData[level].addr(), srcData[level].rowBytes(), 873 trimRB, srcData[level].height()); 874 } 875} 876 877bool GrMtlGpu::createMtlTextureForBackendSurface(MTLPixelFormat mtlFormat, 878 SkISize dimensions, 879 int sampleCnt, 880 GrTexturable texturable, 881 GrRenderable renderable, 882 skgpu::Mipmapped mipmapped, 883 GrMtlTextureInfo* info) { 884 SkASSERT(texturable == GrTexturable::kYes || renderable == GrRenderable::kYes); 885 886 if (texturable == GrTexturable::kYes && !fMtlCaps->isFormatTexturable(mtlFormat)) { 887 return false; 888 } 889 if (renderable == GrRenderable::kYes && !fMtlCaps->isFormatRenderable(mtlFormat, 1)) { 890 return false; 891 } 892 893 if (!check_max_blit_width(dimensions.width())) { 894 return false; 895 } 896 897 auto desc = [[MTLTextureDescriptor alloc] init]; 898 desc.pixelFormat = mtlFormat; 899 desc.width = dimensions.width(); 900 desc.height = dimensions.height(); 901 if (mipmapped == skgpu::Mipmapped::kYes) { 902 desc.mipmapLevelCount = 1 + SkPrevLog2(std::max(dimensions.width(), dimensions.height())); 903 } 904 if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { 905 desc.storageMode = MTLStorageModePrivate; 906 MTLTextureUsage usage = texturable == GrTexturable::kYes ? MTLTextureUsageShaderRead : 0; 907 usage |= renderable == GrRenderable::kYes ? MTLTextureUsageRenderTarget : 0; 908 desc.usage = usage; 909 } 910 if (sampleCnt != 1) { 911 desc.sampleCount = sampleCnt; 912 desc.textureType = MTLTextureType2DMultisample; 913 } 914 id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor: desc]; 915#ifdef SK_ENABLE_MTL_DEBUG_INFO 916 testTexture.label = @"testTexture"; 917#endif 918 info->fTexture.reset(GrRetainPtrFromId(testTexture)); 919 return true; 920} 921 922GrBackendTexture GrMtlGpu::onCreateBackendTexture(SkISize dimensions, 923 const GrBackendFormat& format, 924 GrRenderable renderable, 925 skgpu::Mipmapped mipmapped, 926 GrProtected isProtected, 927 std::string_view label) { 928 const MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format); 929 930 GrMtlTextureInfo info; 931 if (!this->createMtlTextureForBackendSurface(mtlFormat, dimensions, 1, GrTexturable::kYes, 932 renderable, mipmapped, &info)) { 933 return {}; 934 } 935 936 return GrBackendTextures::MakeMtl(dimensions.width(), dimensions.height(), mipmapped, info); 937} 938 939bool GrMtlGpu::onClearBackendTexture(const GrBackendTexture& backendTexture, 940 sk_sp<skgpu::RefCntedCallback> finishedCallback, 941 std::array<float, 4> color) { 942 GrMtlTextureInfo info; 943 SkAssertResult(GrBackendTextures::GetMtlTextureInfo(backendTexture, &info)); 944 945 id<MTLTexture> GR_NORETAIN mtlTexture = GrGetMTLTexture(info.fTexture.get()); 946 947 const MTLPixelFormat mtlFormat = mtlTexture.pixelFormat; 948 949 // Create a transfer buffer and fill with data. 950 size_t bytesPerPixel = skgpu::MtlFormatBytesPerBlock(mtlFormat); 951 size_t combinedBufferSize; 952 953 // Reuse the same buffer for all levels. Should be ok since we made the row bytes tight. 954 combinedBufferSize = bytesPerPixel*backendTexture.width()*backendTexture.height(); 955 956 size_t alignment = std::max(bytesPerPixel, this->mtlCaps().getMinBufferAlignment()); 957 GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice( 958 combinedBufferSize, alignment); 959 if (!slice.fBuffer) { 960 return false; 961 } 962 char* buffer = (char*)slice.fOffsetMapPtr; 963 964 auto colorType = mtl_format_to_backend_tex_clear_colortype(mtlFormat); 965 if (colorType == GrColorType::kUnknown) { 966 return false; 967 } 968 GrImageInfo ii(colorType, kUnpremul_SkAlphaType, nullptr, backendTexture.dimensions()); 969 auto rb = ii.minRowBytes(); 970 SkASSERT(rb == bytesPerPixel*backendTexture.width()); 971 if (!GrClearImage(ii, buffer, rb, color)) { 972 return false; 973 } 974 975 // Transfer buffer contents to texture 976 MTLOrigin origin = MTLOriginMake(0, 0, 0); 977 978 GrMtlCommandBuffer* cmdBuffer = this->commandBuffer(); 979 id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder(); 980 if (!blitCmdEncoder) { 981 return false; 982 } 983#ifdef SK_ENABLE_MTL_DEBUG_INFO 984 [blitCmdEncoder pushDebugGroup:@"onClearBackendTexture"]; 985#endif 986 GrMtlBuffer* mtlBuffer = static_cast<GrMtlBuffer*>(slice.fBuffer); 987 988 SkISize levelDimensions(backendTexture.dimensions()); 989 int numMipLevels = mtlTexture.mipmapLevelCount; 990 for (int currentMipLevel = 0; currentMipLevel < numMipLevels; currentMipLevel++) { 991 size_t levelRowBytes; 992 size_t levelSize; 993 994 levelRowBytes = levelDimensions.width() * bytesPerPixel; 995 levelSize = levelRowBytes * levelDimensions.height(); 996 997 // TODO: can this all be done in one go? 998 [blitCmdEncoder copyFromBuffer: mtlBuffer->mtlBuffer() 999 sourceOffset: slice.fOffset 1000 sourceBytesPerRow: levelRowBytes 1001 sourceBytesPerImage: levelSize 1002 sourceSize: MTLSizeMake(levelDimensions.width(), 1003 levelDimensions.height(), 1004 1) 1005 toTexture: mtlTexture 1006 destinationSlice: 0 1007 destinationLevel: currentMipLevel 1008 destinationOrigin: origin]; 1009 1010 levelDimensions = {std::max(1, levelDimensions.width() / 2), 1011 std::max(1, levelDimensions.height() / 2)}; 1012 } 1013#ifdef SK_BUILD_FOR_MAC 1014 if (this->mtlCaps().isMac()) { 1015 [mtlBuffer->mtlBuffer() didModifyRange: NSMakeRange(slice.fOffset, combinedBufferSize)]; 1016 } 1017#endif 1018 [blitCmdEncoder popDebugGroup]; 1019 1020 if (finishedCallback) { 1021 this->addFinishedCallback(std::move(finishedCallback)); 1022 } 1023 1024 return true; 1025} 1026 1027GrBackendTexture GrMtlGpu::onCreateCompressedBackendTexture(SkISize dimensions, 1028 const GrBackendFormat& format, 1029 skgpu::Mipmapped mipmapped, 1030 GrProtected isProtected) { 1031 const MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format); 1032 1033 GrMtlTextureInfo info; 1034 if (!this->createMtlTextureForBackendSurface(mtlFormat, dimensions, 1, GrTexturable::kYes, 1035 GrRenderable::kNo, mipmapped, &info)) { 1036 return {}; 1037 } 1038 1039 return GrBackendTextures::MakeMtl(dimensions.width(), dimensions.height(), mipmapped, info); 1040} 1041 1042bool GrMtlGpu::onUpdateCompressedBackendTexture(const GrBackendTexture& backendTexture, 1043 sk_sp<skgpu::RefCntedCallback> finishedCallback, 1044 const void* data, 1045 size_t size) { 1046 GrMtlTextureInfo info; 1047 SkAssertResult(GrBackendTextures::GetMtlTextureInfo(backendTexture, &info)); 1048 1049 id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture.get()); 1050 1051 int numMipLevels = mtlTexture.mipmapLevelCount; 1052 skgpu::Mipmapped mipmapped = numMipLevels > 1 ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo; 1053 1054 SkTextureCompressionType compression = 1055 GrBackendFormatToCompressionType(backendTexture.getBackendFormat()); 1056 SkASSERT(compression != SkTextureCompressionType::kNone); 1057 1058 // Create a transfer buffer and fill with data. 1059 STArray<16, size_t> individualMipOffsets; 1060 size_t combinedBufferSize; 1061 combinedBufferSize = SkCompressedDataSize(compression, 1062 backendTexture.dimensions(), 1063 &individualMipOffsets, 1064 mipmapped == skgpu::Mipmapped::kYes); 1065 SkASSERT(individualMipOffsets.size() == numMipLevels); 1066 1067 size_t alignment = std::max(SkCompressedBlockSize(compression), 1068 this->mtlCaps().getMinBufferAlignment()); 1069 GrStagingBufferManager::Slice slice = 1070 fStagingBufferManager.allocateStagingBufferSlice(combinedBufferSize, alignment); 1071 if (!slice.fBuffer) { 1072 return false; 1073 } 1074 char* buffer = (char*)slice.fOffsetMapPtr; 1075 1076 memcpy(buffer, data, size); 1077 1078 // Transfer buffer contents to texture 1079 MTLOrigin origin = MTLOriginMake(0, 0, 0); 1080 1081 GrMtlCommandBuffer* cmdBuffer = this->commandBuffer(); 1082 id<MTLBlitCommandEncoder> blitCmdEncoder = cmdBuffer->getBlitCommandEncoder(); 1083 if (!blitCmdEncoder) { 1084 return false; 1085 } 1086#ifdef SK_ENABLE_MTL_DEBUG_INFO 1087 [blitCmdEncoder pushDebugGroup:@"onUpdateCompressedBackendTexture"]; 1088#endif 1089 GrMtlBuffer* mtlBuffer = static_cast<GrMtlBuffer*>(slice.fBuffer); 1090 1091 SkISize levelDimensions(backendTexture.dimensions()); 1092 for (int currentMipLevel = 0; currentMipLevel < numMipLevels; currentMipLevel++) { 1093 size_t levelRowBytes; 1094 size_t levelSize; 1095 1096 levelRowBytes = skgpu::CompressedRowBytes(compression, levelDimensions.width()); 1097 levelSize = SkCompressedDataSize(compression, levelDimensions, nullptr, false); 1098 1099 // TODO: can this all be done in one go? 1100 [blitCmdEncoder copyFromBuffer: mtlBuffer->mtlBuffer() 1101 sourceOffset: slice.fOffset + individualMipOffsets[currentMipLevel] 1102 sourceBytesPerRow: levelRowBytes 1103 sourceBytesPerImage: levelSize 1104 sourceSize: MTLSizeMake(levelDimensions.width(), 1105 levelDimensions.height(), 1106 1) 1107 toTexture: mtlTexture 1108 destinationSlice: 0 1109 destinationLevel: currentMipLevel 1110 destinationOrigin: origin]; 1111 1112 levelDimensions = {std::max(1, levelDimensions.width() / 2), 1113 std::max(1, levelDimensions.height() / 2)}; 1114 } 1115#ifdef SK_BUILD_FOR_MAC 1116 if (this->mtlCaps().isMac()) { 1117 [mtlBuffer->mtlBuffer() didModifyRange:NSMakeRange(slice.fOffset, combinedBufferSize)]; 1118 } 1119#endif 1120 [blitCmdEncoder popDebugGroup]; 1121 1122 if (finishedCallback) { 1123 this->addFinishedCallback(std::move(finishedCallback)); 1124 } 1125 1126 return true; 1127} 1128 1129void GrMtlGpu::deleteBackendTexture(const GrBackendTexture& tex) { 1130 SkASSERT(GrBackendApi::kMetal == tex.backend()); 1131 // Nothing to do here, will get cleaned up when the GrBackendTexture object goes away 1132} 1133 1134bool GrMtlGpu::compile(const GrProgramDesc& desc, const GrProgramInfo& programInfo) { 1135 1136 GrThreadSafePipelineBuilder::Stats::ProgramCacheResult stat; 1137 1138 auto pipelineState = this->resourceProvider().findOrCreateCompatiblePipelineState( 1139 desc, programInfo, &stat); 1140 if (!pipelineState) { 1141 return false; 1142 } 1143 1144 return stat != GrThreadSafePipelineBuilder::Stats::ProgramCacheResult::kHit; 1145} 1146 1147bool GrMtlGpu::precompileShader(const SkData& key, const SkData& data) { 1148 return this->resourceProvider().precompileShader(key, data); 1149} 1150 1151#if defined(GR_TEST_UTILS) 1152bool GrMtlGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const { 1153 SkASSERT(GrBackendApi::kMetal == tex.backend()); 1154 1155 GrMtlTextureInfo info; 1156 if (!GrBackendTextures::GetMtlTextureInfo(tex, &info)) { 1157 return false; 1158 } 1159 id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture.get()); 1160 if (!mtlTexture) { 1161 return false; 1162 } 1163 if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { 1164 return mtlTexture.usage & MTLTextureUsageShaderRead; 1165 } else { 1166 return true; // best we can do 1167 } 1168} 1169 1170GrBackendRenderTarget GrMtlGpu::createTestingOnlyBackendRenderTarget(SkISize dimensions, 1171 GrColorType ct, 1172 int sampleCnt, 1173 GrProtected isProtected) { 1174 if (dimensions.width() > this->caps()->maxRenderTargetSize() || 1175 dimensions.height() > this->caps()->maxRenderTargetSize()) { 1176 return {}; 1177 } 1178 if (isProtected == GrProtected::kYes) { 1179 return {}; 1180 } 1181 1182 MTLPixelFormat format = this->mtlCaps().getFormatFromColorType(ct); 1183 sampleCnt = this->mtlCaps().getRenderTargetSampleCount(sampleCnt, format); 1184 if (sampleCnt == 0) { 1185 return {}; 1186 } 1187 1188 GrMtlTextureInfo info; 1189 if (!this->createMtlTextureForBackendSurface(format, 1190 dimensions, 1191 sampleCnt, 1192 GrTexturable::kNo, 1193 GrRenderable::kYes, 1194 skgpu::Mipmapped::kNo, 1195 &info)) { 1196 return {}; 1197 } 1198 1199 return GrBackendRenderTargets::MakeMtl(dimensions.width(), dimensions.height(), info); 1200} 1201 1202void GrMtlGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget& rt) { 1203 SkASSERT(GrBackendApi::kMetal == rt.backend()); 1204 1205 GrMtlTextureInfo info; 1206 if (GrBackendRenderTargets::GetMtlTextureInfo(rt, &info)) { 1207 this->submitToGpu(GrSyncCpu::kYes); 1208 // Nothing else to do here, will get cleaned up when the GrBackendRenderTarget 1209 // is deleted. 1210 } 1211} 1212#endif // defined(GR_TEST_UTILS) 1213 1214void GrMtlGpu::copySurfaceAsResolve(GrSurface* dst, GrSurface* src) { 1215 // TODO: Add support for subrectangles 1216 GrMtlRenderTarget* srcRT = static_cast<GrMtlRenderTarget*>(src->asRenderTarget()); 1217 GrRenderTarget* dstRT = dst->asRenderTarget(); 1218 GrMtlAttachment* dstAttachment; 1219 if (dstRT) { 1220 GrMtlRenderTarget* mtlRT = static_cast<GrMtlRenderTarget*>(dstRT); 1221 dstAttachment = mtlRT->colorAttachment(); 1222 } else { 1223 SkASSERT(dst->asTexture()); 1224 dstAttachment = static_cast<GrMtlTexture*>(dst->asTexture())->attachment(); 1225 } 1226 1227 this->resolve(dstAttachment, srcRT->colorAttachment()); 1228} 1229 1230void GrMtlGpu::copySurfaceAsBlit(GrSurface* dst, GrSurface* src, 1231 GrMtlAttachment* dstAttachment, GrMtlAttachment* srcAttachment, 1232 const SkIRect& srcRect, const SkIPoint& dstPoint) { 1233#ifdef SK_DEBUG 1234 SkASSERT(this->mtlCaps().canCopyAsBlit(dstAttachment->mtlFormat(), dstAttachment->numSamples(), 1235 srcAttachment->mtlFormat(), dstAttachment->numSamples(), 1236 srcRect, dstPoint, dst == src)); 1237#endif 1238 id<MTLTexture> GR_NORETAIN dstTex = dstAttachment->mtlTexture(); 1239 id<MTLTexture> GR_NORETAIN srcTex = srcAttachment->mtlTexture(); 1240 1241 auto cmdBuffer = this->commandBuffer(); 1242 id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder(); 1243 if (!blitCmdEncoder) { 1244 return; 1245 } 1246#ifdef SK_ENABLE_MTL_DEBUG_INFO 1247 [blitCmdEncoder pushDebugGroup:@"copySurfaceAsBlit"]; 1248#endif 1249 [blitCmdEncoder copyFromTexture: srcTex 1250 sourceSlice: 0 1251 sourceLevel: 0 1252 sourceOrigin: MTLOriginMake(srcRect.x(), srcRect.y(), 0) 1253 sourceSize: MTLSizeMake(srcRect.width(), srcRect.height(), 1) 1254 toTexture: dstTex 1255 destinationSlice: 0 1256 destinationLevel: 0 1257 destinationOrigin: MTLOriginMake(dstPoint.fX, dstPoint.fY, 0)]; 1258#ifdef SK_ENABLE_MTL_DEBUG_INFO 1259 [blitCmdEncoder popDebugGroup]; 1260#endif 1261 cmdBuffer->addGrSurface(sk_ref_sp<const GrSurface>(dst)); 1262 cmdBuffer->addGrSurface(sk_ref_sp<const GrSurface>(src)); 1263} 1264 1265bool GrMtlGpu::onCopySurface(GrSurface* dst, const SkIRect& dstRect, 1266 GrSurface* src, const SkIRect& srcRect, 1267 GrSamplerState::Filter) { 1268 SkASSERT(!src->isProtected() && !dst->isProtected()); 1269 1270 if (srcRect.size() != dstRect.size()) { 1271 return false; 1272 } 1273 1274 GrMtlAttachment* dstAttachment; 1275 GrMtlAttachment* srcAttachment; 1276 GrRenderTarget* dstRT = dst->asRenderTarget(); 1277 if (dstRT) { 1278 GrMtlRenderTarget* mtlRT = static_cast<GrMtlRenderTarget*>(dstRT); 1279 // This will technically return true for single sample rts that used DMSAA in which case we 1280 // don't have to pick the resolve attachment. But in that case the resolve and color 1281 // attachments will be the same anyways. 1282 if (this->mtlCaps().renderTargetSupportsDiscardableMSAA(mtlRT)) { 1283 dstAttachment = mtlRT->resolveAttachment(); 1284 } else { 1285 dstAttachment = mtlRT->colorAttachment(); 1286 } 1287 } else if (dst->asTexture()) { 1288 dstAttachment = static_cast<GrMtlTexture*>(dst->asTexture())->attachment(); 1289 } else { 1290 // The surface in a GrAttachment already 1291 dstAttachment = static_cast<GrMtlAttachment*>(dst); 1292 } 1293 GrRenderTarget* srcRT = src->asRenderTarget(); 1294 if (srcRT) { 1295 GrMtlRenderTarget* mtlRT = static_cast<GrMtlRenderTarget*>(srcRT); 1296 // This will technically return true for single sample rts that used DMSAA in which case we 1297 // don't have to pick the resolve attachment. But in that case the resolve and color 1298 // attachments will be the same anyways. 1299 if (this->mtlCaps().renderTargetSupportsDiscardableMSAA(mtlRT)) { 1300 srcAttachment = mtlRT->resolveAttachment(); 1301 } else { 1302 srcAttachment = mtlRT->colorAttachment(); 1303 } 1304 } else if (src->asTexture()) { 1305 SkASSERT(src->asTexture()); 1306 srcAttachment = static_cast<GrMtlTexture*>(src->asTexture())->attachment(); 1307 } else { 1308 // The surface in a GrAttachment already 1309 srcAttachment = static_cast<GrMtlAttachment*>(src); 1310 } 1311 1312 MTLPixelFormat dstFormat = dstAttachment->mtlFormat(); 1313 MTLPixelFormat srcFormat = srcAttachment->mtlFormat(); 1314 1315 int dstSampleCnt = dstAttachment->sampleCount(); 1316 int srcSampleCnt = srcAttachment->sampleCount(); 1317 1318 const SkIPoint dstPoint = dstRect.topLeft(); 1319 if (this->mtlCaps().canCopyAsResolve(dstFormat, dstSampleCnt, 1320 srcFormat, srcSampleCnt, 1321 SkToBool(srcRT), src->dimensions(), 1322 srcRect, dstPoint, 1323 dstAttachment == srcAttachment)) { 1324 this->copySurfaceAsResolve(dst, src); 1325 return true; 1326 } 1327 1328 if (srcAttachment->framebufferOnly() || dstAttachment->framebufferOnly()) { 1329 return false; 1330 } 1331 1332 if (this->mtlCaps().canCopyAsBlit(dstFormat, dstSampleCnt, srcFormat, srcSampleCnt, 1333 srcRect, dstPoint, dstAttachment == srcAttachment)) { 1334 this->copySurfaceAsBlit(dst, src, dstAttachment, srcAttachment, srcRect, dstPoint); 1335 return true; 1336 } 1337 1338 return false; 1339} 1340 1341bool GrMtlGpu::onWritePixels(GrSurface* surface, 1342 SkIRect rect, 1343 GrColorType surfaceColorType, 1344 GrColorType srcColorType, 1345 const GrMipLevel texels[], 1346 int mipLevelCount, 1347 bool prepForTexSampling) { 1348 GrMtlTexture* mtlTexture = static_cast<GrMtlTexture*>(surface->asTexture()); 1349 // TODO: In principle we should be able to support pure rendertargets as well, but 1350 // until we find a use case we'll only support texture rendertargets. 1351 if (!mtlTexture) { 1352 return false; 1353 } 1354 if (!mipLevelCount) { 1355 return false; 1356 } 1357#ifdef SK_DEBUG 1358 for (int i = 0; i < mipLevelCount; i++) { 1359 SkASSERT(texels[i].fPixels); 1360 } 1361#endif 1362 return this->uploadToTexture(mtlTexture, rect, srcColorType, texels, mipLevelCount); 1363} 1364 1365bool GrMtlGpu::onReadPixels(GrSurface* surface, 1366 SkIRect rect, 1367 GrColorType surfaceColorType, 1368 GrColorType dstColorType, 1369 void* buffer, 1370 size_t rowBytes) { 1371 SkASSERT(surface); 1372 1373 if (surfaceColorType != dstColorType) { 1374 return false; 1375 } 1376 1377 int bpp = GrColorTypeBytesPerPixel(dstColorType); 1378 size_t transBufferRowBytes = bpp*rect.width(); 1379 size_t transBufferImageBytes = transBufferRowBytes*rect.height(); 1380 1381 GrResourceProvider* resourceProvider = this->getContext()->priv().resourceProvider(); 1382 sk_sp<GrGpuBuffer> transferBuffer = resourceProvider->createBuffer( 1383 transBufferImageBytes, 1384 GrGpuBufferType::kXferGpuToCpu, 1385 kDynamic_GrAccessPattern, 1386 GrResourceProvider::ZeroInit::kNo); 1387 1388 if (!transferBuffer) { 1389 return false; 1390 } 1391 1392 GrMtlBuffer* grMtlBuffer = static_cast<GrMtlBuffer*>(transferBuffer.get()); 1393 if (!this->readOrTransferPixels(surface, 1394 rect, 1395 dstColorType, 1396 grMtlBuffer->mtlBuffer(), 1397 0, 1398 transBufferImageBytes, 1399 transBufferRowBytes)) { 1400 return false; 1401 } 1402 this->submitCommandBuffer(kForce_SyncQueue); 1403 1404 const void* mappedMemory = grMtlBuffer->mtlBuffer().contents; 1405 1406 SkRectMemcpy(buffer, 1407 rowBytes, 1408 mappedMemory, 1409 transBufferRowBytes, 1410 transBufferRowBytes, 1411 rect.height()); 1412 1413 return true; 1414} 1415 1416bool GrMtlGpu::onTransferFromBufferToBuffer(sk_sp<GrGpuBuffer> src, 1417 size_t srcOffset, 1418 sk_sp<GrGpuBuffer> dst, 1419 size_t dstOffset, 1420 size_t size) { 1421 id<MTLBuffer> GR_NORETAIN mtlSrc = static_cast<GrMtlBuffer*>(src.get())->mtlBuffer(); 1422 id<MTLBuffer> GR_NORETAIN mtlDst = static_cast<GrMtlBuffer*>(dst.get())->mtlBuffer(); 1423 SkASSERT(mtlSrc); 1424 SkASSERT(mtlDst); 1425 1426 auto cmdBuffer = this->commandBuffer(); 1427 id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder(); 1428 if (!blitCmdEncoder) { 1429 return false; 1430 } 1431 1432#ifdef SK_ENABLE_MTL_DEBUG_INFO 1433 [blitCmdEncoder pushDebugGroup:@"onTransferFromBufferToBuffer"]; 1434#endif 1435 [blitCmdEncoder copyFromBuffer: mtlSrc 1436 sourceOffset: srcOffset 1437 toBuffer: mtlDst 1438 destinationOffset: dstOffset 1439 size: size]; 1440#ifdef SK_ENABLE_MTL_DEBUG_INFO 1441 [blitCmdEncoder popDebugGroup]; 1442#endif 1443 1444 cmdBuffer->addGrBuffer(std::move(src)); 1445 cmdBuffer->addGrBuffer(std::move(dst)); 1446 1447 return true; 1448} 1449 1450bool GrMtlGpu::onTransferPixelsTo(GrTexture* texture, 1451 SkIRect rect, 1452 GrColorType textureColorType, 1453 GrColorType bufferColorType, 1454 sk_sp<GrGpuBuffer> transferBuffer, 1455 size_t offset, 1456 size_t rowBytes) { 1457 SkASSERT(texture); 1458 SkASSERT(transferBuffer); 1459 if (textureColorType != bufferColorType) { 1460 return false; 1461 } 1462 1463 GrMtlTexture* grMtlTexture = static_cast<GrMtlTexture*>(texture); 1464 id<MTLTexture> GR_NORETAIN mtlTexture = grMtlTexture->mtlTexture(); 1465 SkASSERT(mtlTexture); 1466 1467 GrMtlBuffer* grMtlBuffer = static_cast<GrMtlBuffer*>(transferBuffer.get()); 1468 id<MTLBuffer> GR_NORETAIN mtlBuffer = grMtlBuffer->mtlBuffer(); 1469 SkASSERT(mtlBuffer); 1470 1471 size_t bpp = GrColorTypeBytesPerPixel(bufferColorType); 1472 if (offset % bpp) { 1473 return false; 1474 } 1475 if (GrBackendFormatBytesPerPixel(texture->backendFormat()) != bpp) { 1476 return false; 1477 } 1478 1479 MTLOrigin origin = MTLOriginMake(rect.left(), rect.top(), 0); 1480 1481 auto cmdBuffer = this->commandBuffer(); 1482 id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder(); 1483 if (!blitCmdEncoder) { 1484 return false; 1485 } 1486#ifdef SK_ENABLE_MTL_DEBUG_INFO 1487 [blitCmdEncoder pushDebugGroup:@"onTransferPixelsTo"]; 1488#endif 1489 [blitCmdEncoder copyFromBuffer: mtlBuffer 1490 sourceOffset: offset 1491 sourceBytesPerRow: rowBytes 1492 sourceBytesPerImage: rowBytes*rect.height() 1493 sourceSize: MTLSizeMake(rect.width(), rect.height(), 1) 1494 toTexture: mtlTexture 1495 destinationSlice: 0 1496 destinationLevel: 0 1497 destinationOrigin: origin]; 1498#ifdef SK_ENABLE_MTL_DEBUG_INFO 1499 [blitCmdEncoder popDebugGroup]; 1500#endif 1501 1502 return true; 1503} 1504 1505bool GrMtlGpu::onTransferPixelsFrom(GrSurface* surface, 1506 SkIRect rect, 1507 GrColorType surfaceColorType, 1508 GrColorType bufferColorType, 1509 sk_sp<GrGpuBuffer> transferBuffer, 1510 size_t offset) { 1511 SkASSERT(surface); 1512 SkASSERT(transferBuffer); 1513 1514 if (surfaceColorType != bufferColorType) { 1515 return false; 1516 } 1517 1518 // Metal only supports offsets that are aligned to a pixel. 1519 size_t bpp = GrColorTypeBytesPerPixel(bufferColorType); 1520 if (offset % bpp) { 1521 return false; 1522 } 1523 if (GrBackendFormatBytesPerPixel(surface->backendFormat()) != bpp) { 1524 return false; 1525 } 1526 1527 GrMtlBuffer* grMtlBuffer = static_cast<GrMtlBuffer*>(transferBuffer.get()); 1528 1529 size_t transBufferRowBytes = bpp*rect.width(); 1530 size_t transBufferImageBytes = transBufferRowBytes*rect.height(); 1531 1532 return this->readOrTransferPixels(surface, 1533 rect, 1534 bufferColorType, 1535 grMtlBuffer->mtlBuffer(), 1536 offset, 1537 transBufferImageBytes, 1538 transBufferRowBytes); 1539} 1540 1541bool GrMtlGpu::readOrTransferPixels(GrSurface* surface, 1542 SkIRect rect, 1543 GrColorType dstColorType, 1544 id<MTLBuffer> transferBuffer, 1545 size_t offset, 1546 size_t imageBytes, 1547 size_t rowBytes) { 1548 if (!check_max_blit_width(rect.width())) { 1549 return false; 1550 } 1551 1552 id<MTLTexture> mtlTexture; 1553 if (GrMtlRenderTarget* rt = static_cast<GrMtlRenderTarget*>(surface->asRenderTarget())) { 1554 if (rt->numSamples() > 1) { 1555 SkASSERT(rt->requiresManualMSAAResolve()); // msaa-render-to-texture not yet supported. 1556 mtlTexture = rt->resolveMTLTexture(); 1557 } else { 1558 SkASSERT(!rt->requiresManualMSAAResolve()); 1559 mtlTexture = rt->colorMTLTexture(); 1560 } 1561 } else if (GrMtlTexture* texture = static_cast<GrMtlTexture*>(surface->asTexture())) { 1562 mtlTexture = texture->mtlTexture(); 1563 } 1564 if (!mtlTexture) { 1565 return false; 1566 } 1567 1568 auto cmdBuffer = this->commandBuffer(); 1569 id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder(); 1570 if (!blitCmdEncoder) { 1571 return false; 1572 } 1573#ifdef SK_ENABLE_MTL_DEBUG_INFO 1574 [blitCmdEncoder pushDebugGroup:@"readOrTransferPixels"]; 1575#endif 1576 [blitCmdEncoder copyFromTexture: mtlTexture 1577 sourceSlice: 0 1578 sourceLevel: 0 1579 sourceOrigin: MTLOriginMake(rect.left(), rect.top(), 0) 1580 sourceSize: MTLSizeMake(rect.width(), rect.height(), 1) 1581 toBuffer: transferBuffer 1582 destinationOffset: offset 1583 destinationBytesPerRow: rowBytes 1584 destinationBytesPerImage: imageBytes]; 1585#ifdef SK_BUILD_FOR_MAC 1586 if (this->mtlCaps().isMac()) { 1587 // Sync GPU data back to the CPU 1588 [blitCmdEncoder synchronizeResource: transferBuffer]; 1589 } 1590#endif 1591#ifdef SK_ENABLE_MTL_DEBUG_INFO 1592 [blitCmdEncoder popDebugGroup]; 1593#endif 1594 1595 return true; 1596} 1597 1598[[nodiscard]] std::unique_ptr<GrSemaphore> GrMtlGpu::makeSemaphore(bool /*isOwned*/) { 1599 SkASSERT(this->caps()->semaphoreSupport()); 1600 return GrMtlSemaphore::Make(this); 1601} 1602 1603std::unique_ptr<GrSemaphore> GrMtlGpu::wrapBackendSemaphore(const GrBackendSemaphore& semaphore, 1604 GrSemaphoreWrapType /* wrapType */, 1605 GrWrapOwnership /*ownership*/) { 1606 SkASSERT(this->caps()->backendSemaphoreSupport()); 1607 return GrMtlSemaphore::MakeWrapped(GrBackendSemaphores::GetMtlHandle(semaphore), 1608 GrBackendSemaphores::GetMtlValue(semaphore)); 1609} 1610 1611void GrMtlGpu::insertSemaphore(GrSemaphore* semaphore) { 1612 if (@available(macOS 10.14, iOS 12.0, tvOS 12.0, *)) { 1613 SkASSERT(semaphore); 1614 GrMtlSemaphore* mtlSem = static_cast<GrMtlSemaphore*>(semaphore); 1615 1616 this->commandBuffer()->encodeSignalEvent(mtlSem->event(), mtlSem->value()); 1617 } 1618} 1619 1620void GrMtlGpu::waitSemaphore(GrSemaphore* semaphore) { 1621 if (@available(macOS 10.14, iOS 12.0, tvOS 12.0, *)) { 1622 SkASSERT(semaphore); 1623 GrMtlSemaphore* mtlSem = static_cast<GrMtlSemaphore*>(semaphore); 1624 1625 this->commandBuffer()->encodeWaitForEvent(mtlSem->event(), mtlSem->value()); 1626 } 1627} 1628 1629void GrMtlGpu::onResolveRenderTarget(GrRenderTarget* target, const SkIRect&) { 1630 SkASSERT(target->numSamples() > 1); 1631 GrMtlRenderTarget* rt = static_cast<GrMtlRenderTarget*>(target); 1632 1633 if (rt->resolveAttachment() && this->mtlCaps().renderTargetSupportsDiscardableMSAA(rt)) { 1634 // We would have resolved the RT during the render pass. 1635 return; 1636 } 1637 1638 this->resolve(static_cast<GrMtlRenderTarget*>(target)->resolveAttachment(), 1639 static_cast<GrMtlRenderTarget*>(target)->colorAttachment()); 1640} 1641 1642void GrMtlGpu::resolve(GrMtlAttachment* resolveAttachment, 1643 GrMtlAttachment* msaaAttachment) { 1644 auto renderPassDesc = [[MTLRenderPassDescriptor alloc] init]; 1645 auto colorAttachment = renderPassDesc.colorAttachments[0]; 1646 colorAttachment.texture = msaaAttachment->mtlTexture(); 1647 colorAttachment.resolveTexture = resolveAttachment->mtlTexture(); 1648 colorAttachment.loadAction = MTLLoadActionLoad; 1649 colorAttachment.storeAction = MTLStoreActionMultisampleResolve; 1650 1651 GrMtlRenderCommandEncoder* cmdEncoder = 1652 this->commandBuffer()->getRenderCommandEncoder(renderPassDesc, nullptr, nullptr); 1653 if (cmdEncoder) { 1654 cmdEncoder->setLabel(@"resolveTexture"); 1655 this->commandBuffer()->addGrSurface(sk_ref_sp<const GrSurface>(resolveAttachment)); 1656 this->commandBuffer()->addGrSurface(sk_ref_sp<const GrSurface>(msaaAttachment)); 1657 } 1658} 1659 1660GrMtlRenderCommandEncoder* GrMtlGpu::loadMSAAFromResolve( 1661 GrAttachment* dst, GrMtlAttachment* src, const SkIRect& srcRect, 1662 MTLRenderPassStencilAttachmentDescriptor* stencil) { 1663 if (!dst) { 1664 return nil; 1665 } 1666 if (!src || src->framebufferOnly()) { 1667 return nil; 1668 } 1669 1670 GrMtlAttachment* mtlDst = static_cast<GrMtlAttachment*>(dst); 1671 1672 MTLPixelFormat stencilFormat = stencil.texture.pixelFormat; 1673 auto renderPipeline = this->resourceProvider().findOrCreateMSAALoadPipeline(mtlDst->mtlFormat(), 1674 dst->numSamples(), 1675 stencilFormat); 1676 1677 // Set up rendercommandencoder 1678 auto renderPassDesc = [MTLRenderPassDescriptor new]; 1679 auto colorAttachment = renderPassDesc.colorAttachments[0]; 1680 colorAttachment.texture = mtlDst->mtlTexture(); 1681 colorAttachment.loadAction = MTLLoadActionDontCare; 1682 colorAttachment.storeAction = MTLStoreActionMultisampleResolve; 1683 colorAttachment.resolveTexture = src->mtlTexture(); 1684 1685 renderPassDesc.stencilAttachment = stencil; 1686 1687 // We know in this case that the preceding renderCommandEncoder will not be compatible. 1688 // Either it's using a different rendertarget, or we are reading from the resolve and 1689 // hence we need to let the previous resolve finish. So we create a new one without checking. 1690 auto renderCmdEncoder = 1691 this->commandBuffer()->getRenderCommandEncoder(renderPassDesc, nullptr); 1692 if (!renderCmdEncoder) { 1693 return nullptr; 1694 } 1695 1696 // Bind pipeline 1697 renderCmdEncoder->setRenderPipelineState(renderPipeline->mtlPipelineState()); 1698 this->commandBuffer()->addResource(sk_ref_sp(renderPipeline)); 1699 1700 // Bind src as input texture 1701 renderCmdEncoder->setFragmentTexture(src->mtlTexture(), 0); 1702 // No sampler needed 1703 this->commandBuffer()->addGrSurface(sk_ref_sp<GrSurface>(src)); 1704 1705 // Scissor and viewport should default to size of color attachment 1706 1707 // Update and bind uniform data 1708 int w = srcRect.width(); 1709 int h = srcRect.height(); 1710 1711 // dst rect edges in NDC (-1 to 1) 1712 int dw = dst->width(); 1713 int dh = dst->height(); 1714 float dx0 = 2.f * srcRect.fLeft / dw - 1.f; 1715 float dx1 = 2.f * (srcRect.fLeft + w) / dw - 1.f; 1716 float dy0 = 2.f * srcRect.fTop / dh - 1.f; 1717 float dy1 = 2.f * (srcRect.fTop + h) / dh - 1.f; 1718 1719 struct { 1720 float posXform[4]; 1721 int textureSize[2]; 1722 int pad[2]; 1723 } uniData = {{dx1 - dx0, dy1 - dy0, dx0, dy0}, {dw, dh}, {0, 0}}; 1724 1725 constexpr size_t uniformSize = 32; 1726 if (@available(macOS 10.11, iOS 8.3, tvOS 9.0, *)) { 1727 SkASSERT(uniformSize <= this->caps()->maxPushConstantsSize()); 1728 renderCmdEncoder->setVertexBytes(&uniData, uniformSize, 0); 1729 } else { 1730 // upload the data 1731 GrRingBuffer::Slice slice = this->uniformsRingBuffer()->suballocate(uniformSize); 1732 GrMtlBuffer* buffer = (GrMtlBuffer*) slice.fBuffer; 1733 char* destPtr = static_cast<char*>(slice.fBuffer->map()) + slice.fOffset; 1734 memcpy(destPtr, &uniData, uniformSize); 1735 1736 renderCmdEncoder->setVertexBuffer(buffer->mtlBuffer(), slice.fOffset, 0); 1737 } 1738 1739 renderCmdEncoder->drawPrimitives(MTLPrimitiveTypeTriangleStrip, (NSUInteger)0, (NSUInteger)4); 1740 1741 return renderCmdEncoder; 1742} 1743 1744#if defined(GR_TEST_UTILS) 1745void GrMtlGpu::testingOnly_startCapture() { 1746 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 1747 // TODO: add Metal 3 interface as well 1748 MTLCaptureManager* captureManager = [MTLCaptureManager sharedCaptureManager]; 1749 if (captureManager.isCapturing) { 1750 return; 1751 } 1752 if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { 1753 MTLCaptureDescriptor* captureDescriptor = [[MTLCaptureDescriptor alloc] init]; 1754 captureDescriptor.captureObject = fQueue; 1755 1756 NSError *error; 1757 if (![captureManager startCaptureWithDescriptor: captureDescriptor error:&error]) 1758 { 1759 NSLog(@"Failed to start capture, error %@", error); 1760 } 1761 } else { 1762 [captureManager startCaptureWithCommandQueue: fQueue]; 1763 } 1764 } 1765} 1766 1767void GrMtlGpu::testingOnly_stopCapture() { 1768 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 1769 MTLCaptureManager* captureManager = [MTLCaptureManager sharedCaptureManager]; 1770 if (captureManager.isCapturing) { 1771 [captureManager stopCapture]; 1772 } 1773 } 1774} 1775#endif 1776 1777#ifdef SK_ENABLE_DUMP_GPU 1778#include "src/utils/SkJSONWriter.h" 1779void GrMtlGpu::onDumpJSON(SkJSONWriter* writer) const { 1780 // We are called by the base class, which has already called beginObject(). We choose to nest 1781 // all of our caps information in a named sub-object. 1782 writer->beginObject("Metal GPU"); 1783 1784 writer->beginObject("Device"); 1785 writer->appendCString("name", fDevice.name.UTF8String); 1786#ifdef SK_BUILD_FOR_MAC 1787 if (@available(macOS 10.11, *)) { 1788 writer->appendBool("isHeadless", fDevice.isHeadless); 1789 writer->appendBool("isLowPower", fDevice.isLowPower); 1790 } 1791 if (@available(macOS 10.13, *)) { 1792 writer->appendBool("isRemovable", fDevice.isRemovable); 1793 } 1794#endif 1795 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 1796 writer->appendU64("registryID", fDevice.registryID); 1797 } 1798#if defined(SK_BUILD_FOR_MAC) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 1799 if (@available(macOS 10.15, *)) { 1800 switch (fDevice.location) { 1801 case MTLDeviceLocationBuiltIn: 1802 writer->appendNString("location", "builtIn"); 1803 break; 1804 case MTLDeviceLocationSlot: 1805 writer->appendNString("location", "slot"); 1806 break; 1807 case MTLDeviceLocationExternal: 1808 writer->appendNString("location", "external"); 1809 break; 1810 case MTLDeviceLocationUnspecified: 1811 writer->appendNString("location", "unspecified"); 1812 break; 1813 default: 1814 writer->appendNString("location", "unknown"); 1815 break; 1816 } 1817 writer->appendU64("locationNumber", fDevice.locationNumber); 1818 writer->appendU64("maxTransferRate", fDevice.maxTransferRate); 1819 } 1820#endif // SK_BUILD_FOR_MAC 1821#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 1822 if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { 1823 writer->appendBool("hasUnifiedMemory", fDevice.hasUnifiedMemory); 1824 } 1825#endif 1826#ifdef SK_BUILD_FOR_MAC 1827#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 1828 if (@available(macOS 10.15, *)) { 1829 writer->appendU64("peerGroupID", fDevice.peerGroupID); 1830 writer->appendU32("peerCount", fDevice.peerCount); 1831 writer->appendU32("peerIndex", fDevice.peerIndex); 1832 } 1833#endif 1834 if (@available(macOS 10.12, *)) { 1835 writer->appendU64("recommendedMaxWorkingSetSize", fDevice.recommendedMaxWorkingSetSize); 1836 } 1837#endif // SK_BUILD_FOR_MAC 1838 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 1839 writer->appendU64("currentAllocatedSize", fDevice.currentAllocatedSize); 1840 writer->appendU64("maxThreadgroupMemoryLength", fDevice.maxThreadgroupMemoryLength); 1841 } 1842 1843 if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { 1844 writer->beginObject("maxThreadsPerThreadgroup"); 1845 writer->appendU64("width", fDevice.maxThreadsPerThreadgroup.width); 1846 writer->appendU64("height", fDevice.maxThreadsPerThreadgroup.height); 1847 writer->appendU64("depth", fDevice.maxThreadsPerThreadgroup.depth); 1848 writer->endObject(); 1849 } 1850 1851 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 1852 writer->appendBool("areProgrammableSamplePositionsSupported", 1853 fDevice.areProgrammableSamplePositionsSupported); 1854 writer->appendBool("areRasterOrderGroupsSupported", 1855 fDevice.areRasterOrderGroupsSupported); 1856 } 1857#ifdef SK_BUILD_FOR_MAC 1858 if (@available(macOS 10.11, *)) { 1859 writer->appendBool("isDepth24Stencil8PixelFormatSupported", 1860 fDevice.isDepth24Stencil8PixelFormatSupported); 1861 1862 } 1863#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 1864 if (@available(macOS 10.15, *)) { 1865 writer->appendBool("areBarycentricCoordsSupported", 1866 fDevice.areBarycentricCoordsSupported); 1867 writer->appendBool("supportsShaderBarycentricCoordinates", 1868 fDevice.supportsShaderBarycentricCoordinates); 1869 } 1870#endif 1871#endif // SK_BUILD_FOR_MAC 1872 if (@available(macOS 10.14, iOS 12.0, tvOS 12.0, *)) { 1873 writer->appendU64("maxBufferLength", fDevice.maxBufferLength); 1874 } 1875 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 1876 switch (fDevice.readWriteTextureSupport) { 1877 case MTLReadWriteTextureTier1: 1878 writer->appendNString("readWriteTextureSupport", "tier1"); 1879 break; 1880 case MTLReadWriteTextureTier2: 1881 writer->appendNString("readWriteTextureSupport", "tier2"); 1882 break; 1883 case MTLReadWriteTextureTierNone: 1884 writer->appendNString("readWriteTextureSupport", "tierNone"); 1885 break; 1886 default: 1887 writer->appendNString("readWriteTextureSupport", "unknown"); 1888 break; 1889 } 1890 switch (fDevice.argumentBuffersSupport) { 1891 case MTLArgumentBuffersTier1: 1892 writer->appendNString("argumentBuffersSupport", "tier1"); 1893 break; 1894 case MTLArgumentBuffersTier2: 1895 writer->appendNString("argumentBuffersSupport", "tier2"); 1896 break; 1897 default: 1898 writer->appendNString("argumentBuffersSupport", "unknown"); 1899 break; 1900 } 1901 } 1902 if (@available(macOS 10.14, iOS 12.0, tvOS 12.0, *)) { 1903 writer->appendU64("maxArgumentBufferSamplerCount", fDevice.maxArgumentBufferSamplerCount); 1904 } 1905#ifdef SK_BUILD_FOR_IOS 1906 if (@available(iOS 13.0, tvOS 13.0, *)) { 1907 writer->appendU64("sparseTileSizeInBytes", fDevice.sparseTileSizeInBytes); 1908 } 1909#endif 1910 writer->endObject(); 1911 1912 writer->appendCString("queue", fQueue.label.UTF8String); 1913 writer->appendBool("disconnected", fDisconnected); 1914 1915 writer->endObject(); 1916} 1917#endif 1918 1919GR_NORETAIN_END 1920