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