/* * Copyright 2018 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/GrMtlPipelineStateBuilder.h" #include "include/gpu/GrDirectContext.h" #include "src/core/SkReadBuffer.h" #include "src/core/SkTraceEvent.h" #include "src/core/SkWriteBuffer.h" #include "src/gpu/GrAutoLocaleSetter.h" #include "src/gpu/GrDirectContextPriv.h" #include "src/gpu/GrPersistentCacheUtils.h" #include "src/gpu/GrRenderTarget.h" #include "src/gpu/GrShaderUtils.h" #include "src/gpu/mtl/GrMtlGpu.h" #include "src/gpu/mtl/GrMtlPipelineState.h" #include "src/gpu/mtl/GrMtlUtil.h" #import #if !__has_feature(objc_arc) #error This file must be compiled with Arc. Use -fobjc-arc flag #endif GR_NORETAIN_BEGIN GrMtlPipelineState* GrMtlPipelineStateBuilder::CreatePipelineState( GrMtlGpu* gpu, const GrProgramDesc& desc, const GrProgramInfo& programInfo, const GrMtlPrecompiledLibraries* precompiledLibs) { GrAutoLocaleSetter als("C"); GrMtlPipelineStateBuilder builder(gpu, desc, programInfo); if (!builder.emitAndInstallProcs()) { return nullptr; } return builder.finalize(desc, programInfo, precompiledLibs); } GrMtlPipelineStateBuilder::GrMtlPipelineStateBuilder(GrMtlGpu* gpu, const GrProgramDesc& desc, const GrProgramInfo& programInfo) : INHERITED(desc, programInfo) , fGpu(gpu) , fUniformHandler(this) , fVaryingHandler(this) { } const GrCaps* GrMtlPipelineStateBuilder::caps() const { return fGpu->caps(); } SkSL::Compiler* GrMtlPipelineStateBuilder::shaderCompiler() const { return fGpu->shaderCompiler(); } void GrMtlPipelineStateBuilder::finalizeFragmentOutputColor(GrShaderVar& outputColor) { outputColor.addLayoutQualifier("location = 0, index = 0"); } void GrMtlPipelineStateBuilder::finalizeFragmentSecondaryColor(GrShaderVar& outputColor) { outputColor.addLayoutQualifier("location = 0, index = 1"); } static constexpr SkFourByteTag kMSL_Tag = SkSetFourByteTag('M', 'S', 'L', ' '); static constexpr SkFourByteTag kSKSL_Tag = SkSetFourByteTag('S', 'K', 'S', 'L'); void GrMtlPipelineStateBuilder::storeShadersInCache(const SkSL::String shaders[], const SkSL::Program::Inputs inputs[], SkSL::Program::Settings* settings, sk_sp pipelineData, bool isSkSL) { sk_sp key = SkData::MakeWithoutCopy(this->desc().asKey(), this->desc().keyLength()); SkString description = GrProgramDesc::Describe(fProgramInfo, *this->caps()); // cache metadata to allow for a complete precompile in either case GrPersistentCacheUtils::ShaderMetadata meta; meta.fSettings = settings; meta.fPlatformData = std::move(pipelineData); SkFourByteTag tag = isSkSL ? kSKSL_Tag : kMSL_Tag; sk_sp data = GrPersistentCacheUtils::PackCachedShaders(tag, shaders, inputs, kGrShaderTypeCount, &meta); fGpu->getContext()->priv().getPersistentCache()->store(*key, *data, description); } id GrMtlPipelineStateBuilder::compileMtlShaderLibrary( const SkSL::String& shader, SkSL::Program::Inputs inputs, GrContextOptions::ShaderErrorHandler* errorHandler) { id shaderLibrary = GrCompileMtlShaderLibrary(fGpu, shader, errorHandler); if (shaderLibrary != nil && inputs.fUseFlipRTUniform) { this->addRTFlipUniform(SKSL_RTFLIP_NAME); } return shaderLibrary; } static inline MTLVertexFormat attribute_type_to_mtlformat(GrVertexAttribType type) { switch (type) { case kFloat_GrVertexAttribType: return MTLVertexFormatFloat; case kFloat2_GrVertexAttribType: return MTLVertexFormatFloat2; case kFloat3_GrVertexAttribType: return MTLVertexFormatFloat3; case kFloat4_GrVertexAttribType: return MTLVertexFormatFloat4; case kHalf_GrVertexAttribType: if (@available(macOS 10.13, iOS 11.0, *)) { return MTLVertexFormatHalf; } else { return MTLVertexFormatInvalid; } case kHalf2_GrVertexAttribType: return MTLVertexFormatHalf2; case kHalf4_GrVertexAttribType: return MTLVertexFormatHalf4; case kInt2_GrVertexAttribType: return MTLVertexFormatInt2; case kInt3_GrVertexAttribType: return MTLVertexFormatInt3; case kInt4_GrVertexAttribType: return MTLVertexFormatInt4; case kByte_GrVertexAttribType: if (@available(macOS 10.13, iOS 11.0, *)) { return MTLVertexFormatChar; } else { return MTLVertexFormatInvalid; } case kByte2_GrVertexAttribType: return MTLVertexFormatChar2; case kByte4_GrVertexAttribType: return MTLVertexFormatChar4; case kUByte_GrVertexAttribType: if (@available(macOS 10.13, iOS 11.0, *)) { return MTLVertexFormatUChar; } else { return MTLVertexFormatInvalid; } case kUByte2_GrVertexAttribType: return MTLVertexFormatUChar2; case kUByte4_GrVertexAttribType: return MTLVertexFormatUChar4; case kUByte_norm_GrVertexAttribType: if (@available(macOS 10.13, iOS 11.0, *)) { return MTLVertexFormatUCharNormalized; } else { return MTLVertexFormatInvalid; } case kUByte4_norm_GrVertexAttribType: return MTLVertexFormatUChar4Normalized; case kShort2_GrVertexAttribType: return MTLVertexFormatShort2; case kShort4_GrVertexAttribType: return MTLVertexFormatShort4; case kUShort2_GrVertexAttribType: return MTLVertexFormatUShort2; case kUShort2_norm_GrVertexAttribType: return MTLVertexFormatUShort2Normalized; case kInt_GrVertexAttribType: return MTLVertexFormatInt; case kUInt_GrVertexAttribType: return MTLVertexFormatUInt; case kUShort_norm_GrVertexAttribType: if (@available(macOS 10.13, iOS 11.0, *)) { return MTLVertexFormatUShortNormalized; } else { return MTLVertexFormatInvalid; } case kUShort4_norm_GrVertexAttribType: return MTLVertexFormatUShort4Normalized; } SK_ABORT("Unknown vertex attribute type"); } static MTLVertexDescriptor* create_vertex_descriptor(const GrGeometryProcessor& geomProc, SkBinaryWriteBuffer* writer) { uint32_t vertexBinding = 0, instanceBinding = 0; int nextBinding = GrMtlUniformHandler::kLastUniformBinding + 1; if (geomProc.hasVertexAttributes()) { vertexBinding = nextBinding++; } if (geomProc.hasInstanceAttributes()) { instanceBinding = nextBinding; } if (writer) { writer->writeUInt(vertexBinding); writer->writeUInt(instanceBinding); } auto vertexDescriptor = [[MTLVertexDescriptor alloc] init]; int attributeIndex = 0; int vertexAttributeCount = geomProc.numVertexAttributes(); if (writer) { writer->writeInt(vertexAttributeCount); } size_t vertexAttributeOffset = 0; for (const auto& attribute : geomProc.vertexAttributes()) { MTLVertexAttributeDescriptor* mtlAttribute = vertexDescriptor.attributes[attributeIndex]; MTLVertexFormat format = attribute_type_to_mtlformat(attribute.cpuType()); SkASSERT(MTLVertexFormatInvalid != format); mtlAttribute.format = format; mtlAttribute.offset = vertexAttributeOffset; mtlAttribute.bufferIndex = vertexBinding; if (writer) { writer->writeInt(format); writer->writeUInt(vertexAttributeOffset); writer->writeUInt(vertexBinding); } vertexAttributeOffset += attribute.sizeAlign4(); attributeIndex++; } SkASSERT(vertexAttributeOffset == geomProc.vertexStride()); if (vertexAttributeCount) { MTLVertexBufferLayoutDescriptor* vertexBufferLayout = vertexDescriptor.layouts[vertexBinding]; vertexBufferLayout.stepFunction = MTLVertexStepFunctionPerVertex; vertexBufferLayout.stepRate = 1; vertexBufferLayout.stride = vertexAttributeOffset; if (writer) { writer->writeUInt(vertexAttributeOffset); } } int instanceAttributeCount = geomProc.numInstanceAttributes(); if (writer) { writer->writeInt(instanceAttributeCount); } size_t instanceAttributeOffset = 0; for (const auto& attribute : geomProc.instanceAttributes()) { MTLVertexAttributeDescriptor* mtlAttribute = vertexDescriptor.attributes[attributeIndex]; MTLVertexFormat format = attribute_type_to_mtlformat(attribute.cpuType()); SkASSERT(MTLVertexFormatInvalid != format); mtlAttribute.format = format; mtlAttribute.offset = instanceAttributeOffset; mtlAttribute.bufferIndex = instanceBinding; if (writer) { writer->writeInt(format); writer->writeUInt(instanceAttributeOffset); writer->writeUInt(instanceBinding); } instanceAttributeOffset += attribute.sizeAlign4(); attributeIndex++; } SkASSERT(instanceAttributeOffset == geomProc.instanceStride()); if (instanceAttributeCount) { MTLVertexBufferLayoutDescriptor* instanceBufferLayout = vertexDescriptor.layouts[instanceBinding]; instanceBufferLayout.stepFunction = MTLVertexStepFunctionPerInstance; instanceBufferLayout.stepRate = 1; instanceBufferLayout.stride = instanceAttributeOffset; if (writer) { writer->writeUInt(instanceAttributeOffset); } } return vertexDescriptor; } static MTLBlendFactor blend_coeff_to_mtl_blend(GrBlendCoeff coeff) { switch (coeff) { case kZero_GrBlendCoeff: return MTLBlendFactorZero; case kOne_GrBlendCoeff: return MTLBlendFactorOne; case kSC_GrBlendCoeff: return MTLBlendFactorSourceColor; case kISC_GrBlendCoeff: return MTLBlendFactorOneMinusSourceColor; case kDC_GrBlendCoeff: return MTLBlendFactorDestinationColor; case kIDC_GrBlendCoeff: return MTLBlendFactorOneMinusDestinationColor; case kSA_GrBlendCoeff: return MTLBlendFactorSourceAlpha; case kISA_GrBlendCoeff: return MTLBlendFactorOneMinusSourceAlpha; case kDA_GrBlendCoeff: return MTLBlendFactorDestinationAlpha; case kIDA_GrBlendCoeff: return MTLBlendFactorOneMinusDestinationAlpha; case kConstC_GrBlendCoeff: return MTLBlendFactorBlendColor; case kIConstC_GrBlendCoeff: return MTLBlendFactorOneMinusBlendColor; case kS2C_GrBlendCoeff: if (@available(macOS 10.12, iOS 11.0, *)) { return MTLBlendFactorSource1Color; } else { return MTLBlendFactorZero; } case kIS2C_GrBlendCoeff: if (@available(macOS 10.12, iOS 11.0, *)) { return MTLBlendFactorOneMinusSource1Color; } else { return MTLBlendFactorZero; } case kS2A_GrBlendCoeff: if (@available(macOS 10.12, iOS 11.0, *)) { return MTLBlendFactorSource1Alpha; } else { return MTLBlendFactorZero; } case kIS2A_GrBlendCoeff: if (@available(macOS 10.12, iOS 11.0, *)) { return MTLBlendFactorOneMinusSource1Alpha; } else { return MTLBlendFactorZero; } case kIllegal_GrBlendCoeff: return MTLBlendFactorZero; } SK_ABORT("Unknown blend coefficient"); } static MTLBlendOperation blend_equation_to_mtl_blend_op(GrBlendEquation equation) { static const MTLBlendOperation gTable[] = { MTLBlendOperationAdd, // kAdd_GrBlendEquation MTLBlendOperationSubtract, // kSubtract_GrBlendEquation MTLBlendOperationReverseSubtract, // kReverseSubtract_GrBlendEquation }; static_assert(SK_ARRAY_COUNT(gTable) == kFirstAdvancedGrBlendEquation); static_assert(0 == kAdd_GrBlendEquation); static_assert(1 == kSubtract_GrBlendEquation); static_assert(2 == kReverseSubtract_GrBlendEquation); SkASSERT((unsigned)equation < kGrBlendEquationCnt); return gTable[equation]; } static MTLRenderPipelineColorAttachmentDescriptor* create_color_attachment( MTLPixelFormat format, const GrPipeline& pipeline, SkBinaryWriteBuffer* writer) { auto mtlColorAttachment = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init]; // pixel format mtlColorAttachment.pixelFormat = format; if (writer) { writer->writeInt(format); } // blending const GrXferProcessor::BlendInfo& blendInfo = pipeline.getXferProcessor().getBlendInfo(); GrBlendEquation equation = blendInfo.fEquation; GrBlendCoeff srcCoeff = blendInfo.fSrcBlend; GrBlendCoeff dstCoeff = blendInfo.fDstBlend; bool blendOn = !GrBlendShouldDisable(equation, srcCoeff, dstCoeff); mtlColorAttachment.blendingEnabled = blendOn; if (writer) { writer->writeBool(blendOn); } if (blendOn) { mtlColorAttachment.sourceRGBBlendFactor = blend_coeff_to_mtl_blend(srcCoeff); mtlColorAttachment.destinationRGBBlendFactor = blend_coeff_to_mtl_blend(dstCoeff); mtlColorAttachment.rgbBlendOperation = blend_equation_to_mtl_blend_op(equation); mtlColorAttachment.sourceAlphaBlendFactor = blend_coeff_to_mtl_blend(srcCoeff); mtlColorAttachment.destinationAlphaBlendFactor = blend_coeff_to_mtl_blend(dstCoeff); mtlColorAttachment.alphaBlendOperation = blend_equation_to_mtl_blend_op(equation); if (writer) { writer->writeInt(mtlColorAttachment.sourceRGBBlendFactor); writer->writeInt(mtlColorAttachment.destinationRGBBlendFactor); writer->writeInt(mtlColorAttachment.rgbBlendOperation); writer->writeInt(mtlColorAttachment.sourceAlphaBlendFactor); writer->writeInt(mtlColorAttachment.destinationAlphaBlendFactor); writer->writeInt(mtlColorAttachment.alphaBlendOperation); } } if (blendInfo.fWriteColor) { mtlColorAttachment.writeMask = MTLColorWriteMaskAll; } else { mtlColorAttachment.writeMask = MTLColorWriteMaskNone; } if (writer) { writer->writeBool(blendInfo.fWriteColor); } return mtlColorAttachment; } static uint32_t buffer_size(uint32_t offset, uint32_t maxAlignment) { // Metal expects the buffer to be padded at the end according to the alignment // of the largest element in the buffer. uint32_t offsetDiff = offset & maxAlignment; if (offsetDiff != 0) { offsetDiff = maxAlignment - offsetDiff + 1; } return offset + offsetDiff; } static MTLRenderPipelineDescriptor* read_pipeline_data(SkReadBuffer* reader) { auto pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; #ifdef SK_ENABLE_MTL_DEBUG_INFO // set label { SkString description; reader->readString(&description); pipelineDescriptor.label = @(description.c_str()); } #endif // set up vertex descriptor { auto vertexDescriptor = [[MTLVertexDescriptor alloc] init]; uint32_t vertexBinding = reader->readUInt(); uint32_t instanceBinding = reader->readUInt(); int attributeIndex = 0; // vertex attributes int vertexAttributeCount = reader->readInt(); for (int i = 0; i < vertexAttributeCount; ++i) { MTLVertexAttributeDescriptor* mtlAttribute = vertexDescriptor.attributes[attributeIndex]; mtlAttribute.format = (MTLVertexFormat) reader->readInt(); mtlAttribute.offset = reader->readUInt(); mtlAttribute.bufferIndex = reader->readUInt(); ++attributeIndex; } if (vertexAttributeCount) { MTLVertexBufferLayoutDescriptor* vertexBufferLayout = vertexDescriptor.layouts[vertexBinding]; vertexBufferLayout.stepFunction = MTLVertexStepFunctionPerVertex; vertexBufferLayout.stepRate = 1; vertexBufferLayout.stride = reader->readUInt(); } // instance attributes int instanceAttributeCount = reader->readInt(); for (int i = 0; i < instanceAttributeCount; ++i) { MTLVertexAttributeDescriptor* mtlAttribute = vertexDescriptor.attributes[attributeIndex]; mtlAttribute.format = (MTLVertexFormat) reader->readInt(); mtlAttribute.offset = reader->readUInt(); mtlAttribute.bufferIndex = reader->readUInt(); ++attributeIndex; } if (instanceAttributeCount) { MTLVertexBufferLayoutDescriptor* instanceBufferLayout = vertexDescriptor.layouts[instanceBinding]; instanceBufferLayout.stepFunction = MTLVertexStepFunctionPerInstance; instanceBufferLayout.stepRate = 1; instanceBufferLayout.stride = reader->readUInt(); } pipelineDescriptor.vertexDescriptor = vertexDescriptor; } // set up color attachments { auto mtlColorAttachment = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init]; mtlColorAttachment.pixelFormat = (MTLPixelFormat) reader->readInt(); mtlColorAttachment.blendingEnabled = reader->readBool(); if (mtlColorAttachment.blendingEnabled) { mtlColorAttachment.sourceRGBBlendFactor = (MTLBlendFactor) reader->readInt(); mtlColorAttachment.destinationRGBBlendFactor = (MTLBlendFactor) reader->readInt(); mtlColorAttachment.rgbBlendOperation = (MTLBlendOperation) reader->readInt(); mtlColorAttachment.sourceAlphaBlendFactor = (MTLBlendFactor) reader->readInt(); mtlColorAttachment.destinationAlphaBlendFactor = (MTLBlendFactor) reader->readInt(); mtlColorAttachment.alphaBlendOperation = (MTLBlendOperation) reader->readInt(); } if (reader->readBool()) { mtlColorAttachment.writeMask = MTLColorWriteMaskAll; } else { mtlColorAttachment.writeMask = MTLColorWriteMaskNone; } pipelineDescriptor.colorAttachments[0] = mtlColorAttachment; } pipelineDescriptor.stencilAttachmentPixelFormat = (MTLPixelFormat) reader->readInt(); return pipelineDescriptor; } GrMtlPipelineState* GrMtlPipelineStateBuilder::finalize( const GrProgramDesc& desc, const GrProgramInfo& programInfo, const GrMtlPrecompiledLibraries* precompiledLibs) { TRACE_EVENT0("skia.shaders", TRACE_FUNC); // Set up for cache if needed std::unique_ptr writer; sk_sp cached; auto persistentCache = fGpu->getContext()->priv().getPersistentCache(); if (persistentCache && !precompiledLibs) { sk_sp key = SkData::MakeWithoutCopy(desc.asKey(), desc.keyLength()); cached = persistentCache->load(*key); } if (persistentCache && !cached) { writer.reset(new SkBinaryWriteBuffer()); } // Ordering in how we set these matters. If it changes adjust read_pipeline_data, above. auto pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; #ifdef SK_ENABLE_MTL_DEBUG_INFO SkString description = GrProgramDesc::Describe(programInfo, *fGpu->caps()); int split = description.find("\n"); description.resize(split); pipelineDescriptor.label = @(description.c_str()); if (writer) { writer->writeString(description.c_str()); } #endif pipelineDescriptor.vertexDescriptor = create_vertex_descriptor(programInfo.geomProc(), writer.get()); MTLPixelFormat pixelFormat = GrBackendFormatAsMTLPixelFormat(programInfo.backendFormat()); if (pixelFormat == MTLPixelFormatInvalid) { return nullptr; } pipelineDescriptor.colorAttachments[0] = create_color_attachment(pixelFormat, programInfo.pipeline(), writer.get()); pipelineDescriptor.sampleCount = programInfo.numSamples(); GrMtlCaps* mtlCaps = (GrMtlCaps*)this->caps(); pipelineDescriptor.stencilAttachmentPixelFormat = mtlCaps->getStencilPixelFormat(desc); if (writer) { writer->writeInt(pipelineDescriptor.stencilAttachmentPixelFormat); } SkASSERT(pipelineDescriptor.vertexDescriptor); SkASSERT(pipelineDescriptor.colorAttachments[0]); if (precompiledLibs) { SkASSERT(precompiledLibs->fVertexLibrary); SkASSERT(precompiledLibs->fFragmentLibrary); pipelineDescriptor.vertexFunction = [precompiledLibs->fVertexLibrary newFunctionWithName: @"vertexMain"]; pipelineDescriptor.fragmentFunction = [precompiledLibs->fFragmentLibrary newFunctionWithName: @"fragmentMain"]; SkASSERT(pipelineDescriptor.vertexFunction); SkASSERT(pipelineDescriptor.fragmentFunction); if (precompiledLibs->fRTFlip) { this->addRTFlipUniform(SKSL_RTFLIP_NAME); } } else { id shaderLibraries[kGrShaderTypeCount]; this->finalizeShaders(); SkSL::Program::Settings settings; settings.fSharpenTextures = fGpu->getContext()->priv().options().fSharpenMipmappedTextures; SkASSERT(!this->fragColorIsInOut()); SkReadBuffer reader; SkFourByteTag shaderType = 0; if (persistentCache && cached) { reader.setMemory(cached->data(), cached->size()); shaderType = GrPersistentCacheUtils::GetType(&reader); } auto errorHandler = fGpu->getContext()->priv().getShaderErrorHandler(); SkSL::String msl[kGrShaderTypeCount]; SkSL::Program::Inputs inputs[kGrShaderTypeCount]; // Unpack any stored shaders from the persistent cache if (cached) { switch (shaderType) { case kMSL_Tag: { GrPersistentCacheUtils::UnpackCachedShaders(&reader, msl, inputs, kGrShaderTypeCount); break; } case kSKSL_Tag: { SkSL::String cached_sksl[kGrShaderTypeCount]; if (GrPersistentCacheUtils::UnpackCachedShaders(&reader, cached_sksl, inputs, kGrShaderTypeCount)) { bool success = GrSkSLToMSL(fGpu, cached_sksl[kVertex_GrShaderType], SkSL::ProgramKind::kVertex, settings, &msl[kVertex_GrShaderType], &inputs[kVertex_GrShaderType], errorHandler); success = success && GrSkSLToMSL(fGpu, cached_sksl[kFragment_GrShaderType], SkSL::ProgramKind::kFragment, settings, &msl[kFragment_GrShaderType], &inputs[kFragment_GrShaderType], errorHandler); if (!success) { return nullptr; } } break; } default: { break; } } } // Create any MSL shaders from pipeline data if necessary and cache if (msl[kVertex_GrShaderType].empty() || msl[kFragment_GrShaderType].empty()) { bool success = true; if (msl[kVertex_GrShaderType].empty()) { success = GrSkSLToMSL(fGpu, fVS.fCompilerString, SkSL::ProgramKind::kVertex, settings, &msl[kVertex_GrShaderType], &inputs[kVertex_GrShaderType], errorHandler); } if (success && msl[kFragment_GrShaderType].empty()) { success = GrSkSLToMSL(fGpu, fFS.fCompilerString, SkSL::ProgramKind::kFragment, settings, &msl[kFragment_GrShaderType], &inputs[kFragment_GrShaderType], errorHandler); } if (!success) { return nullptr; } if (persistentCache && !cached) { sk_sp pipelineData = writer->snapshotAsData(); if (fGpu->getContext()->priv().options().fShaderCacheStrategy == GrContextOptions::ShaderCacheStrategy::kSkSL) { SkSL::String sksl[kGrShaderTypeCount]; sksl[kVertex_GrShaderType] = GrShaderUtils::PrettyPrint(fVS.fCompilerString); sksl[kFragment_GrShaderType] = GrShaderUtils::PrettyPrint(fFS.fCompilerString); this->storeShadersInCache(sksl, inputs, &settings, std::move(pipelineData), true); } else { /*** dump pipeline data here */ this->storeShadersInCache(msl, inputs, nullptr, std::move(pipelineData), false); } } } // Compile MSL to libraries shaderLibraries[kVertex_GrShaderType] = this->compileMtlShaderLibrary( msl[kVertex_GrShaderType], inputs[kVertex_GrShaderType], errorHandler); shaderLibraries[kFragment_GrShaderType] = this->compileMtlShaderLibrary( msl[kFragment_GrShaderType], inputs[kFragment_GrShaderType], errorHandler); if (!shaderLibraries[kVertex_GrShaderType] || !shaderLibraries[kFragment_GrShaderType]) { return nullptr; } pipelineDescriptor.vertexFunction = [shaderLibraries[kVertex_GrShaderType] newFunctionWithName: @"vertexMain"]; pipelineDescriptor.fragmentFunction = [shaderLibraries[kFragment_GrShaderType] newFunctionWithName: @"fragmentMain"]; } if (pipelineDescriptor.vertexFunction == nil) { SkDebugf("Couldn't find vertexMain() in library\n"); return nullptr; } if (pipelineDescriptor.fragmentFunction == nil) { SkDebugf("Couldn't find fragmentMain() in library\n"); return nullptr; } SkASSERT(pipelineDescriptor.vertexFunction); SkASSERT(pipelineDescriptor.fragmentFunction); NSError* error = nil; #if GR_METAL_SDK_VERSION >= 230 if (@available(macOS 11.0, iOS 14.0, *)) { id archive = fGpu->binaryArchive(); if (archive) { NSArray* archiveArray = [NSArray arrayWithObjects:archive, nil]; pipelineDescriptor.binaryArchives = archiveArray; BOOL result; { TRACE_EVENT0("skia.shaders", "addRenderPipelineFunctionsWithDescriptor"); result = [archive addRenderPipelineFunctionsWithDescriptor: pipelineDescriptor error: &error]; } if (!result && error) { SkDebugf("Error storing pipeline: %s\n", [[error localizedDescription] cStringUsingEncoding: NSASCIIStringEncoding]); } } } #endif id pipelineState; { TRACE_EVENT0("skia.shaders", "newRenderPipelineStateWithDescriptor"); #if defined(SK_BUILD_FOR_MAC) pipelineState = GrMtlNewRenderPipelineStateWithDescriptor( fGpu->device(), pipelineDescriptor, &error); #else pipelineState = [fGpu->device() newRenderPipelineStateWithDescriptor: pipelineDescriptor error: &error]; #endif } if (error) { SkDebugf("Error creating pipeline: %s\n", [[error localizedDescription] cStringUsingEncoding: NSASCIIStringEncoding]); return nullptr; } if (!pipelineState) { return nullptr; } sk_sp renderPipeline = GrMtlRenderPipeline::Make(pipelineState); uint32_t bufferSize = buffer_size(fUniformHandler.fCurrentUBOOffset, fUniformHandler.fCurrentUBOMaxAlignment); return new GrMtlPipelineState(fGpu, std::move(renderPipeline), pipelineDescriptor.colorAttachments[0].pixelFormat, fUniformHandles, fUniformHandler.fUniforms, bufferSize, (uint32_t)fUniformHandler.numSamplers(), std::move(fGPImpl), std::move(fXPImpl), std::move(fFPImpls)); } ////////////////////////////////////////////////////////////////////////////// bool GrMtlPipelineStateBuilder::PrecompileShaders(GrMtlGpu* gpu, const SkData& cachedData, GrMtlPrecompiledLibraries* precompiledLibs) { SkASSERT(precompiledLibs); SkReadBuffer reader(cachedData.data(), cachedData.size()); SkFourByteTag shaderType = GrPersistentCacheUtils::GetType(&reader); auto errorHandler = gpu->getContext()->priv().getShaderErrorHandler(); SkSL::Program::Settings settings; settings.fSharpenTextures = gpu->getContext()->priv().options().fSharpenMipmappedTextures; GrPersistentCacheUtils::ShaderMetadata meta; meta.fSettings = &settings; SkSL::String shaders[kGrShaderTypeCount]; SkSL::Program::Inputs inputs[kGrShaderTypeCount]; if (!GrPersistentCacheUtils::UnpackCachedShaders(&reader, shaders, inputs, kGrShaderTypeCount, &meta)) { return false; } // skip the size reader.readUInt(); auto pipelineDescriptor = read_pipeline_data(&reader); if (!reader.isValid()) { return false; } switch (shaderType) { case kMSL_Tag: { precompiledLibs->fVertexLibrary = GrCompileMtlShaderLibrary(gpu, shaders[kVertex_GrShaderType], errorHandler); precompiledLibs->fFragmentLibrary = GrCompileMtlShaderLibrary(gpu, shaders[kFragment_GrShaderType], errorHandler); break; } case kSKSL_Tag: { SkSL::String msl[kGrShaderTypeCount]; if (!GrSkSLToMSL(gpu, shaders[kVertex_GrShaderType], SkSL::ProgramKind::kVertex, settings, &msl[kVertex_GrShaderType], &inputs[kVertex_GrShaderType], errorHandler)) { return false; } if (!GrSkSLToMSL(gpu, shaders[kFragment_GrShaderType], SkSL::ProgramKind::kFragment, settings, &msl[kFragment_GrShaderType], &inputs[kFragment_GrShaderType], errorHandler)) { return false; } precompiledLibs->fVertexLibrary = GrCompileMtlShaderLibrary(gpu, msl[kVertex_GrShaderType], errorHandler); precompiledLibs->fFragmentLibrary = GrCompileMtlShaderLibrary(gpu, msl[kFragment_GrShaderType], errorHandler); break; } default: { return false; } } pipelineDescriptor.vertexFunction = [precompiledLibs->fVertexLibrary newFunctionWithName: @"vertexMain"]; pipelineDescriptor.fragmentFunction = [precompiledLibs->fFragmentLibrary newFunctionWithName: @"fragmentMain"]; #if GR_METAL_SDK_VERSION >= 230 if (@available(macOS 11.0, iOS 14.0, *)) { id archive = gpu->binaryArchive(); if (archive) { NSArray* archiveArray = [NSArray arrayWithObjects:archive, nil]; pipelineDescriptor.binaryArchives = archiveArray; BOOL result; NSError* error = nil; { TRACE_EVENT0("skia.shaders", "addRenderPipelineFunctionsWithDescriptor"); result = [archive addRenderPipelineFunctionsWithDescriptor: pipelineDescriptor error: &error]; } if (!result && error) { SkDebugf("Error storing pipeline: %s\n", [[error localizedDescription] cStringUsingEncoding: NSASCIIStringEncoding]); } } } #endif { TRACE_EVENT0("skia.shaders", "newRenderPipelineStateWithDescriptor"); MTLNewRenderPipelineStateCompletionHandler completionHandler = ^(id state, NSError* error) { if (error) { SkDebugf("Error creating pipeline: %s\n", [[error localizedDescription] cStringUsingEncoding: NSASCIIStringEncoding]); } }; // kick off asynchronous pipeline build and depend on Apple's cache to manage it [gpu->device() newRenderPipelineStateWithDescriptor: pipelineDescriptor completionHandler: completionHandler]; } precompiledLibs->fRTFlip = inputs[kFragment_GrShaderType].fUseFlipRTUniform; return true; } GR_NORETAIN_END