1/* 2 * Copyright 2021 Google LLC 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/graphite/mtl/MtlGraphicsPipeline.h" 9 10#include "include/gpu/graphite/TextureInfo.h" 11#include "src/gpu/graphite/Attribute.h" 12#include "src/gpu/graphite/Log.h" 13#include "src/gpu/graphite/RenderPassDesc.h" 14#include "src/gpu/graphite/mtl/MtlResourceProvider.h" 15#include "src/gpu/graphite/mtl/MtlSharedContext.h" 16#include "src/gpu/mtl/MtlUtilsPriv.h" 17 18namespace skgpu::graphite { 19 20namespace { 21 22inline MTLVertexFormat attribute_type_to_mtlformat(VertexAttribType type) { 23 switch (type) { 24 case VertexAttribType::kFloat: 25 return MTLVertexFormatFloat; 26 case VertexAttribType::kFloat2: 27 return MTLVertexFormatFloat2; 28 case VertexAttribType::kFloat3: 29 return MTLVertexFormatFloat3; 30 case VertexAttribType::kFloat4: 31 return MTLVertexFormatFloat4; 32 case VertexAttribType::kHalf: 33 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 34 return MTLVertexFormatHalf; 35 } else { 36 return MTLVertexFormatInvalid; 37 } 38 case VertexAttribType::kHalf2: 39 return MTLVertexFormatHalf2; 40 case VertexAttribType::kHalf4: 41 return MTLVertexFormatHalf4; 42 case VertexAttribType::kInt2: 43 return MTLVertexFormatInt2; 44 case VertexAttribType::kInt3: 45 return MTLVertexFormatInt3; 46 case VertexAttribType::kInt4: 47 return MTLVertexFormatInt4; 48 case VertexAttribType::kByte: 49 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 50 return MTLVertexFormatChar; 51 } else { 52 return MTLVertexFormatInvalid; 53 } 54 case VertexAttribType::kByte2: 55 return MTLVertexFormatChar2; 56 case VertexAttribType::kByte4: 57 return MTLVertexFormatChar4; 58 case VertexAttribType::kUByte: 59 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 60 return MTLVertexFormatUChar; 61 } else { 62 return MTLVertexFormatInvalid; 63 } 64 case VertexAttribType::kUByte2: 65 return MTLVertexFormatUChar2; 66 case VertexAttribType::kUByte4: 67 return MTLVertexFormatUChar4; 68 case VertexAttribType::kUByte_norm: 69 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 70 return MTLVertexFormatUCharNormalized; 71 } else { 72 return MTLVertexFormatInvalid; 73 } 74 case VertexAttribType::kUByte4_norm: 75 return MTLVertexFormatUChar4Normalized; 76 case VertexAttribType::kShort2: 77 return MTLVertexFormatShort2; 78 case VertexAttribType::kShort4: 79 return MTLVertexFormatShort4; 80 case VertexAttribType::kUShort2: 81 return MTLVertexFormatUShort2; 82 case VertexAttribType::kUShort2_norm: 83 return MTLVertexFormatUShort2Normalized; 84 case VertexAttribType::kInt: 85 return MTLVertexFormatInt; 86 case VertexAttribType::kUInt: 87 return MTLVertexFormatUInt; 88 case VertexAttribType::kUShort_norm: 89 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 90 return MTLVertexFormatUShortNormalized; 91 } else { 92 return MTLVertexFormatInvalid; 93 } 94 case VertexAttribType::kUShort4_norm: 95 return MTLVertexFormatUShort4Normalized; 96 } 97 SK_ABORT("Unknown vertex attribute type"); 98} 99 100MTLVertexDescriptor* create_vertex_descriptor(SkSpan<const Attribute> vertexAttrs, 101 SkSpan<const Attribute> instanceAttrs) { 102 auto vertexDescriptor = [[MTLVertexDescriptor alloc] init]; 103 int attributeIndex = 0; 104 105 size_t vertexAttributeOffset = 0; 106 for (const auto& attribute : vertexAttrs) { 107 MTLVertexAttributeDescriptor* mtlAttribute = vertexDescriptor.attributes[attributeIndex]; 108 MTLVertexFormat format = attribute_type_to_mtlformat(attribute.cpuType()); 109 SkASSERT(MTLVertexFormatInvalid != format); 110 mtlAttribute.format = format; 111 mtlAttribute.offset = vertexAttributeOffset; 112 mtlAttribute.bufferIndex = MtlGraphicsPipeline::kVertexBufferIndex; 113 114 vertexAttributeOffset += attribute.sizeAlign4(); 115 attributeIndex++; 116 } 117 118 if (vertexAttributeOffset) { 119 MTLVertexBufferLayoutDescriptor* vertexBufferLayout = 120 vertexDescriptor.layouts[MtlGraphicsPipeline::kVertexBufferIndex]; 121 vertexBufferLayout.stepFunction = MTLVertexStepFunctionPerVertex; 122 vertexBufferLayout.stepRate = 1; 123 vertexBufferLayout.stride = vertexAttributeOffset; 124 } 125 126 size_t instanceAttributeOffset = 0; 127 for (const auto& attribute : instanceAttrs) { 128 MTLVertexAttributeDescriptor* mtlAttribute = vertexDescriptor.attributes[attributeIndex]; 129 MTLVertexFormat format = attribute_type_to_mtlformat(attribute.cpuType()); 130 SkASSERT(MTLVertexFormatInvalid != format); 131 mtlAttribute.format = format; 132 mtlAttribute.offset = instanceAttributeOffset; 133 mtlAttribute.bufferIndex = MtlGraphicsPipeline::kInstanceBufferIndex; 134 135 instanceAttributeOffset += attribute.sizeAlign4(); 136 attributeIndex++; 137 } 138 139 if (instanceAttributeOffset) { 140 MTLVertexBufferLayoutDescriptor* instanceBufferLayout = 141 vertexDescriptor.layouts[MtlGraphicsPipeline::kInstanceBufferIndex]; 142 instanceBufferLayout.stepFunction = MTLVertexStepFunctionPerInstance; 143 instanceBufferLayout.stepRate = 1; 144 instanceBufferLayout.stride = instanceAttributeOffset; 145 } 146 return vertexDescriptor; 147} 148 149// TODO: share this w/ Ganesh Metal backend? 150static MTLBlendFactor blend_coeff_to_mtl_blend(skgpu::BlendCoeff coeff) { 151 switch (coeff) { 152 case skgpu::BlendCoeff::kZero: 153 return MTLBlendFactorZero; 154 case skgpu::BlendCoeff::kOne: 155 return MTLBlendFactorOne; 156 case skgpu::BlendCoeff::kSC: 157 return MTLBlendFactorSourceColor; 158 case skgpu::BlendCoeff::kISC: 159 return MTLBlendFactorOneMinusSourceColor; 160 case skgpu::BlendCoeff::kDC: 161 return MTLBlendFactorDestinationColor; 162 case skgpu::BlendCoeff::kIDC: 163 return MTLBlendFactorOneMinusDestinationColor; 164 case skgpu::BlendCoeff::kSA: 165 return MTLBlendFactorSourceAlpha; 166 case skgpu::BlendCoeff::kISA: 167 return MTLBlendFactorOneMinusSourceAlpha; 168 case skgpu::BlendCoeff::kDA: 169 return MTLBlendFactorDestinationAlpha; 170 case skgpu::BlendCoeff::kIDA: 171 return MTLBlendFactorOneMinusDestinationAlpha; 172 case skgpu::BlendCoeff::kConstC: 173 return MTLBlendFactorBlendColor; 174 case skgpu::BlendCoeff::kIConstC: 175 return MTLBlendFactorOneMinusBlendColor; 176 case skgpu::BlendCoeff::kS2C: 177 if (@available(macOS 10.12, iOS 11.0, tvOS 11.0, *)) { 178 return MTLBlendFactorSource1Color; 179 } else { 180 return MTLBlendFactorZero; 181 } 182 case skgpu::BlendCoeff::kIS2C: 183 if (@available(macOS 10.12, iOS 11.0, tvOS 11.0, *)) { 184 return MTLBlendFactorOneMinusSource1Color; 185 } else { 186 return MTLBlendFactorZero; 187 } 188 case skgpu::BlendCoeff::kS2A: 189 if (@available(macOS 10.12, iOS 11.0, tvOS 11.0, *)) { 190 return MTLBlendFactorSource1Alpha; 191 } else { 192 return MTLBlendFactorZero; 193 } 194 case skgpu::BlendCoeff::kIS2A: 195 if (@available(macOS 10.12, iOS 11.0, tvOS 11.0, *)) { 196 return MTLBlendFactorOneMinusSource1Alpha; 197 } else { 198 return MTLBlendFactorZero; 199 } 200 case skgpu::BlendCoeff::kIllegal: 201 return MTLBlendFactorZero; 202 } 203 204 SK_ABORT("Unknown blend coefficient"); 205} 206 207// TODO: share this w/ Ganesh Metal backend? 208static MTLBlendOperation blend_equation_to_mtl_blend_op(skgpu::BlendEquation equation) { 209 static const MTLBlendOperation gTable[] = { 210 MTLBlendOperationAdd, // skgpu::BlendEquation::kAdd 211 MTLBlendOperationSubtract, // skgpu::BlendEquation::kSubtract 212 MTLBlendOperationReverseSubtract, // skgpu::BlendEquation::kReverseSubtract 213 }; 214 static_assert(std::size(gTable) == (int)skgpu::BlendEquation::kFirstAdvanced); 215 static_assert(0 == (int)skgpu::BlendEquation::kAdd); 216 static_assert(1 == (int)skgpu::BlendEquation::kSubtract); 217 static_assert(2 == (int)skgpu::BlendEquation::kReverseSubtract); 218 219 SkASSERT((unsigned)equation < skgpu::kBlendEquationCnt); 220 return gTable[(int)equation]; 221} 222 223static MTLRenderPipelineColorAttachmentDescriptor* create_color_attachment( 224 MTLPixelFormat format, 225 const BlendInfo& blendInfo) { 226 227 skgpu::BlendEquation equation = blendInfo.fEquation; 228 skgpu::BlendCoeff srcCoeff = blendInfo.fSrcBlend; 229 skgpu::BlendCoeff dstCoeff = blendInfo.fDstBlend; 230 bool blendOn = !skgpu::BlendShouldDisable(equation, srcCoeff, dstCoeff); 231 232 // TODO: I *think* this gets cleaned up by the pipelineDescriptor? 233 auto mtlColorAttachment = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init]; 234 235 mtlColorAttachment.pixelFormat = format; 236 237 mtlColorAttachment.blendingEnabled = blendOn; 238 239 if (blendOn) { 240 mtlColorAttachment.sourceRGBBlendFactor = blend_coeff_to_mtl_blend(srcCoeff); 241 mtlColorAttachment.destinationRGBBlendFactor = blend_coeff_to_mtl_blend(dstCoeff); 242 mtlColorAttachment.rgbBlendOperation = blend_equation_to_mtl_blend_op(equation); 243 mtlColorAttachment.sourceAlphaBlendFactor = blend_coeff_to_mtl_blend(srcCoeff); 244 mtlColorAttachment.destinationAlphaBlendFactor = blend_coeff_to_mtl_blend(dstCoeff); 245 mtlColorAttachment.alphaBlendOperation = blend_equation_to_mtl_blend_op(equation); 246 } 247 248 mtlColorAttachment.writeMask = blendInfo.fWritesColor ? MTLColorWriteMaskAll 249 : MTLColorWriteMaskNone; 250 251 return mtlColorAttachment; 252} 253 254} // anonymous namespace 255 256sk_sp<MtlGraphicsPipeline> MtlGraphicsPipeline::Make(const MtlSharedContext* sharedContext, 257 const std::string& label, 258 MSLFunction vertexMain, 259 SkSpan<const Attribute> vertexAttrs, 260 SkSpan<const Attribute> instanceAttrs, 261 MSLFunction fragmentMain, 262 sk_cfp<id<MTLDepthStencilState>> dss, 263 uint32_t stencilRefValue, 264 const BlendInfo& blendInfo, 265 const RenderPassDesc& renderPassDesc, 266 PipelineInfo* pipelineInfo) { 267 id<MTLLibrary> vsLibrary = std::get<0>(vertexMain); 268 id<MTLLibrary> fsLibrary = std::get<0>(fragmentMain); 269 if (!vsLibrary || !fsLibrary) { 270 return nullptr; 271 } 272 273 sk_cfp<MTLRenderPipelineDescriptor*> psoDescriptor([[MTLRenderPipelineDescriptor alloc] init]); 274 275 NSString* labelName = [NSString stringWithUTF8String: label.c_str()]; 276 NSString* vsFuncName = [NSString stringWithUTF8String: std::get<1>(vertexMain).c_str()]; 277 NSString* fsFuncName = [NSString stringWithUTF8String: std::get<1>(fragmentMain).c_str()]; 278 279 (*psoDescriptor).label = labelName; 280 (*psoDescriptor).vertexFunction = [vsLibrary newFunctionWithName: vsFuncName]; 281 (*psoDescriptor).fragmentFunction = [fsLibrary newFunctionWithName: fsFuncName]; 282 283 // TODO: I *think* this gets cleaned up by the pipelineDescriptor? 284 (*psoDescriptor).vertexDescriptor = create_vertex_descriptor(vertexAttrs, instanceAttrs); 285 286 const MtlTextureSpec& mtlColorSpec = 287 renderPassDesc.fColorAttachment.fTextureInfo.mtlTextureSpec(); 288 auto mtlColorAttachment = create_color_attachment((MTLPixelFormat)mtlColorSpec.fFormat, 289 blendInfo); 290 (*psoDescriptor).colorAttachments[0] = mtlColorAttachment; 291 292 (*psoDescriptor).rasterSampleCount = 293 renderPassDesc.fColorAttachment.fTextureInfo.numSamples(); 294 295 const MtlTextureSpec& mtlDSSpec = 296 renderPassDesc.fDepthStencilAttachment.fTextureInfo.mtlTextureSpec(); 297 MTLPixelFormat depthStencilFormat = (MTLPixelFormat)mtlDSSpec.fFormat; 298 if (MtlFormatIsStencil(depthStencilFormat)) { 299 (*psoDescriptor).stencilAttachmentPixelFormat = depthStencilFormat; 300 } else { 301 (*psoDescriptor).stencilAttachmentPixelFormat = MTLPixelFormatInvalid; 302 } 303 if (MtlFormatIsDepth(depthStencilFormat)) { 304 (*psoDescriptor).depthAttachmentPixelFormat = depthStencilFormat; 305 } else { 306 (*psoDescriptor).depthAttachmentPixelFormat = MTLPixelFormatInvalid; 307 } 308 309 NSError* error; 310 sk_cfp<id<MTLRenderPipelineState>> pso( 311 [sharedContext->device() newRenderPipelineStateWithDescriptor:psoDescriptor.get() 312 error:&error]); 313 if (!pso) { 314 SKGPU_LOG_E("Render pipeline creation failure:\n%s", error.debugDescription.UTF8String); 315 return nullptr; 316 } 317 318 return sk_sp<MtlGraphicsPipeline>(new MtlGraphicsPipeline(sharedContext, 319 pipelineInfo, 320 std::move(pso), 321 std::move(dss), 322 stencilRefValue)); 323} 324 325MtlGraphicsPipeline::MtlGraphicsPipeline(const skgpu::graphite::SharedContext* sharedContext, 326 PipelineInfo* pipelineInfo, 327 sk_cfp<id<MTLRenderPipelineState>> pso, 328 sk_cfp<id<MTLDepthStencilState>> dss, 329 uint32_t refValue) 330 : GraphicsPipeline(sharedContext, pipelineInfo) 331 , fPipelineState(std::move(pso)) 332 , fDepthStencilState(std::move(dss)) 333 , fStencilReferenceValue(refValue) {} 334 335void MtlGraphicsPipeline::freeGpuData() { 336 fPipelineState.reset(); 337} 338 339} // namespace skgpu::graphite 340