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