• 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 "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