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