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 "include/gpu/graphite/mtl/MtlGraphiteTypes.h" 12#include "src/gpu/graphite/Attribute.h" 13#include "src/gpu/graphite/ContextUtils.h" 14#include "src/gpu/graphite/GraphicsPipelineDesc.h" 15#include "src/gpu/graphite/Log.h" 16#include "src/gpu/graphite/RenderPassDesc.h" 17#include "src/gpu/graphite/RendererProvider.h" 18#include "src/gpu/graphite/ShaderInfo.h" 19#include "src/gpu/graphite/TextureInfoPriv.h" 20#include "src/gpu/graphite/mtl/MtlGraphiteUtils.h" 21#include "src/gpu/graphite/mtl/MtlResourceProvider.h" 22#include "src/gpu/graphite/mtl/MtlSharedContext.h" 23#include "src/gpu/mtl/MtlUtilsPriv.h" 24#include "src/sksl/SkSLCompiler.h" 25#include "src/sksl/SkSLProgramSettings.h" 26#include "src/sksl/ir/SkSLProgram.h" 27 28namespace skgpu::graphite { 29 30namespace { 31 32inline MTLVertexFormat attribute_type_to_mtlformat(VertexAttribType type) { 33 switch (type) { 34 case VertexAttribType::kFloat: 35 return MTLVertexFormatFloat; 36 case VertexAttribType::kFloat2: 37 return MTLVertexFormatFloat2; 38 case VertexAttribType::kFloat3: 39 return MTLVertexFormatFloat3; 40 case VertexAttribType::kFloat4: 41 return MTLVertexFormatFloat4; 42 case VertexAttribType::kHalf: 43 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 44 return MTLVertexFormatHalf; 45 } else { 46 return MTLVertexFormatInvalid; 47 } 48 case VertexAttribType::kHalf2: 49 return MTLVertexFormatHalf2; 50 case VertexAttribType::kHalf4: 51 return MTLVertexFormatHalf4; 52 case VertexAttribType::kInt2: 53 return MTLVertexFormatInt2; 54 case VertexAttribType::kInt3: 55 return MTLVertexFormatInt3; 56 case VertexAttribType::kInt4: 57 return MTLVertexFormatInt4; 58 case VertexAttribType::kUInt2: 59 return MTLVertexFormatUInt2; 60 case VertexAttribType::kByte: 61 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 62 return MTLVertexFormatChar; 63 } else { 64 return MTLVertexFormatInvalid; 65 } 66 case VertexAttribType::kByte2: 67 return MTLVertexFormatChar2; 68 case VertexAttribType::kByte4: 69 return MTLVertexFormatChar4; 70 case VertexAttribType::kUByte: 71 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 72 return MTLVertexFormatUChar; 73 } else { 74 return MTLVertexFormatInvalid; 75 } 76 case VertexAttribType::kUByte2: 77 return MTLVertexFormatUChar2; 78 case VertexAttribType::kUByte4: 79 return MTLVertexFormatUChar4; 80 case VertexAttribType::kUByte_norm: 81 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 82 return MTLVertexFormatUCharNormalized; 83 } else { 84 return MTLVertexFormatInvalid; 85 } 86 case VertexAttribType::kUByte4_norm: 87 return MTLVertexFormatUChar4Normalized; 88 case VertexAttribType::kShort2: 89 return MTLVertexFormatShort2; 90 case VertexAttribType::kShort4: 91 return MTLVertexFormatShort4; 92 case VertexAttribType::kUShort2: 93 return MTLVertexFormatUShort2; 94 case VertexAttribType::kUShort2_norm: 95 return MTLVertexFormatUShort2Normalized; 96 case VertexAttribType::kInt: 97 return MTLVertexFormatInt; 98 case VertexAttribType::kUInt: 99 return MTLVertexFormatUInt; 100 case VertexAttribType::kUShort_norm: 101 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) { 102 return MTLVertexFormatUShortNormalized; 103 } else { 104 return MTLVertexFormatInvalid; 105 } 106 case VertexAttribType::kUShort4_norm: 107 return MTLVertexFormatUShort4Normalized; 108 } 109 SK_ABORT("Unknown vertex attribute type"); 110} 111 112MTLVertexDescriptor* create_vertex_descriptor(SkSpan<const Attribute> vertexAttrs, 113 SkSpan<const Attribute> instanceAttrs) { 114 auto vertexDescriptor = [[MTLVertexDescriptor alloc] init]; 115 int attributeIndex = 0; 116 117 size_t vertexAttributeOffset = 0; 118 for (const auto& attribute : vertexAttrs) { 119 MTLVertexAttributeDescriptor* mtlAttribute = vertexDescriptor.attributes[attributeIndex]; 120 MTLVertexFormat format = attribute_type_to_mtlformat(attribute.cpuType()); 121 SkASSERT(MTLVertexFormatInvalid != format); 122 mtlAttribute.format = format; 123 mtlAttribute.offset = vertexAttributeOffset; 124 mtlAttribute.bufferIndex = MtlGraphicsPipeline::kVertexBufferIndex; 125 126 vertexAttributeOffset += attribute.sizeAlign4(); 127 attributeIndex++; 128 } 129 130 if (vertexAttributeOffset) { 131 MTLVertexBufferLayoutDescriptor* vertexBufferLayout = 132 vertexDescriptor.layouts[MtlGraphicsPipeline::kVertexBufferIndex]; 133 vertexBufferLayout.stepFunction = MTLVertexStepFunctionPerVertex; 134 vertexBufferLayout.stepRate = 1; 135 vertexBufferLayout.stride = vertexAttributeOffset; 136 } 137 138 size_t instanceAttributeOffset = 0; 139 for (const auto& attribute : instanceAttrs) { 140 MTLVertexAttributeDescriptor* mtlAttribute = vertexDescriptor.attributes[attributeIndex]; 141 MTLVertexFormat format = attribute_type_to_mtlformat(attribute.cpuType()); 142 SkASSERT(MTLVertexFormatInvalid != format); 143 mtlAttribute.format = format; 144 mtlAttribute.offset = instanceAttributeOffset; 145 mtlAttribute.bufferIndex = MtlGraphicsPipeline::kInstanceBufferIndex; 146 147 instanceAttributeOffset += attribute.sizeAlign4(); 148 attributeIndex++; 149 } 150 151 if (instanceAttributeOffset) { 152 MTLVertexBufferLayoutDescriptor* instanceBufferLayout = 153 vertexDescriptor.layouts[MtlGraphicsPipeline::kInstanceBufferIndex]; 154 instanceBufferLayout.stepFunction = MTLVertexStepFunctionPerInstance; 155 instanceBufferLayout.stepRate = 1; 156 instanceBufferLayout.stride = instanceAttributeOffset; 157 } 158 return vertexDescriptor; 159} 160 161// TODO: share this w/ Ganesh Metal backend? 162static MTLBlendFactor blend_coeff_to_mtl_blend(skgpu::BlendCoeff coeff) { 163 switch (coeff) { 164 case skgpu::BlendCoeff::kZero: 165 return MTLBlendFactorZero; 166 case skgpu::BlendCoeff::kOne: 167 return MTLBlendFactorOne; 168 case skgpu::BlendCoeff::kSC: 169 return MTLBlendFactorSourceColor; 170 case skgpu::BlendCoeff::kISC: 171 return MTLBlendFactorOneMinusSourceColor; 172 case skgpu::BlendCoeff::kDC: 173 return MTLBlendFactorDestinationColor; 174 case skgpu::BlendCoeff::kIDC: 175 return MTLBlendFactorOneMinusDestinationColor; 176 case skgpu::BlendCoeff::kSA: 177 return MTLBlendFactorSourceAlpha; 178 case skgpu::BlendCoeff::kISA: 179 return MTLBlendFactorOneMinusSourceAlpha; 180 case skgpu::BlendCoeff::kDA: 181 return MTLBlendFactorDestinationAlpha; 182 case skgpu::BlendCoeff::kIDA: 183 return MTLBlendFactorOneMinusDestinationAlpha; 184 case skgpu::BlendCoeff::kConstC: 185 return MTLBlendFactorBlendColor; 186 case skgpu::BlendCoeff::kIConstC: 187 return MTLBlendFactorOneMinusBlendColor; 188 case skgpu::BlendCoeff::kS2C: 189 if (@available(macOS 10.12, iOS 11.0, tvOS 11.0, *)) { 190 return MTLBlendFactorSource1Color; 191 } else { 192 return MTLBlendFactorZero; 193 } 194 case skgpu::BlendCoeff::kIS2C: 195 if (@available(macOS 10.12, iOS 11.0, tvOS 11.0, *)) { 196 return MTLBlendFactorOneMinusSource1Color; 197 } else { 198 return MTLBlendFactorZero; 199 } 200 case skgpu::BlendCoeff::kS2A: 201 if (@available(macOS 10.12, iOS 11.0, tvOS 11.0, *)) { 202 return MTLBlendFactorSource1Alpha; 203 } else { 204 return MTLBlendFactorZero; 205 } 206 case skgpu::BlendCoeff::kIS2A: 207 if (@available(macOS 10.12, iOS 11.0, tvOS 11.0, *)) { 208 return MTLBlendFactorOneMinusSource1Alpha; 209 } else { 210 return MTLBlendFactorZero; 211 } 212 case skgpu::BlendCoeff::kIllegal: 213 return MTLBlendFactorZero; 214 } 215 216 SK_ABORT("Unknown blend coefficient"); 217} 218 219// TODO: share this w/ Ganesh Metal backend? 220static MTLBlendOperation blend_equation_to_mtl_blend_op(skgpu::BlendEquation equation) { 221 static const MTLBlendOperation gTable[] = { 222 MTLBlendOperationAdd, // skgpu::BlendEquation::kAdd 223 MTLBlendOperationSubtract, // skgpu::BlendEquation::kSubtract 224 MTLBlendOperationReverseSubtract, // skgpu::BlendEquation::kReverseSubtract 225 }; 226 static_assert(std::size(gTable) == (int)skgpu::BlendEquation::kFirstAdvanced); 227 static_assert(0 == (int)skgpu::BlendEquation::kAdd); 228 static_assert(1 == (int)skgpu::BlendEquation::kSubtract); 229 static_assert(2 == (int)skgpu::BlendEquation::kReverseSubtract); 230 231 SkASSERT((unsigned)equation < skgpu::kBlendEquationCnt); 232 return gTable[(int)equation]; 233} 234 235static MTLRenderPipelineColorAttachmentDescriptor* create_color_attachment( 236 MTLPixelFormat format, 237 const BlendInfo& blendInfo) { 238 239 skgpu::BlendEquation equation = blendInfo.fEquation; 240 skgpu::BlendCoeff srcCoeff = blendInfo.fSrcBlend; 241 skgpu::BlendCoeff dstCoeff = blendInfo.fDstBlend; 242 bool blendOn = !skgpu::BlendShouldDisable(equation, srcCoeff, dstCoeff); 243 244 // TODO: I *think* this gets cleaned up by the pipelineDescriptor? 245 auto mtlColorAttachment = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init]; 246 247 mtlColorAttachment.pixelFormat = format; 248 249 mtlColorAttachment.blendingEnabled = blendOn; 250 251 if (blendOn) { 252 mtlColorAttachment.sourceRGBBlendFactor = blend_coeff_to_mtl_blend(srcCoeff); 253 mtlColorAttachment.destinationRGBBlendFactor = blend_coeff_to_mtl_blend(dstCoeff); 254 mtlColorAttachment.rgbBlendOperation = blend_equation_to_mtl_blend_op(equation); 255 mtlColorAttachment.sourceAlphaBlendFactor = blend_coeff_to_mtl_blend(srcCoeff); 256 mtlColorAttachment.destinationAlphaBlendFactor = blend_coeff_to_mtl_blend(dstCoeff); 257 mtlColorAttachment.alphaBlendOperation = blend_equation_to_mtl_blend_op(equation); 258 } 259 260 mtlColorAttachment.writeMask = blendInfo.fWritesColor ? MTLColorWriteMaskAll 261 : MTLColorWriteMaskNone; 262 263 return mtlColorAttachment; 264} 265 266} // anonymous namespace 267 268sk_sp<MtlGraphicsPipeline> MtlGraphicsPipeline::Make( 269 const MtlSharedContext* sharedContext, 270 MtlResourceProvider* resourceProvider, 271 const RuntimeEffectDictionary* runtimeDict, 272 const UniqueKey& pipelineKey, 273 const GraphicsPipelineDesc& pipelineDesc, 274 const RenderPassDesc& renderPassDesc, 275 SkEnumBitMask<PipelineCreationFlags> pipelineCreationFlags, 276 uint32_t compilationID) { 277 std::string vsMSL, fsMSL; 278 SkSL::Program::Interface vsInterface, fsInterface; 279 280 SkSL::ProgramSettings settings; 281 settings.fSharpenTextures = true; 282 settings.fForceNoRTFlip = true; 283 284 SkSL::Compiler skslCompiler; 285 ShaderErrorHandler* errorHandler = sharedContext->caps()->shaderErrorHandler(); 286 287 const RenderStep* step = sharedContext->rendererProvider()->lookup(pipelineDesc.renderStepID()); 288 const bool useStorageBuffers = sharedContext->caps()->storageBufferSupport(); 289 290 UniquePaintParamsID paintID = pipelineDesc.paintParamsID(); 291 292 std::unique_ptr<ShaderInfo> shaderInfo = 293 ShaderInfo::Make(sharedContext->caps(), 294 sharedContext->shaderCodeDictionary(), 295 runtimeDict, 296 step, 297 paintID, 298 useStorageBuffers, 299 renderPassDesc.fWriteSwizzle, 300 renderPassDesc.fDstReadStrategyIfRequired); 301 302 const std::string& fsSkSL = shaderInfo->fragmentSkSL(); 303 const BlendInfo& blendInfo = shaderInfo->blendInfo(); 304 if (!SkSLToMSL(sharedContext->caps()->shaderCaps(), 305 fsSkSL, 306 SkSL::ProgramKind::kGraphiteFragment, 307 settings, 308 &fsMSL, 309 &fsInterface, 310 errorHandler)) { 311 return nullptr; 312 } 313 314 const std::string& vsSkSL = shaderInfo->vertexSkSL(); 315 if (!SkSLToMSL(sharedContext->caps()->shaderCaps(), 316 vsSkSL, 317 SkSL::ProgramKind::kGraphiteVertex, 318 settings, 319 &vsMSL, 320 &vsInterface, 321 errorHandler)) { 322 return nullptr; 323 } 324 325 auto vsLibrary = 326 MtlCompileShaderLibrary(sharedContext, shaderInfo->vsLabel(), vsMSL, errorHandler); 327 auto fsLibrary = 328 MtlCompileShaderLibrary(sharedContext, shaderInfo->fsLabel(), fsMSL, errorHandler); 329 330 sk_cfp<id<MTLDepthStencilState>> dss = 331 resourceProvider->findOrCreateCompatibleDepthStencilState(step->depthStencilSettings()); 332 333 PipelineInfo pipelineInfo{ *shaderInfo, pipelineCreationFlags, 334 pipelineKey.hash(), compilationID }; 335#if defined(GPU_TEST_UTILS) 336 pipelineInfo.fNativeVertexShader = std::move(vsMSL); 337 pipelineInfo.fNativeFragmentShader = std::move(fsMSL); 338#endif 339 340 std::string pipelineLabel = 341 GetPipelineLabel(sharedContext->shaderCodeDictionary(), renderPassDesc, step, paintID); 342 return Make(sharedContext, 343 pipelineLabel, 344 pipelineInfo, 345 {vsLibrary.get(), "vertexMain"}, 346 step->vertexAttributes(), 347 step->instanceAttributes(), 348 {fsLibrary.get(), "fragmentMain"}, 349 std::move(dss), 350 step->depthStencilSettings().fStencilReferenceValue, 351 blendInfo, 352 renderPassDesc); 353} 354 355sk_sp<MtlGraphicsPipeline> MtlGraphicsPipeline::MakeLoadMSAAPipeline( 356 const MtlSharedContext* sharedContext, 357 MtlResourceProvider* resourceProvider, 358 const RenderPassDesc& renderPassDesc) { 359 static const char* kLoadMSAAShaderText = 360 "#include <metal_stdlib>\n" 361 "#include <simd/simd.h>\n" 362 "using namespace metal;" 363 364 "typedef struct {" 365 "float4 position [[position]];" 366 "} VertexOutput;" 367 368 "vertex VertexOutput vertexMain(uint vertexID [[vertex_id]]) {" 369 "VertexOutput out;" 370 "float2 position = float2(float(vertexID >> 1), float(vertexID & 1));" 371 "out.position = float4(2.0 * position - 1.0, 0.0, 1.0);" 372 "return out;" 373 "}" 374 375 "fragment float4 fragmentMain(VertexOutput in [[stage_in]]," 376 "texture2d<half> colorMap [[texture(0)]]) {" 377 "uint2 coords = uint2(in.position.x, in.position.y);" 378 "half4 colorSample = colorMap.read(coords);" 379 "return float4(colorSample);" 380 "}"; 381 382 auto mtlLibrary = MtlCompileShaderLibrary(sharedContext, 383 "LoadMSAAFromResolve", 384 kLoadMSAAShaderText, 385 sharedContext->caps()->shaderErrorHandler()); 386 BlendInfo noBlend{}; // default is equivalent to kSrc blending 387 sk_cfp<id<MTLDepthStencilState>> ignoreDS = 388 resourceProvider->findOrCreateCompatibleDepthStencilState({}); 389 390 std::string pipelineLabel = "LoadMSAAFromResolve + "; 391 pipelineLabel += renderPassDesc.toString().c_str(); 392 393 PipelineInfo pipelineInfo; 394 pipelineInfo.fNumFragTexturesAndSamplers = 1; 395 // This is an internal shader, leave off filling out the test-utils shader code 396 return Make(sharedContext, 397 pipelineLabel, 398 pipelineInfo, 399 {mtlLibrary.get(), "vertexMain"}, 400 /*vertexAttrs=*/{}, 401 /*instanceAttrs=*/{}, 402 {mtlLibrary.get(), "fragmentMain"}, 403 std::move(ignoreDS), 404 /*stencilRefValue=*/0, 405 noBlend, 406 renderPassDesc); 407} 408 409sk_sp<MtlGraphicsPipeline> MtlGraphicsPipeline::Make(const MtlSharedContext* sharedContext, 410 const std::string& label, 411 const PipelineInfo& pipelineInfo, 412 MSLFunction vertexMain, 413 SkSpan<const Attribute> vertexAttrs, 414 SkSpan<const Attribute> instanceAttrs, 415 MSLFunction fragmentMain, 416 sk_cfp<id<MTLDepthStencilState>> dss, 417 uint32_t stencilRefValue, 418 const BlendInfo& blendInfo, 419 const RenderPassDesc& renderPassDesc) { 420 id<MTLLibrary> vsLibrary = std::get<0>(vertexMain); 421 id<MTLLibrary> fsLibrary = std::get<0>(fragmentMain); 422 if (!vsLibrary || !fsLibrary) { 423 return nullptr; 424 } 425 426 sk_cfp<MTLRenderPipelineDescriptor*> psoDescriptor([[MTLRenderPipelineDescriptor alloc] init]); 427 428 NSString* labelName = [NSString stringWithUTF8String: label.c_str()]; 429 NSString* vsFuncName = [NSString stringWithUTF8String: std::get<1>(vertexMain).c_str()]; 430 NSString* fsFuncName = [NSString stringWithUTF8String: std::get<1>(fragmentMain).c_str()]; 431 432 (*psoDescriptor).label = labelName; 433 (*psoDescriptor).vertexFunction = [vsLibrary newFunctionWithName: vsFuncName]; 434 (*psoDescriptor).fragmentFunction = [fsLibrary newFunctionWithName: fsFuncName]; 435 436 // TODO: I *think* this gets cleaned up by the pipelineDescriptor? 437 (*psoDescriptor).vertexDescriptor = create_vertex_descriptor(vertexAttrs, instanceAttrs); 438 439 const TextureInfo& colorAttachmentInfo = renderPassDesc.fColorAttachment.fTextureInfo; 440 const TextureInfo& dsAttachmentInfo = renderPassDesc.fDepthStencilAttachment.fTextureInfo; 441 442 MTLPixelFormat pixelFormat = TextureInfoPriv::Get<MtlTextureInfo>(colorAttachmentInfo).fFormat; 443 auto mtlColorAttachment = create_color_attachment(pixelFormat, blendInfo); 444 (*psoDescriptor).colorAttachments[0] = mtlColorAttachment; 445 446 (*psoDescriptor).rasterSampleCount = colorAttachmentInfo.numSamples(); 447 448 MTLPixelFormat depthStencilFormat = 449 TextureInfoPriv::Get<MtlTextureInfo>(dsAttachmentInfo).fFormat; 450 if (MtlFormatIsStencil(depthStencilFormat)) { 451 (*psoDescriptor).stencilAttachmentPixelFormat = depthStencilFormat; 452 } else { 453 (*psoDescriptor).stencilAttachmentPixelFormat = MTLPixelFormatInvalid; 454 } 455 if (MtlFormatIsDepth(depthStencilFormat)) { 456 (*psoDescriptor).depthAttachmentPixelFormat = depthStencilFormat; 457 } else { 458 (*psoDescriptor).depthAttachmentPixelFormat = MTLPixelFormatInvalid; 459 } 460 461 NSError* error; 462 sk_cfp<id<MTLRenderPipelineState>> pso( 463 [sharedContext->device() newRenderPipelineStateWithDescriptor:psoDescriptor.get() 464 error:&error]); 465 if (!pso) { 466 SKGPU_LOG_E("Render pipeline creation failure:\n%s", error.debugDescription.UTF8String); 467 return nullptr; 468 } 469 470 return sk_sp<MtlGraphicsPipeline>(new MtlGraphicsPipeline(sharedContext, 471 pipelineInfo, 472 std::move(pso), 473 std::move(dss), 474 stencilRefValue)); 475} 476 477MtlGraphicsPipeline::MtlGraphicsPipeline(const skgpu::graphite::SharedContext* sharedContext, 478 const PipelineInfo& pipelineInfo, 479 sk_cfp<id<MTLRenderPipelineState>> pso, 480 sk_cfp<id<MTLDepthStencilState>> dss, 481 uint32_t refValue) 482 : GraphicsPipeline(sharedContext, pipelineInfo) 483 , fPipelineState(std::move(pso)) 484 , fDepthStencilState(std::move(dss)) 485 , fStencilReferenceValue(refValue) {} 486 487void MtlGraphicsPipeline::freeGpuData() { 488 fPipelineState.reset(); 489} 490 491} // namespace skgpu::graphite 492