| /* |
| * Copyright 2017 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/gpu/mtl/GrMtlUtil.h" |
| |
| #include "include/gpu/GrBackendSurface.h" |
| #include "include/private/GrTypesPriv.h" |
| #include "include/private/SkMutex.h" |
| #include "src/core/SkTraceEvent.h" |
| #include "src/gpu/GrSurface.h" |
| #include "src/gpu/mtl/GrMtlGpu.h" |
| #include "src/gpu/mtl/GrMtlRenderTarget.h" |
| #include "src/gpu/mtl/GrMtlTexture.h" |
| #include "src/sksl/SkSLCompiler.h" |
| #include "src/utils/SkShaderUtils.h" |
| |
| #import <Metal/Metal.h> |
| #ifdef SK_BUILD_FOR_IOS |
| #import <UIKit/UIApplication.h> |
| #endif |
| |
| #if !__has_feature(objc_arc) |
| #error This file must be compiled with Arc. Use -fobjc-arc flag |
| #endif |
| |
| GR_NORETAIN_BEGIN |
| |
| NSError* GrCreateMtlError(NSString* description, GrMtlErrorCode errorCode) { |
| NSDictionary* userInfo = [NSDictionary dictionaryWithObject:description |
| forKey:NSLocalizedDescriptionKey]; |
| return [NSError errorWithDomain:@"org.skia.ganesh" |
| code:(NSInteger)errorCode |
| userInfo:userInfo]; |
| } |
| |
| MTLTextureDescriptor* GrGetMTLTextureDescriptor(id<MTLTexture> mtlTexture) { |
| MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init]; |
| texDesc.textureType = mtlTexture.textureType; |
| texDesc.pixelFormat = mtlTexture.pixelFormat; |
| texDesc.width = mtlTexture.width; |
| texDesc.height = mtlTexture.height; |
| texDesc.depth = mtlTexture.depth; |
| texDesc.mipmapLevelCount = mtlTexture.mipmapLevelCount; |
| texDesc.arrayLength = mtlTexture.arrayLength; |
| texDesc.sampleCount = mtlTexture.sampleCount; |
| if (@available(macOS 10.11, iOS 9.0, *)) { |
| texDesc.usage = mtlTexture.usage; |
| } |
| return texDesc; |
| } |
| |
| // Print the source code for all shaders generated. |
| static const bool gPrintSKSL = false; |
| static const bool gPrintMSL = false; |
| |
| bool GrSkSLToMSL(const GrMtlGpu* gpu, |
| const std::string& sksl, |
| SkSL::ProgramKind programKind, |
| const SkSL::Program::Settings& settings, |
| std::string* msl, |
| SkSL::Program::Inputs* outInputs, |
| GrContextOptions::ShaderErrorHandler* errorHandler) { |
| #ifdef SK_DEBUG |
| std::string src = SkShaderUtils::PrettyPrint(sksl); |
| #else |
| const std::string& src = sksl; |
| #endif |
| SkSL::Compiler* compiler = gpu->shaderCompiler(); |
| std::unique_ptr<SkSL::Program> program = |
| gpu->shaderCompiler()->convertProgram(programKind, |
| src, |
| settings); |
| if (!program || !compiler->toMetal(*program, msl)) { |
| errorHandler->compileError(src.c_str(), compiler->errorText().c_str()); |
| return false; |
| } |
| |
| if (gPrintSKSL || gPrintMSL) { |
| SkShaderUtils::PrintShaderBanner(programKind); |
| if (gPrintSKSL) { |
| SkDebugf("SKSL:\n"); |
| SkShaderUtils::PrintLineByLine(SkShaderUtils::PrettyPrint(sksl)); |
| } |
| if (gPrintMSL) { |
| SkDebugf("MSL:\n"); |
| SkShaderUtils::PrintLineByLine(SkShaderUtils::PrettyPrint(*msl)); |
| } |
| } |
| |
| *outInputs = program->fInputs; |
| return true; |
| } |
| |
| id<MTLLibrary> GrCompileMtlShaderLibrary(const GrMtlGpu* gpu, |
| const std::string& msl, |
| GrContextOptions::ShaderErrorHandler* errorHandler) { |
| TRACE_EVENT0("skia.shaders", "driver_compile_shader"); |
| auto nsSource = [[NSString alloc] initWithBytesNoCopy:const_cast<char*>(msl.c_str()) |
| length:msl.size() |
| encoding:NSUTF8StringEncoding |
| freeWhenDone:NO]; |
| MTLCompileOptions* options = [[MTLCompileOptions alloc] init]; |
| // array<> is supported in MSL 2.0 on MacOS 10.13+ and iOS 11+, |
| // and in MSL 1.2 on iOS 10+ (but not MacOS). |
| if (@available(macOS 10.13, iOS 11.0, *)) { |
| options.languageVersion = MTLLanguageVersion2_0; |
| #if defined(SK_BUILD_FOR_IOS) |
| } else if (@available(macOS 10.12, iOS 10.0, *)) { |
| options.languageVersion = MTLLanguageVersion1_2; |
| #endif |
| } |
| if (gpu->caps()->shaderCaps()->canUseFastMath()) { |
| options.fastMathEnabled = YES; |
| } |
| |
| NSError* error = nil; |
| #if defined(SK_BUILD_FOR_MAC) |
| id<MTLLibrary> compiledLibrary = GrMtlNewLibraryWithSource(gpu->device(), nsSource, |
| options, &error); |
| #else |
| id<MTLLibrary> compiledLibrary = [gpu->device() newLibraryWithSource:nsSource |
| options:options |
| error:&error]; |
| #endif |
| if (!compiledLibrary) { |
| errorHandler->compileError(msl.c_str(), error.debugDescription.UTF8String); |
| return nil; |
| } |
| |
| return compiledLibrary; |
| } |
| |
| void GrPrecompileMtlShaderLibrary(const GrMtlGpu* gpu, |
| const std::string& msl) { |
| auto nsSource = [[NSString alloc] initWithBytesNoCopy:const_cast<char*>(msl.c_str()) |
| length:msl.size() |
| encoding:NSUTF8StringEncoding |
| freeWhenDone:NO]; |
| // Do nothing after completion for now. |
| // TODO: cache the result somewhere so we can use it later. |
| MTLNewLibraryCompletionHandler completionHandler = |
| ^(id<MTLLibrary> library, NSError* error) {}; |
| [gpu->device() newLibraryWithSource:nsSource |
| options:nil |
| completionHandler:completionHandler]; |
| } |
| |
| // Wrapper to get atomic assignment for compiles and pipeline creation |
| class MtlCompileResult : public SkRefCnt { |
| public: |
| MtlCompileResult() : fCompiledObject(nil), fError(nil) {} |
| void set(id compiledObject, NSError* error) { |
| SkAutoMutexExclusive automutex(fMutex); |
| fCompiledObject = compiledObject; |
| fError = error; |
| } |
| std::pair<id, NSError*> get() { |
| SkAutoMutexExclusive automutex(fMutex); |
| return std::make_pair(fCompiledObject, fError); |
| } |
| private: |
| SkMutex fMutex; |
| id fCompiledObject SK_GUARDED_BY(fMutex); |
| NSError* fError SK_GUARDED_BY(fMutex); |
| }; |
| |
| id<MTLLibrary> GrMtlNewLibraryWithSource(id<MTLDevice> device, NSString* mslCode, |
| MTLCompileOptions* options, NSError** error) { |
| dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); |
| sk_sp<MtlCompileResult> compileResult(new MtlCompileResult); |
| // We have to increment the ref for the Obj-C block manually because it won't do it for us |
| compileResult->ref(); |
| MTLNewLibraryCompletionHandler completionHandler = |
| ^(id<MTLLibrary> library, NSError* compileError) { |
| compileResult->set(library, compileError); |
| dispatch_semaphore_signal(semaphore); |
| compileResult->unref(); |
| }; |
| |
| [device newLibraryWithSource: mslCode |
| options: options |
| completionHandler: completionHandler]; |
| |
| // Wait 1 second for the compiler |
| constexpr auto kTimeoutNS = 1000000000UL; |
| if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, kTimeoutNS))) { |
| if (error) { |
| constexpr auto kTimeoutMS = kTimeoutNS/1000000UL; |
| NSString* description = |
| [NSString stringWithFormat:@"Compilation took longer than %lu ms", |
| kTimeoutMS]; |
| *error = GrCreateMtlError(description, GrMtlErrorCode::kTimeout); |
| } |
| return nil; |
| } |
| |
| id<MTLLibrary> compiledLibrary; |
| std::tie(compiledLibrary, *error) = compileResult->get(); |
| |
| return compiledLibrary; |
| } |
| |
| id<MTLRenderPipelineState> GrMtlNewRenderPipelineStateWithDescriptor( |
| id<MTLDevice> device, MTLRenderPipelineDescriptor* pipelineDescriptor, NSError** error) { |
| dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); |
| sk_sp<MtlCompileResult> compileResult(new MtlCompileResult); |
| // We have to increment the ref for the Obj-C block manually because it won't do it for us |
| compileResult->ref(); |
| MTLNewRenderPipelineStateCompletionHandler completionHandler = |
| ^(id<MTLRenderPipelineState> state, NSError* compileError) { |
| compileResult->set(state, compileError); |
| dispatch_semaphore_signal(semaphore); |
| compileResult->unref(); |
| }; |
| |
| [device newRenderPipelineStateWithDescriptor: pipelineDescriptor |
| completionHandler: completionHandler]; |
| |
| // Wait 1 second for pipeline creation |
| constexpr auto kTimeoutNS = 1000000000UL; |
| if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, kTimeoutNS))) { |
| if (error) { |
| constexpr auto kTimeoutMS = kTimeoutNS/1000000UL; |
| NSString* description = |
| [NSString stringWithFormat:@"Pipeline creation took longer than %lu ms", |
| kTimeoutMS]; |
| *error = GrCreateMtlError(description, GrMtlErrorCode::kTimeout); |
| } |
| return nil; |
| } |
| |
| id<MTLRenderPipelineState> pipelineState; |
| std::tie(pipelineState, *error) = compileResult->get(); |
| |
| return pipelineState; |
| } |
| |
| id<MTLTexture> GrGetMTLTextureFromSurface(GrSurface* surface) { |
| id<MTLTexture> mtlTexture = nil; |
| |
| GrMtlRenderTarget* renderTarget = static_cast<GrMtlRenderTarget*>(surface->asRenderTarget()); |
| GrMtlTexture* texture; |
| if (renderTarget) { |
| // We should not be using this for multisampled rendertargets with a separate resolve |
| // texture. |
| if (renderTarget->resolveAttachment()) { |
| SkASSERT(renderTarget->numSamples() > 1); |
| SkASSERT(false); |
| return nil; |
| } |
| mtlTexture = renderTarget->colorMTLTexture(); |
| } else { |
| texture = static_cast<GrMtlTexture*>(surface->asTexture()); |
| if (texture) { |
| mtlTexture = texture->mtlTexture(); |
| } |
| } |
| return mtlTexture; |
| } |
| |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // CPP Utils |
| |
| GrMTLPixelFormat GrGetMTLPixelFormatFromMtlTextureInfo(const GrMtlTextureInfo& info) { |
| id<MTLTexture> GR_NORETAIN mtlTexture = GrGetMTLTexture(info.fTexture.get()); |
| return static_cast<GrMTLPixelFormat>(mtlTexture.pixelFormat); |
| } |
| |
| uint32_t GrMtlFormatChannels(GrMTLPixelFormat mtlFormat) { |
| switch (mtlFormat) { |
| case MTLPixelFormatRGBA8Unorm: return kRGBA_SkColorChannelFlags; |
| case MTLPixelFormatR8Unorm: return kRed_SkColorChannelFlag; |
| case MTLPixelFormatA8Unorm: return kAlpha_SkColorChannelFlag; |
| case MTLPixelFormatBGRA8Unorm: return kRGBA_SkColorChannelFlags; |
| #if defined(SK_BUILD_FOR_IOS) && !TARGET_OS_SIMULATOR |
| case MTLPixelFormatB5G6R5Unorm: return kRGB_SkColorChannelFlags; |
| #endif |
| case MTLPixelFormatRGBA16Float: return kRGBA_SkColorChannelFlags; |
| case MTLPixelFormatR16Float: return kRed_SkColorChannelFlag; |
| case MTLPixelFormatRG8Unorm: return kRG_SkColorChannelFlags; |
| case MTLPixelFormatRGB10A2Unorm: return kRGBA_SkColorChannelFlags; |
| #ifdef SK_BUILD_FOR_MAC |
| case MTLPixelFormatBGR10A2Unorm: return kRGBA_SkColorChannelFlags; |
| #endif |
| #if defined(SK_BUILD_FOR_IOS) && !TARGET_OS_SIMULATOR |
| case MTLPixelFormatABGR4Unorm: return kRGBA_SkColorChannelFlags; |
| #endif |
| case MTLPixelFormatRGBA8Unorm_sRGB: return kRGBA_SkColorChannelFlags; |
| case MTLPixelFormatR16Unorm: return kRed_SkColorChannelFlag; |
| case MTLPixelFormatRG16Unorm: return kRG_SkColorChannelFlags; |
| #ifdef SK_BUILD_FOR_IOS |
| case MTLPixelFormatETC2_RGB8: return kRGB_SkColorChannelFlags; |
| #else |
| case MTLPixelFormatBC1_RGBA: return kRGBA_SkColorChannelFlags; |
| #endif |
| case MTLPixelFormatRGBA16Unorm: return kRGBA_SkColorChannelFlags; |
| case MTLPixelFormatRG16Float: return kRG_SkColorChannelFlags; |
| case MTLPixelFormatStencil8: return 0; |
| |
| default: return 0; |
| } |
| } |
| |
| GrColorFormatDesc GrMtlFormatDesc(GrMTLPixelFormat mtlFormat) { |
| switch (mtlFormat) { |
| case MTLPixelFormatRGBA8Unorm: |
| return GrColorFormatDesc::MakeRGBA(8, GrColorTypeEncoding::kUnorm); |
| case MTLPixelFormatR8Unorm: |
| return GrColorFormatDesc::MakeR(8, GrColorTypeEncoding::kUnorm); |
| case MTLPixelFormatA8Unorm: |
| return GrColorFormatDesc::MakeAlpha(8, GrColorTypeEncoding::kUnorm); |
| case MTLPixelFormatBGRA8Unorm: |
| return GrColorFormatDesc::MakeRGBA(8, GrColorTypeEncoding::kUnorm); |
| #if defined(SK_BUILD_FOR_IOS) && !TARGET_OS_SIMULATOR |
| case MTLPixelFormatB5G6R5Unorm: |
| return GrColorFormatDesc::MakeRGB(5, 6, 5, GrColorTypeEncoding::kUnorm); |
| #endif |
| case MTLPixelFormatRGBA16Float: |
| return GrColorFormatDesc::MakeRGBA(16, GrColorTypeEncoding::kFloat); |
| case MTLPixelFormatR16Float: |
| return GrColorFormatDesc::MakeR(16, GrColorTypeEncoding::kFloat); |
| case MTLPixelFormatRG8Unorm: |
| return GrColorFormatDesc::MakeRG(8, GrColorTypeEncoding::kUnorm); |
| case MTLPixelFormatRGB10A2Unorm: |
| return GrColorFormatDesc::MakeRGBA(10, 2, GrColorTypeEncoding::kUnorm); |
| #ifdef SK_BUILD_FOR_MAC |
| case MTLPixelFormatBGR10A2Unorm: |
| return GrColorFormatDesc::MakeRGBA(10, 2, GrColorTypeEncoding::kUnorm); |
| #endif |
| #if defined(SK_BUILD_FOR_IOS) && !TARGET_OS_SIMULATOR |
| case MTLPixelFormatABGR4Unorm: |
| return GrColorFormatDesc::MakeRGBA(4, GrColorTypeEncoding::kUnorm); |
| #endif |
| case MTLPixelFormatRGBA8Unorm_sRGB: |
| return GrColorFormatDesc::MakeRGBA(8, GrColorTypeEncoding::kSRGBUnorm); |
| case MTLPixelFormatR16Unorm: |
| return GrColorFormatDesc::MakeR(16, GrColorTypeEncoding::kUnorm); |
| case MTLPixelFormatRG16Unorm: |
| return GrColorFormatDesc::MakeRG(16, GrColorTypeEncoding::kUnorm); |
| case MTLPixelFormatRGBA16Unorm: |
| return GrColorFormatDesc::MakeRGBA(16, GrColorTypeEncoding::kUnorm); |
| case MTLPixelFormatRG16Float: |
| return GrColorFormatDesc::MakeRG(16, GrColorTypeEncoding::kFloat); |
| |
| // Compressed texture formats are not expected to have a description. |
| #ifdef SK_BUILD_FOR_IOS |
| case MTLPixelFormatETC2_RGB8: return GrColorFormatDesc::MakeInvalid(); |
| #else |
| case MTLPixelFormatBC1_RGBA: return GrColorFormatDesc::MakeInvalid(); |
| #endif |
| |
| // This type only describes color channels. |
| case MTLPixelFormatStencil8: return GrColorFormatDesc::MakeInvalid(); |
| |
| default: |
| return GrColorFormatDesc::MakeInvalid(); |
| } |
| } |
| |
| SkImage::CompressionType GrMtlBackendFormatToCompressionType(const GrBackendFormat& format) { |
| MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format); |
| return GrMtlFormatToCompressionType(mtlFormat); |
| } |
| |
| bool GrMtlFormatIsCompressed(MTLPixelFormat mtlFormat) { |
| switch (mtlFormat) { |
| #ifdef SK_BUILD_FOR_IOS |
| case MTLPixelFormatETC2_RGB8: |
| return true; |
| #else |
| case MTLPixelFormatBC1_RGBA: |
| return true; |
| #endif |
| default: |
| return false; |
| } |
| } |
| |
| SkImage::CompressionType GrMtlFormatToCompressionType(MTLPixelFormat mtlFormat) { |
| switch (mtlFormat) { |
| #ifdef SK_BUILD_FOR_IOS |
| case MTLPixelFormatETC2_RGB8: return SkImage::CompressionType::kETC2_RGB8_UNORM; |
| #else |
| case MTLPixelFormatBC1_RGBA: return SkImage::CompressionType::kBC1_RGBA8_UNORM; |
| #endif |
| default: return SkImage::CompressionType::kNone; |
| } |
| |
| SkUNREACHABLE; |
| } |
| |
| int GrMtlTextureInfoSampleCount(const GrMtlTextureInfo& info) { |
| id<MTLTexture> texture = GrGetMTLTexture(info.fTexture.get()); |
| if (!texture) { |
| return 0; |
| } |
| return texture.sampleCount; |
| } |
| |
| size_t GrMtlBackendFormatBytesPerBlock(const GrBackendFormat& format) { |
| MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format); |
| return GrMtlFormatBytesPerBlock(mtlFormat); |
| } |
| |
| size_t GrMtlFormatBytesPerBlock(MTLPixelFormat mtlFormat) { |
| switch (mtlFormat) { |
| case MTLPixelFormatInvalid: return 0; |
| case MTLPixelFormatRGBA8Unorm: return 4; |
| case MTLPixelFormatR8Unorm: return 1; |
| case MTLPixelFormatA8Unorm: return 1; |
| case MTLPixelFormatBGRA8Unorm: return 4; |
| #ifdef SK_BUILD_FOR_IOS |
| case MTLPixelFormatB5G6R5Unorm: return 2; |
| #endif |
| case MTLPixelFormatRGBA16Float: return 8; |
| case MTLPixelFormatR16Float: return 2; |
| case MTLPixelFormatRG8Unorm: return 2; |
| case MTLPixelFormatRGB10A2Unorm: return 4; |
| #ifdef SK_BUILD_FOR_MAC |
| case MTLPixelFormatBGR10A2Unorm: return 4; |
| #endif |
| #ifdef SK_BUILD_FOR_IOS |
| case MTLPixelFormatABGR4Unorm: return 2; |
| #endif |
| case MTLPixelFormatRGBA8Unorm_sRGB: return 4; |
| case MTLPixelFormatR16Unorm: return 2; |
| case MTLPixelFormatRG16Unorm: return 4; |
| #ifdef SK_BUILD_FOR_IOS |
| case MTLPixelFormatETC2_RGB8: return 8; |
| #else |
| case MTLPixelFormatBC1_RGBA: return 8; |
| #endif |
| case MTLPixelFormatRGBA16Unorm: return 8; |
| case MTLPixelFormatRG16Float: return 4; |
| case MTLPixelFormatStencil8: return 1; |
| |
| default: return 0; |
| } |
| } |
| |
| int GrMtlBackendFormatStencilBits(const GrBackendFormat& format) { |
| MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format); |
| return GrMtlFormatStencilBits(mtlFormat); |
| } |
| |
| int GrMtlFormatStencilBits(MTLPixelFormat mtlFormat) { |
| switch(mtlFormat) { |
| case MTLPixelFormatStencil8: |
| return 8; |
| default: |
| return 0; |
| } |
| } |
| |
| #ifdef SK_BUILD_FOR_IOS |
| bool GrMtlIsAppInBackground() { |
| return [NSThread isMainThread] && |
| ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground); |
| } |
| #endif |
| |
| #if defined(SK_DEBUG) || GR_TEST_UTILS |
| bool GrMtlFormatIsBGRA8(GrMTLPixelFormat mtlFormat) { |
| return mtlFormat == MTLPixelFormatBGRA8Unorm; |
| } |
| |
| const char* GrMtlFormatToStr(GrMTLPixelFormat mtlFormat) { |
| switch (mtlFormat) { |
| case MTLPixelFormatInvalid: return "Invalid"; |
| case MTLPixelFormatRGBA8Unorm: return "RGBA8Unorm"; |
| case MTLPixelFormatR8Unorm: return "R8Unorm"; |
| case MTLPixelFormatA8Unorm: return "A8Unorm"; |
| case MTLPixelFormatBGRA8Unorm: return "BGRA8Unorm"; |
| #ifdef SK_BUILD_FOR_IOS |
| case MTLPixelFormatB5G6R5Unorm: return "B5G6R5Unorm"; |
| #endif |
| case MTLPixelFormatRGBA16Float: return "RGBA16Float"; |
| case MTLPixelFormatR16Float: return "R16Float"; |
| case MTLPixelFormatRG8Unorm: return "RG8Unorm"; |
| case MTLPixelFormatRGB10A2Unorm: return "RGB10A2Unorm"; |
| #ifdef SK_BUILD_FOR_MAC |
| case MTLPixelFormatBGR10A2Unorm: return "BGR10A2Unorm"; |
| #endif |
| #ifdef SK_BUILD_FOR_IOS |
| case MTLPixelFormatABGR4Unorm: return "ABGR4Unorm"; |
| #endif |
| case MTLPixelFormatRGBA8Unorm_sRGB: return "RGBA8Unorm_sRGB"; |
| case MTLPixelFormatR16Unorm: return "R16Unorm"; |
| case MTLPixelFormatRG16Unorm: return "RG16Unorm"; |
| #ifdef SK_BUILD_FOR_IOS |
| case MTLPixelFormatETC2_RGB8: return "ETC2_RGB8"; |
| #else |
| case MTLPixelFormatBC1_RGBA: return "BC1_RGBA"; |
| #endif |
| case MTLPixelFormatRGBA16Unorm: return "RGBA16Unorm"; |
| case MTLPixelFormatRG16Float: return "RG16Float"; |
| case MTLPixelFormatStencil8: return "Stencil8"; |
| |
| default: return "Unknown"; |
| } |
| } |
| |
| #endif |
| |
| GR_NORETAIN_END |