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