| /* |
| * 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 <simd/simd.h> |
| |
| #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<SkData> pipelineData, |
| bool isSkSL) { |
| sk_sp<SkData> 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<SkData> data = GrPersistentCacheUtils::PackCachedShaders(tag, shaders, inputs, |
| kGrShaderTypeCount, &meta); |
| fGpu->getContext()->priv().getPersistentCache()->store(*key, *data, description); |
| } |
| |
| id<MTLLibrary> GrMtlPipelineStateBuilder::compileMtlShaderLibrary( |
| const SkSL::String& shader, SkSL::Program::Inputs inputs, |
| GrContextOptions::ShaderErrorHandler* errorHandler) { |
| id<MTLLibrary> shaderLibrary = GrCompileMtlShaderLibrary(fGpu, shader, errorHandler); |
| if (shaderLibrary != nil && inputs.fRTHeight) { |
| this->addRTHeightUniform(SKSL_RTHEIGHT_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]; |
| |
| // 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); |
| |
| // Geometry shaders are not supported |
| SkASSERT(!this->geometryProcessor().willUseGeoShader()); |
| |
| if (precompiledLibs) { |
| SkASSERT(precompiledLibs->fPipelineState); |
| if (precompiledLibs->fRTHeight) { |
| this->addRTHeightUniform(SKSL_RTHEIGHT_NAME); |
| } |
| uint32_t bufferSize = buffer_size(fUniformHandler.fCurrentUBOOffset, |
| fUniformHandler.fCurrentUBOMaxAlignment); |
| return new GrMtlPipelineState(fGpu, |
| precompiledLibs->fPipelineState, |
| GrBackendFormatAsMTLPixelFormat(programInfo.backendFormat()), |
| fUniformHandles, |
| fUniformHandler.fUniforms, |
| bufferSize, |
| (uint32_t)fUniformHandler.numSamplers(), |
| std::move(fGeometryProcessor), |
| std::move(fXferProcessor), |
| std::move(fFPImpls)); |
| } |
| |
| // build from scratch |
| std::unique_ptr<SkBinaryWriteBuffer> writer; |
| |
| sk_sp<SkData> cached; |
| auto persistentCache = fGpu->getContext()->priv().getPersistentCache(); |
| if (persistentCache) { |
| sk_sp<SkData> 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]; |
| 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]); |
| |
| id<MTLLibrary> shaderLibraries[kGrShaderTypeCount]; |
| |
| fVS.extensions().appendf("#extension GL_ARB_separate_shader_objects : enable\n"); |
| fFS.extensions().appendf("#extension GL_ARB_separate_shader_objects : enable\n"); |
| fVS.extensions().appendf("#extension GL_ARB_shading_language_420pack : enable\n"); |
| fFS.extensions().appendf("#extension GL_ARB_shading_language_420pack : enable\n"); |
| |
| this->finalizeShaders(); |
| |
| SkSL::Program::Settings settings; |
| settings.fFlipY = this->origin() != kTopLeft_GrSurfaceOrigin; |
| 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<SkData> 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<MTLBinaryArchive> 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<MTLRenderPipelineState> 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; |
| } |
| |
| uint32_t bufferSize = buffer_size(fUniformHandler.fCurrentUBOOffset, |
| fUniformHandler.fCurrentUBOMaxAlignment); |
| return new GrMtlPipelineState(fGpu, |
| pipelineState, |
| pipelineDescriptor.colorAttachments[0].pixelFormat, |
| fUniformHandles, |
| fUniformHandler.fUniforms, |
| bufferSize, |
| (uint32_t)fUniformHandler.numSamplers(), |
| std::move(fGeometryProcessor), |
| std::move(fXferProcessor), |
| 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; |
| } |
| |
| id<MTLLibrary> vertexLibrary; |
| id<MTLLibrary> fragmentLibrary; |
| switch (shaderType) { |
| case kMSL_Tag: { |
| vertexLibrary = |
| GrCompileMtlShaderLibrary(gpu, shaders[kVertex_GrShaderType], errorHandler); |
| fragmentLibrary = |
| 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; |
| } |
| vertexLibrary = |
| GrCompileMtlShaderLibrary(gpu, msl[kVertex_GrShaderType], errorHandler); |
| fragmentLibrary = |
| GrCompileMtlShaderLibrary(gpu, msl[kFragment_GrShaderType], errorHandler); |
| break; |
| } |
| |
| default: { |
| return false; |
| } |
| } |
| |
| pipelineDescriptor.vertexFunction = |
| [vertexLibrary newFunctionWithName: @"vertexMain"]; |
| pipelineDescriptor.fragmentFunction = |
| [fragmentLibrary newFunctionWithName: @"fragmentMain"]; |
| |
| NSError* error = nil; |
| #if GR_METAL_SDK_VERSION >= 230 |
| if (@available(macOS 11.0, iOS 14.0, *)) { |
| id<MTLBinaryArchive> archive = gpu->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 |
| { |
| TRACE_EVENT0("skia.shaders", "newRenderPipelineStateWithDescriptor"); |
| #if defined(SK_BUILD_FOR_MAC) |
| precompiledLibs->fPipelineState = |
| GrMtlNewRenderPipelineStateWithDescriptor(gpu->device(), pipelineDescriptor, &error); |
| #else |
| precompiledLibs->fPipelineState = |
| [gpu->device() newRenderPipelineStateWithDescriptor: pipelineDescriptor |
| error: &error]; |
| #endif |
| } |
| if (error) { |
| SkDebugf("Error creating pipeline: %s\n", |
| [[error localizedDescription] cStringUsingEncoding: NSASCIIStringEncoding]); |
| return false; |
| } |
| |
| precompiledLibs->fRTHeight = inputs[kFragment_GrShaderType].fRTHeight; |
| return true; |
| } |
| |
| GR_NORETAIN_END |