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