• 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/GrMtlUtil.h"
9
10#include "include/gpu/GrSurface.h"
11#include "include/private/GrTypesPriv.h"
12#include "include/private/SkMutex.h"
13#include "src/gpu/mtl/GrMtlGpu.h"
14#include "src/gpu/mtl/GrMtlRenderTarget.h"
15#include "src/gpu/mtl/GrMtlTexture.h"
16#include "src/sksl/SkSLCompiler.h"
17
18#import <Metal/Metal.h>
19
20#if !__has_feature(objc_arc)
21#error This file must be compiled with Arc. Use -fobjc-arc flag
22#endif
23
24#define PRINT_MSL 0 // print out the MSL code generated
25
26NSError* GrCreateMtlError(NSString* description, GrMtlErrorCode errorCode) {
27    NSDictionary* userInfo = [NSDictionary dictionaryWithObject:description
28                                                         forKey:NSLocalizedDescriptionKey];
29    return [NSError errorWithDomain:@"org.skia.ganesh"
30                               code:(NSInteger)errorCode
31                           userInfo:userInfo];
32}
33
34MTLTextureDescriptor* GrGetMTLTextureDescriptor(id<MTLTexture> mtlTexture) {
35    MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
36    texDesc.textureType = mtlTexture.textureType;
37    texDesc.pixelFormat = mtlTexture.pixelFormat;
38    texDesc.width = mtlTexture.width;
39    texDesc.height = mtlTexture.height;
40    texDesc.depth = mtlTexture.depth;
41    texDesc.mipmapLevelCount = mtlTexture.mipmapLevelCount;
42    texDesc.arrayLength = mtlTexture.arrayLength;
43    texDesc.sampleCount = mtlTexture.sampleCount;
44    if (@available(macOS 10.11, iOS 9.0, *)) {
45        texDesc.usage = mtlTexture.usage;
46    }
47    return texDesc;
48}
49
50#if PRINT_MSL
51void print_msl(const char* source) {
52    SkTArray<SkString> lines;
53    SkStrSplit(source, "\n", kStrict_SkStrSplitMode, &lines);
54    for (int i = 0; i < lines.count(); i++) {
55        SkString& line = lines[i];
56        line.prependf("%4i\t", i + 1);
57        SkDebugf("%s\n", line.c_str());
58    }
59}
60#endif
61
62id<MTLLibrary> GrGenerateMtlShaderLibrary(const GrMtlGpu* gpu,
63                                          const SkSL::String& shaderString,
64                                          SkSL::Program::Kind kind,
65                                          const SkSL::Program::Settings& settings,
66                                          SkSL::String* mslShader,
67                                          SkSL::Program::Inputs* outInputs) {
68    std::unique_ptr<SkSL::Program> program =
69            gpu->shaderCompiler()->convertProgram(kind,
70                                                  shaderString,
71                                                  settings);
72
73    if (!program) {
74        SkDebugf("SkSL error:\n%s\n", gpu->shaderCompiler()->errorText().c_str());
75        SkASSERT(false);
76        return nil;
77    }
78
79    *outInputs = program->fInputs;
80    if (!gpu->shaderCompiler()->toMetal(*program, mslShader)) {
81        SkDebugf("%s\n", gpu->shaderCompiler()->errorText().c_str());
82        SkASSERT(false);
83        return nil;
84    }
85
86    return GrCompileMtlShaderLibrary(gpu, *mslShader);
87}
88
89id<MTLLibrary> GrCompileMtlShaderLibrary(const GrMtlGpu* gpu,
90                                         const SkSL::String& shaderString) {
91    NSString* mtlCode = [[NSString alloc] initWithCString: shaderString.c_str()
92                                                 encoding: NSASCIIStringEncoding];
93#if PRINT_MSL
94    print_msl([mtlCode cStringUsingEncoding: NSASCIIStringEncoding]);
95#endif
96
97    MTLCompileOptions* defaultOptions = [[MTLCompileOptions alloc] init];
98    NSError* error = nil;
99#if defined(SK_BUILD_FOR_MAC)
100    id<MTLLibrary> compiledLibrary = GrMtlNewLibraryWithSource(gpu->device(), mtlCode,
101                                                               defaultOptions, &error);
102#else
103    id<MTLLibrary> compiledLibrary = [gpu->device() newLibraryWithSource: mtlCode
104                                                                 options: defaultOptions
105                                                                   error: &error];
106#endif
107    if (!compiledLibrary) {
108        SkDebugf("Error compiling MSL shader: %s\n%s\n",
109                 shaderString.c_str(),
110                 [[error localizedDescription] cStringUsingEncoding: NSASCIIStringEncoding]);
111        return nil;
112    }
113
114    return compiledLibrary;
115}
116
117// Wrapper to get atomic assignment for compiles and pipeline creation
118class MtlCompileResult : public SkRefCnt {
119public:
120    MtlCompileResult() : fCompiledObject(nil), fError(nil) {}
121    ~MtlCompileResult() = default;
122    void set(id compiledObject, NSError* error) {
123        SkAutoMutexExclusive automutex(fMutex);
124        fCompiledObject = compiledObject;
125        fError = error;
126    }
127    std::pair<id, NSError*> get() {
128        SkAutoMutexExclusive automutex(fMutex);
129        return std::make_pair(fCompiledObject, fError);
130    }
131private:
132    SkMutex fMutex;
133    id fCompiledObject SK_GUARDED_BY(fMutex);
134    NSError* fError SK_GUARDED_BY(fMutex);
135};
136
137id<MTLLibrary> GrMtlNewLibraryWithSource(id<MTLDevice> device, NSString* mslCode,
138                                         MTLCompileOptions* options, NSError** error) {
139    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
140    sk_sp<MtlCompileResult> compileResult(new MtlCompileResult);
141    // We have to increment the ref for the Obj-C block manually because it won't do it for us
142    compileResult->ref();
143    MTLNewLibraryCompletionHandler completionHandler =
144            ^(id<MTLLibrary> library, NSError* error) {
145                compileResult->set(library, error);
146                dispatch_semaphore_signal(semaphore);
147                compileResult->unref();
148            };
149
150    [device newLibraryWithSource: mslCode
151                         options: options
152               completionHandler: completionHandler];
153
154    // Wait 100 ms for the compiler
155    constexpr auto kTimeoutNS = 100000000UL;
156    if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, kTimeoutNS))) {
157        if (error) {
158            constexpr auto kTimeoutMS = kTimeoutNS/1000000UL;
159            NSString* description =
160                    [NSString stringWithFormat:@"Compilation took longer than %lu ms",
161                                               kTimeoutMS];
162            *error = GrCreateMtlError(description, GrMtlErrorCode::kTimeout);
163        }
164        return nil;
165    }
166
167    id<MTLLibrary> compiledLibrary;
168    std::tie(compiledLibrary, *error) = compileResult->get();
169
170    return compiledLibrary;
171}
172
173id<MTLRenderPipelineState> GrMtlNewRenderPipelineStateWithDescriptor(
174        id<MTLDevice> device, MTLRenderPipelineDescriptor* pipelineDescriptor, NSError** error) {
175    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
176    sk_sp<MtlCompileResult> compileResult(new MtlCompileResult);
177    // We have to increment the ref for the Obj-C block manually because it won't do it for us
178    compileResult->ref();
179    MTLNewRenderPipelineStateCompletionHandler completionHandler =
180            ^(id<MTLRenderPipelineState> state, NSError* error) {
181                compileResult->set(state, error);
182                dispatch_semaphore_signal(semaphore);
183                compileResult->unref();
184            };
185
186    [device newRenderPipelineStateWithDescriptor: pipelineDescriptor
187                               completionHandler: completionHandler];
188
189    // Wait 100 ms for pipeline creation
190    constexpr auto kTimeoutNS = 100000000UL;
191    if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, kTimeoutNS))) {
192        if (error) {
193            constexpr auto kTimeoutMS = kTimeoutNS/1000000UL;
194            NSString* description =
195                    [NSString stringWithFormat:@"Pipeline creation took longer than %lu ms",
196                                               kTimeoutMS];
197            *error = GrCreateMtlError(description, GrMtlErrorCode::kTimeout);
198        }
199        return nil;
200    }
201
202    id<MTLRenderPipelineState> pipelineState;
203    std::tie(pipelineState, *error) = compileResult->get();
204
205    return pipelineState;
206}
207
208id<MTLTexture> GrGetMTLTextureFromSurface(GrSurface* surface) {
209    id<MTLTexture> mtlTexture = nil;
210
211    GrMtlRenderTarget* renderTarget = static_cast<GrMtlRenderTarget*>(surface->asRenderTarget());
212    GrMtlTexture* texture;
213    if (renderTarget) {
214        // We should not be using this for multisampled rendertargets
215        if (renderTarget->numSamples() > 1) {
216            SkASSERT(false);
217            return nil;
218        }
219        mtlTexture = renderTarget->mtlColorTexture();
220    } else {
221        texture = static_cast<GrMtlTexture*>(surface->asTexture());
222        if (texture) {
223            mtlTexture = texture->mtlTexture();
224        }
225    }
226    return mtlTexture;
227}
228
229
230//////////////////////////////////////////////////////////////////////////////
231// CPP Utils
232
233GrMTLPixelFormat GrGetMTLPixelFormatFromMtlTextureInfo(const GrMtlTextureInfo& info) {
234    id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture.get());
235    return static_cast<GrMTLPixelFormat>(mtlTexture.pixelFormat);
236}
237
238bool GrMtlFormatIsCompressed(MTLPixelFormat mtlFormat) {
239    switch (mtlFormat) {
240#ifdef SK_BUILD_FOR_IOS
241        case MTLPixelFormatETC2_RGB8:
242            return true;
243#else
244        case MTLPixelFormatBC1_RGBA:
245            return true;
246#endif
247        default:
248            return false;
249    }
250}
251
252SkImage::CompressionType GrMtlFormatToCompressionType(MTLPixelFormat mtlFormat) {
253    switch (mtlFormat) {
254#ifdef SK_BUILD_FOR_IOS
255        case MTLPixelFormatETC2_RGB8: return SkImage::CompressionType::kETC2_RGB8_UNORM;
256#else
257        case MTLPixelFormatBC1_RGBA:  return SkImage::CompressionType::kBC1_RGBA8_UNORM;
258#endif
259        default:                      return SkImage::CompressionType::kNone;
260    }
261
262    SkUNREACHABLE;
263}
264
265#if GR_TEST_UTILS
266bool GrMtlFormatIsBGRA(GrMTLPixelFormat mtlFormat) {
267    return mtlFormat == MTLPixelFormatBGRA8Unorm;
268}
269
270const char* GrMtlFormatToStr(GrMTLPixelFormat mtlFormat) {
271    switch (mtlFormat) {
272        case MTLPixelFormatInvalid:         return "Invalid";
273        case MTLPixelFormatRGBA8Unorm:      return "RGBA8Unorm";
274        case MTLPixelFormatR8Unorm:         return "R8Unorm";
275        case MTLPixelFormatA8Unorm:         return "A8Unorm";
276        case MTLPixelFormatBGRA8Unorm:      return "BGRA8Unorm";
277#ifdef SK_BUILD_FOR_IOS
278        case MTLPixelFormatB5G6R5Unorm:     return "B5G6R5Unorm";
279#endif
280        case MTLPixelFormatRGBA16Float:     return "RGBA16Float";
281        case MTLPixelFormatR16Float:        return "R16Float";
282        case MTLPixelFormatRG8Unorm:        return "RG8Unorm";
283        case MTLPixelFormatRGB10A2Unorm:    return "RGB10A2Unorm";
284#ifdef SK_BUILD_FOR_IOS
285        case MTLPixelFormatABGR4Unorm:      return "ABGR4Unorm";
286#endif
287        case MTLPixelFormatRGBA8Unorm_sRGB: return "RGBA8Unorm_sRGB";
288        case MTLPixelFormatR16Unorm:        return "R16Unorm";
289        case MTLPixelFormatRG16Unorm:       return "RG16Unorm";
290#ifdef SK_BUILD_FOR_IOS
291        case MTLPixelFormatETC2_RGB8:       return "ETC2_RGB8";
292#else
293        case MTLPixelFormatBC1_RGBA:        return "BC1_RGBA";
294#endif
295        case MTLPixelFormatRGBA16Unorm:     return "RGBA16Unorm";
296        case MTLPixelFormatRG16Float:       return "RG16Float";
297
298        default:                            return "Unknown";
299    }
300}
301
302#endif
303
304
305
306