Flutter Engine
The Flutter Engine
MtlGraphicsPipeline.mm
Go to the documentation of this file.
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
9
17
18namespace skgpu::graphite {
19
20namespace {
21
23 switch (type) {
25 return MTLVertexFormatFloat;
27 return MTLVertexFormatFloat2;
29 return MTLVertexFormatFloat3;
31 return MTLVertexFormatFloat4;
33 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
34 return MTLVertexFormatHalf;
35 } else {
36 return MTLVertexFormatInvalid;
37 }
39 return MTLVertexFormatHalf2;
41 return MTLVertexFormatHalf4;
43 return MTLVertexFormatInt2;
45 return MTLVertexFormatInt3;
47 return MTLVertexFormatInt4;
49 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
50 return MTLVertexFormatChar;
51 } else {
52 return MTLVertexFormatInvalid;
53 }
55 return MTLVertexFormatChar2;
57 return MTLVertexFormatChar4;
59 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
60 return MTLVertexFormatUChar;
61 } else {
62 return MTLVertexFormatInvalid;
63 }
65 return MTLVertexFormatUChar2;
67 return MTLVertexFormatUChar4;
69 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
70 return MTLVertexFormatUCharNormalized;
71 } else {
72 return MTLVertexFormatInvalid;
73 }
75 return MTLVertexFormatUChar4Normalized;
77 return MTLVertexFormatShort2;
79 return MTLVertexFormatShort4;
81 return MTLVertexFormatUShort2;
83 return MTLVertexFormatUShort2Normalized;
85 return MTLVertexFormatInt;
87 return MTLVertexFormatUInt;
89 if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
90 return MTLVertexFormatUShortNormalized;
91 } else {
92 return MTLVertexFormatInvalid;
93 }
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) {
153 return MTLBlendFactorZero;
155 return MTLBlendFactorOne;
157 return MTLBlendFactorSourceColor;
159 return MTLBlendFactorOneMinusSourceColor;
161 return MTLBlendFactorDestinationColor;
163 return MTLBlendFactorOneMinusDestinationColor;
165 return MTLBlendFactorSourceAlpha;
167 return MTLBlendFactorOneMinusSourceAlpha;
169 return MTLBlendFactorDestinationAlpha;
171 return MTLBlendFactorOneMinusDestinationAlpha;
173 return MTLBlendFactorBlendColor;
175 return MTLBlendFactorOneMinusBlendColor;
177 if (@available(macOS 10.12, iOS 11.0, tvOS 11.0, *)) {
178 return MTLBlendFactorSource1Color;
179 } else {
180 return MTLBlendFactorZero;
181 }
183 if (@available(macOS 10.12, iOS 11.0, tvOS 11.0, *)) {
184 return MTLBlendFactorOneMinusSource1Color;
185 } else {
186 return MTLBlendFactorZero;
187 }
189 if (@available(macOS 10.12, iOS 11.0, tvOS 11.0, *)) {
190 return MTLBlendFactorSource1Alpha;
191 } else {
192 return MTLBlendFactorZero;
193 }
195 if (@available(macOS 10.12, iOS 11.0, tvOS 11.0, *)) {
196 return MTLBlendFactorOneMinusSource1Alpha;
197 } else {
198 return MTLBlendFactorZero;
199 }
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
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 =
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
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
static MTLBlendOperation blend_equation_to_mtl_blend_op(skgpu::BlendEquation equation)
static MTLRenderPipelineColorAttachmentDescriptor * create_color_attachment(MTLPixelFormat format, const GrPipeline &pipeline, SkBinaryWriteBuffer *writer)
static MTLBlendFactor blend_coeff_to_mtl_blend(skgpu::BlendCoeff coeff)
static MTLVertexFormat attribute_type_to_mtlformat(GrVertexAttribType type)
static MTLVertexDescriptor * create_vertex_descriptor(const GrGeometryProcessor &geomProc, SkBinaryWriteBuffer *writer)
#define SKGPU_LOG_E(fmt,...)
Definition: Log.h:38
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
#define SkASSERT(cond)
Definition: SkAssert.h:116
GLenum type
std::pair< id< MTLLibrary >, std::string > MSLFunction
static constexpr unsigned int kVertexBufferIndex
static constexpr unsigned int kInstanceBufferIndex
static sk_sp< MtlGraphicsPipeline > Make(const MtlSharedContext *, const std::string &label, MSLFunction vertexMain, SkSpan< const Attribute > vertexAttrs, SkSpan< const Attribute > instanceAttrs, MSLFunction fragmentMain, sk_cfp< id< MTLDepthStencilState > >, uint32_t stencilRefValue, const BlendInfo &blendInfo, const RenderPassDesc &, PipelineInfo *pipelineInfo)
const SharedContext * sharedContext() const
Definition: Resource.h:189
uint32_t numSamples() const
Definition: TextureInfo.h:78
if(end==-1)
const uint8_t uint32_t uint32_t GError ** error
uint32_t uint32_t * format
static bool init()
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
const myers::Point & get< 1 >(const myers::Segment &s)
Definition: Myers.h:81
const myers::Point & get< 0 >(const myers::Segment &s)
Definition: Myers.h:80
static constexpr bool BlendShouldDisable(BlendEquation equation, BlendCoeff srcCoeff, BlendCoeff dstCoeff)
Definition: Blend.h:145
static const int kBlendEquationCnt
Definition: Blend.h:55
BlendEquation
Definition: Blend.h:26
BlendCoeff
Definition: Blend.h:60
bool MtlFormatIsStencil(MTLPixelFormat format)
Definition: MtlUtils.mm:47
bool MtlFormatIsDepth(MTLPixelFormat format)
Definition: MtlUtils.mm:33
Definition: ref_ptr.h:256
AttachmentDesc fDepthStencilAttachment