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