VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
metalRenderPipeline.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025-2026 Arnis Lektauers
3//
4// Created by Arnis Lektauers on 27.08.2025.
5//
7
8#include "metalRenderTarget.h"
9#include "metalShader.h"
10#include "core/utils.h"
11#include "spdlog/spdlog.h"
12#include "Foundation/NSBundle.hpp"
13
14namespace visutwin::canvas
15{
16 static int _pipelineId = 0;
17
18 // Primitive topology mapping
19 const MTL::PrimitiveType MetalRenderPipeline::primitiveTopology[5] = {
20 MTL::PrimitiveTypePoint, // PRIMITIVE_POINTS
21 MTL::PrimitiveTypeLine, // PRIMITIVE_LINES
22 MTL::PrimitiveTypeLineStrip, // PRIMITIVE_LINESTRIP
23 MTL::PrimitiveTypeTriangle, // PRIMITIVE_TRIANGLES
24 MTL::PrimitiveTypeTriangleStrip, // PRIMITIVE_TRISTRIP
25 };
26
27 // Blend operation mapping
28 const MTL::BlendOperation MetalRenderPipeline::blendOperation[5] = {
29 MTL::BlendOperationAdd, // BLENDEQUATION_ADD
30 MTL::BlendOperationSubtract, // BLENDEQUATION_SUBTRACT
31 MTL::BlendOperationReverseSubtract, // BLENDEQUATION_REVERSE_SUBTRACT
32 MTL::BlendOperationMin, // BLENDEQUATION_MIN
33 MTL::BlendOperationMax // BLENDEQUATION_MAX
34 };
35
36 // Blend factor mapping
37 const MTL::BlendFactor MetalRenderPipeline::blendFactor[13] = {
38 MTL::BlendFactorZero, // BLENDMODE_ZERO
39 MTL::BlendFactorOne, // BLENDMODE_ONE
40 MTL::BlendFactorSourceColor, // BLENDMODE_SRC_COLOR
41 MTL::BlendFactorOneMinusSourceColor, // BLENDMODE_ONE_MINUS_SRC_COLOR
42 MTL::BlendFactorDestinationColor, // BLENDMODE_DST_COLOR
43 MTL::BlendFactorOneMinusDestinationColor, // BLENDMODE_ONE_MINUS_DST_COLOR
44 MTL::BlendFactorSourceAlpha, // BLENDMODE_SRC_ALPHA
45 MTL::BlendFactorSourceAlphaSaturated, // BLENDMODE_SRC_ALPHA_SATURATE
46 MTL::BlendFactorOneMinusSourceAlpha, // BLENDMODE_ONE_MINUS_SRC_ALPHA
47 MTL::BlendFactorDestinationAlpha, // BLENDMODE_DST_ALPHA
48 MTL::BlendFactorOneMinusDestinationAlpha, // BLENDMODE_ONE_MINUS_DST_ALPHA
49 MTL::BlendFactorBlendColor, // BLENDMODE_CONSTANT
50 MTL::BlendFactorOneMinusBlendColor // BLENDMODE_ONE_MINUS_CONSTANT
51 };
52
54 {
55 _lookupHashes.resize(15, 0);
56 _vertexBufferLayout = std::make_unique<MetalVertexBufferLayout>();
57 _pipeline = nullptr;
58 }
59
61 {
62 if (_pipeline) {
63 _pipeline->release();
64 _pipeline = nullptr;
65 }
66
67 for (const auto& [hash, entries] : _cache) {
68 for (const auto& entry : entries) {
69 if (entry && entry->pipeline) {
70 entry->pipeline->release();
71 }
72 }
73 }
74 }
75
76 /*MTL::VertexDescriptor* buildVertexDescriptor()
77 {
78 MTL::VertexDescriptor* vertexDescriptor = MTL::VertexDescriptor::alloc()->init();
79
80 // Position
81 vertexDescriptor->attributes()->object(0)->setFormat(MTL::VertexFormat::VertexFormatFloat3);
82 vertexDescriptor->attributes()->object(0)->setOffset(0);
83 vertexDescriptor->attributes()->object(0)->setBufferIndex(0);
84
85 // Normal
86 vertexDescriptor->attributes()->object(1)->setFormat(MTL::VertexFormat::VertexFormatFloat3);
87 vertexDescriptor->attributes()->object(1)->setOffset(3 * sizeof(float));
88 vertexDescriptor->attributes()->object(1)->setBufferIndex(0);
89
90 // Texcoord
91 vertexDescriptor->attributes()->object(2)->setFormat(MTL::VertexFormat::VertexFormatFloat2);
92 vertexDescriptor->attributes()->object(2)->setOffset(6 * sizeof(float));
93 vertexDescriptor->attributes()->object(2)->setBufferIndex(0);
94
95 vertexDescriptor->layouts()->object(0)->setStride(8 * sizeof(float));
96 //vertexDescriptor->layouts()->object(0)->setStepFunction(MTL::VertexStepFunction::VertexStepFunctionPerVertex);
97
98 return vertexDescriptor;
99 }
100
101 MetalRenderPipeline::MetalRenderPipeline(const MetalGraphicsDevice* device)
102 {
103 const NS::Bundle* bundle = NS::Bundle::mainBundle();
104 if (!bundle)
105 {
106 spdlog::error("Failed to load main bundle");
107 assert(false);
108 }
109
110 const std::string path = std::string(bundle->resourceURL()->fileSystemRepresentation()) + "/" + descriptor.vertexShader.source;
111 spdlog::info("Loading shader library from {}", path);
112
113 auto* mtlDevice = device.raw();
114
115 NS::Error* error = nullptr;
116 MTL::Library* library = mtlDevice->newLibrary(NS::String::string(path.c_str(), NS::UTF8StringEncoding), &error);
117 if (!library)
118 {
119 spdlog::error("Failed to load shader library: {}", error->localizedDescription()->utf8String());
120 assert(false);
121 }
122
123 const MTL::Function* vertexShader = library->newFunction(NS::String::string(descriptor.vertexShader.entryPoint.c_str(), NS::UTF8StringEncoding));
124 assert(vertexShader);
125
126 const MTL::Function* fragmentShader = library->newFunction(NS::String::string(descriptor.fragmentShader.entryPoint.c_str(), NS::UTF8StringEncoding));
127 assert(fragmentShader);
128
129 const MTL::VertexDescriptor* vertexDescriptor = buildVertexDescriptor();
130
131 MTL::RenderPipelineDescriptor* pipelineDescriptor = MTL::RenderPipelineDescriptor::alloc()->init();
132 pipelineDescriptor->setVertexFunction(vertexShader);
133 pipelineDescriptor->setVertexDescriptor(vertexDescriptor);
134 pipelineDescriptor->setFragmentFunction(fragmentShader);
135 //pipelineDescriptor->setRasterizationEnabled(false);
136
137 const MTL::PixelFormat pixelFormat = device.getSwapChain()->raw()->pixelFormat();
138 pipelineDescriptor->colorAttachments()->object(0)->setPixelFormat(pixelFormat);
139
140 pipeline = mtlDevice->newRenderPipelineState(pipelineDescriptor, &error);
141 if (!pipeline)
142 {
143 spdlog::error("Failed to create render pipeline state: {}", error->localizedDescription()->utf8String());
144 assert(false);
145 }
146
147 pipelineDescriptor->release();
148 }*/
149
150 MTL::RenderPipelineState* MetalRenderPipeline::get(const Primitive& primitive, const std::shared_ptr<VertexFormat>& vertexFormat0,
151 const std::shared_ptr<VertexFormat>& vertexFormat1, int ibFormat, const std::shared_ptr<Shader>& shader,
152 const std::shared_ptr<RenderTarget>& renderTarget,
153 const std::vector<std::shared_ptr<MetalBindGroupFormat>>& bindGroupFormats,
154 const std::shared_ptr<BlendState>& blendState, const std::shared_ptr<DepthState>& depthState,
155 CullMode cullMode, bool stencilEnabled,
156 const std::shared_ptr<StencilParameters>& stencilFront, const std::shared_ptr<StencilParameters>& stencilBack,
157 const std::shared_ptr<VertexFormat>& instancingFormat) {
158 assert(bindGroupFormats.size() <= 3);
159 assert(shader != nullptr);
160 assert(blendState != nullptr);
161 assert(depthState != nullptr);
162
163 // ibFormat is used only for stripped primitives, clear it otherwise to avoid additional render pipelines
164 int primitiveType = primitive.type;
165 if (primitiveType < 0 || primitiveType >= 5) {
166 primitiveType = PRIMITIVE_TRIANGLES;
167 }
168 if (ibFormat != -1 && primitiveType != PRIMITIVE_LINESTRIP && primitiveType != PRIMITIVE_TRISTRIP) {
169 ibFormat = -1;
170 }
171
172 // Render pipeline unique hash
173 _lookupHashes[0] = primitiveType;
174 _lookupHashes[1] = shader->id();
175 _lookupHashes[2] = static_cast<int>(cullMode);
176 _lookupHashes[3] = depthState->key();
177 _lookupHashes[4] = blendState->key();
178 _lookupHashes[5] = vertexFormat0 ? vertexFormat0->renderingHash() : 0;
179 _lookupHashes[6] = vertexFormat1 ? vertexFormat1->renderingHash() : 0;
180 _lookupHashes[7] = renderTarget ? renderTarget->key() : 0;
181 _lookupHashes[8] = bindGroupFormats.size() > 0 && bindGroupFormats[0] ? bindGroupFormats[0]->key() : 0;
182 _lookupHashes[9] = bindGroupFormats.size() > 1 && bindGroupFormats[1] ? bindGroupFormats[1]->key() : 0;
183 _lookupHashes[10] = bindGroupFormats.size() > 2 && bindGroupFormats[2] ? bindGroupFormats[2]->key() : 0;
184 _lookupHashes[11] = stencilEnabled ? stencilFront->key() : 0;
185 _lookupHashes[12] = stencilEnabled ? stencilBack->key() : 0;
186 _lookupHashes[13] = ibFormat != -1 ? ibFormat : 0;
187 _lookupHashes[14] = (instancingFormat && instancingFormat->isInstancing())
188 ? instancingFormat->renderingHash() : 0;
189
190 uint32_t hash = hash32Fnv1a(_lookupHashes.data(), _lookupHashes.size());
191
192 // Check cached pipeline
193 auto it = _cache.find(hash);
194 if (it != _cache.end()) {
195 auto& cacheEntries = it->second;
196 // Find an exact match in case of hash collision
197 for (auto& entry : cacheEntries) {
198 if (std::equal(entry->hashes.begin(), entry->hashes.end(), _lookupHashes.begin())) {
199 return entry->pipeline;
200 }
201 }
202 }
203
204 // No match or hash collision, create a new pipeline
205 const MTL::PrimitiveType primTopology = primitiveTopology[primitiveType];
206
207 // Pipeline layout
208 metal::PipelineLayout* pipelineLayout = getPipelineLayout(bindGroupFormats);
209
210 // Vertex buffer layout
211 auto vbLayout = _vertexBufferLayout->get(vertexFormat0, vertexFormat1);
212
213 // Derive vertex stride from format (defaults to 56 for standard 14-float layout)
214 const int vbStride = vertexFormat0 ? vertexFormat0->size() : 14 * static_cast<int>(sizeof(float));
215
216 // Instancing stride: 0 means no instancing layout in the vertex descriptor.
217 const int instStride = (instancingFormat && instancingFormat->isInstancing())
218 ? instancingFormat->size() : 0;
219
220 // Create a pipeline
221 auto cacheEntry = std::make_shared<CacheEntry>();
222 cacheEntry->hashes = _lookupHashes;
223 cacheEntry->pipeline = create(
224 primTopology, ibFormat, shader, renderTarget, pipelineLayout,
225 blendState, depthState, vbLayout, cullMode,
226 stencilEnabled, stencilFront, stencilBack,
227 vbStride, instStride
228 );
229 if (!cacheEntry->pipeline) {
230 spdlog::error("Render pipeline creation returned null");
231 return nullptr;
232 }
233
234 // Add to cache
235 if (it != _cache.end()) {
236 it->second.push_back(cacheEntry);
237 } else {
238 _cache[hash] = { cacheEntry };
239 }
240
241 return cacheEntry->pipeline;
242 }
243
244 MTL::RenderPipelineState* MetalRenderPipeline::create(const MTL::PrimitiveType primitiveTopology, int ibFormat,
245 const std::shared_ptr<Shader>& shader, const std::shared_ptr<RenderTarget>& renderTarget,
246 metal::PipelineLayout* pipelineLayout, std::shared_ptr<BlendState> blendState,
247 std::shared_ptr<DepthState> depthState, const std::vector<void*>& vertexBufferLayout,
248 CullMode cullMode, bool stencilEnabled, std::shared_ptr<StencilParameters> stencilFront,
249 std::shared_ptr<StencilParameters> stencilBack,
250 int vertexStride,
251 int instancingStride
252 )
253 {
254 auto* mtlDevice = _device->raw();
255 spdlog::trace("Creating Metal render pipeline");
256
257 MTL::RenderPipelineDescriptor* pipelineDescriptor = MTL::RenderPipelineDescriptor::alloc()->init();
258
259 NS::Error* error = nullptr;
260 MTL::Library* library = nullptr;
261 const NS::Bundle* bundle = NS::Bundle::mainBundle();
262 if (auto* metalShader = dynamic_cast<MetalShader*>(shader.get())) {
263 library = metalShader->getLibrary(mtlDevice, bundle, &error);
264 if (library) {
265 library->retain();
266 }
267 } else {
268 spdlog::error("Unsupported shader implementation for MetalRenderPipeline. Expected MetalShader.");
269 pipelineDescriptor->release();
270 return nullptr;
271 }
272
273 if (!library) {
274 spdlog::error("Failed to load Metal library: {}", error ? error->localizedDescription()->utf8String() : "unknown");
275 assert(false);
276 pipelineDescriptor->release();
277 return nullptr;
278 }
279
280 const auto& vertexEntry = shader->vertexEntry().empty() ? std::string("vertexShader") : shader->vertexEntry();
281 const auto& fragmentEntry = shader->fragmentEntry().empty() ? std::string("fragmentShader") : shader->fragmentEntry();
282 auto* vertexFunction = library->newFunction(NS::String::string(vertexEntry.c_str(), NS::UTF8StringEncoding));
283 auto* fragmentFunction = library->newFunction(NS::String::string(fragmentEntry.c_str(), NS::UTF8StringEncoding));
284 if (!vertexFunction || !fragmentFunction) {
285 spdlog::error("Failed to find required shader entry points ({} / {})", vertexEntry, fragmentEntry);
286 if (vertexFunction) vertexFunction->release();
287 if (fragmentFunction) fragmentFunction->release();
288 library->release();
289 pipelineDescriptor->release();
290 assert(false);
291 return nullptr;
292 }
293
294 pipelineDescriptor->setVertexFunction(vertexFunction);
295 pipelineDescriptor->setFragmentFunction(fragmentFunction);
296
297 if (const auto metalTarget = std::dynamic_pointer_cast<MetalRenderTarget>(renderTarget))
298 {
299 const auto colorAttachments = metalTarget->colorAttachments();
300
301 MTL::ColorWriteMask writeMask = MTL::ColorWriteMaskNone;
302 if (blendState->redWrite()) writeMask |= MTL::ColorWriteMaskRed;
303 if (blendState->greenWrite()) writeMask |= MTL::ColorWriteMaskGreen;
304 if (blendState->blueWrite()) writeMask |= MTL::ColorWriteMaskBlue;
305 if (blendState->alphaWrite()) writeMask |= MTL::ColorWriteMaskAlpha;
306
307 for (auto i = 0; i < colorAttachments.size(); ++i)
308 {
309 auto* colorAttachment = pipelineDescriptor->colorAttachments()->object(i);
310 colorAttachment->setPixelFormat(colorAttachments[i]->pixelFormat);
311 colorAttachment->setWriteMask(writeMask);
312
313 setBlend(colorAttachment, blendState);
314 }
315
316 // Set depth/stencil pixel format from the render target's depth attachment
317 if (const auto& depthAtt = metalTarget->depthAttachment()) {
318 pipelineDescriptor->setDepthAttachmentPixelFormat(depthAtt->pixelFormat);
319 if (depthAtt->hasStencil) {
320 pipelineDescriptor->setStencilAttachmentPixelFormat(depthAtt->pixelFormat);
321 }
322 }
323 } else {
324 // Back buffer: BGRA8 color + Depth32Float (always attached by startRenderPass)
325 auto* colorAttachment = pipelineDescriptor->colorAttachments()->object(0);
326 colorAttachment->setPixelFormat(MTL::PixelFormatBGRA8Unorm);
327 setBlend(colorAttachment, blendState);
328 pipelineDescriptor->setDepthAttachmentPixelFormat(MTL::PixelFormatDepth32Float);
329 }
330
331 _pipelineId++;
332 const std::string label = "RenderPipeline-" + std::to_string(_pipelineId);
333 pipelineDescriptor->setLabel(NS::String::string(label.c_str(), NS::UTF8StringEncoding));
334
335 MTL::VertexDescriptor* vertexDescriptor = MTL::VertexDescriptor::vertexDescriptor();
336 auto* attributes = vertexDescriptor->attributes();
337 auto* layouts = vertexDescriptor->layouts();
338
339 // Forward shader input contract (interleaved):
340 // attribute(0)=position(float3), attribute(1)=normal(float3),
341 // attribute(2)=uv0(float2), attribute(3)=tangent(float4), attribute(4)=uv1(float2).
342 // When vertex colors are enabled (stride == 72):
343 // attribute(5)=color(float4) follows uv1.
344 // Point cloud vertices (stride == 28):
345 // attribute(0)=position(float3)@0, attribute(5)=color(float4)@12.
346 // Unused attributes map to offset 0 (read harmless data from position).
347 constexpr int STRIDE_POINT_VERTEX = 7 * static_cast<int>(sizeof(float)); // 28
348 constexpr int STRIDE_DYNAMIC_BATCH = 15 * static_cast<int>(sizeof(float)); // 60
349 constexpr int STRIDE_WITH_COLOR = 18 * static_cast<int>(sizeof(float)); // 72
350
351 if (vertexStride <= STRIDE_POINT_VERTEX) {
352 // Point cloud vertex: position(float3)@0 + color(float4)@12.
353 // Unused attributes (normal, uv0, tangent, uv1) point to offset 0,
354 // reading harmless data from position. The unlit shader ignores them.
355 attributes->object(0)->setFormat(MTL::VertexFormatFloat3);
356 attributes->object(0)->setOffset(0);
357 attributes->object(0)->setBufferIndex(0);
358
359 attributes->object(1)->setFormat(MTL::VertexFormatFloat3);
360 attributes->object(1)->setOffset(0); // dummy: reads position
361 attributes->object(1)->setBufferIndex(0);
362
363 attributes->object(2)->setFormat(MTL::VertexFormatFloat2);
364 attributes->object(2)->setOffset(0); // dummy
365 attributes->object(2)->setBufferIndex(0);
366
367 attributes->object(3)->setFormat(MTL::VertexFormatFloat4);
368 attributes->object(3)->setOffset(0); // dummy
369 attributes->object(3)->setBufferIndex(0);
370
371 attributes->object(4)->setFormat(MTL::VertexFormatFloat2);
372 attributes->object(4)->setOffset(0); // dummy
373 attributes->object(4)->setBufferIndex(0);
374
375 attributes->object(5)->setFormat(MTL::VertexFormatFloat4);
376 attributes->object(5)->setOffset(3 * static_cast<NS::UInteger>(sizeof(float))); // color at offset 12
377 attributes->object(5)->setBufferIndex(0);
378 } else {
379 // Standard vertex layouts (56/60/72 bytes)
380 attributes->object(0)->setFormat(MTL::VertexFormatFloat3);
381 attributes->object(0)->setOffset(0);
382 attributes->object(0)->setBufferIndex(0);
383
384 attributes->object(1)->setFormat(MTL::VertexFormatFloat3);
385 attributes->object(1)->setOffset(3 * static_cast<NS::UInteger>(sizeof(float)));
386 attributes->object(1)->setBufferIndex(0);
387
388 attributes->object(2)->setFormat(MTL::VertexFormatFloat2);
389 attributes->object(2)->setOffset(6 * static_cast<NS::UInteger>(sizeof(float)));
390 attributes->object(2)->setBufferIndex(0);
391
392 attributes->object(3)->setFormat(MTL::VertexFormatFloat4);
393 attributes->object(3)->setOffset(8 * static_cast<NS::UInteger>(sizeof(float)));
394 attributes->object(3)->setBufferIndex(0);
395
396 attributes->object(4)->setFormat(MTL::VertexFormatFloat2);
397 attributes->object(4)->setOffset(12 * static_cast<NS::UInteger>(sizeof(float)));
398 attributes->object(4)->setBufferIndex(0);
399
400 // Dynamic batching: attribute(5) as Float1 (bone index) at offset 56 when stride is 60 bytes.
401 // Vertex colors: attribute(5) as Float4 at offset 56 when stride is 72 bytes.
402 // These are mutually exclusive (both use attribute 5).
403 if (vertexStride >= STRIDE_WITH_COLOR) {
404 attributes->object(5)->setFormat(MTL::VertexFormatFloat4);
405 attributes->object(5)->setOffset(14 * static_cast<NS::UInteger>(sizeof(float)));
406 attributes->object(5)->setBufferIndex(0);
407 } else if (vertexStride >= STRIDE_DYNAMIC_BATCH) {
408 attributes->object(5)->setFormat(MTL::VertexFormatFloat);
409 attributes->object(5)->setOffset(14 * static_cast<NS::UInteger>(sizeof(float)));
410 attributes->object(5)->setBufferIndex(0);
411 }
412 }
413
414 layouts->object(0)->setStride(static_cast<NS::UInteger>(vertexStride));
415 layouts->object(0)->setStepFunction(MTL::VertexStepFunctionPerVertex);
416 layouts->object(0)->setStepRate(1);
417
418 // Hardware instancing: set up vertex descriptor layout(5) with perInstance step function.
419 // instance_line1..4 (4x float4 for model matrix) + instanceColor (float4).
420 // These map to [[attribute(6)]]..[[attribute(10)]] in the shader's VertexData struct.
421 if (instancingStride > 0) {
422 constexpr NS::UInteger INST_BUFFER_INDEX = 5;
423 constexpr NS::UInteger INST_ATTR_BASE = 6;
424
425 for (NS::UInteger i = 0; i < 5; ++i) {
426 attributes->object(INST_ATTR_BASE + i)->setFormat(MTL::VertexFormatFloat4);
427 attributes->object(INST_ATTR_BASE + i)->setOffset(i * 4 * sizeof(float));
428 attributes->object(INST_ATTR_BASE + i)->setBufferIndex(INST_BUFFER_INDEX);
429 }
430
431 layouts->object(INST_BUFFER_INDEX)->setStride(static_cast<NS::UInteger>(instancingStride));
432 layouts->object(INST_BUFFER_INDEX)->setStepFunction(MTL::VertexStepFunctionPerInstance);
433 layouts->object(INST_BUFFER_INDEX)->setStepRate(1);
434 }
435
436 pipelineDescriptor->setVertexDescriptor(vertexDescriptor);
437
438 auto* pipeline = mtlDevice->newRenderPipelineState(pipelineDescriptor, &error);
439 if (!pipeline)
440 {
441 spdlog::error("Failed to create render pipeline state: {}", error->localizedDescription()->utf8String());
442 assert(false);
443 }
444
445 spdlog::trace("RenderPipelineAlloc | Alloc: Id " + std::to_string(_pipelineId));
446
447 vertexFunction->release();
448 fragmentFunction->release();
449 library->release();
450 pipelineDescriptor->release();
451
452 return pipeline;
453 }
454
455 void MetalRenderPipeline::setBlend(MTL::RenderPipelineColorAttachmentDescriptor* colorAttachment,
456 const std::shared_ptr<BlendState>& blendState)
457 {
458 if (!blendState->enabled()) {
459 return;
460 }
461
462 colorAttachment->setBlendingEnabled(true);
463
464 colorAttachment->setRgbBlendOperation(blendOperation[blendState->colorOp()]);
465 colorAttachment->setSourceRGBBlendFactor(blendFactor[blendState->colorSrcFactor()]);
466 colorAttachment->setDestinationRGBBlendFactor(blendFactor[blendState->colorDstFactor()]);
467
468 colorAttachment->setAlphaBlendOperation(blendOperation[blendState->alphaOp()]);
469 colorAttachment->setSourceAlphaBlendFactor(blendFactor[blendState->alphaSrcFactor()]);
470 colorAttachment->setDestinationAlphaBlendFactor(blendFactor[blendState->alphaDstFactor()]);
471 }
472}
MetalPipeline(const MetalGraphicsDevice *device)
metal::PipelineLayout * getPipelineLayout(const std::vector< std::shared_ptr< MetalBindGroupFormat > > &bindGroupFormats)
const MetalGraphicsDevice * _device
MetalRenderPipeline(const MetalGraphicsDevice *device)
MTL::RenderPipelineState * get(const Primitive &primitive, const std::shared_ptr< VertexFormat > &vertexFormat0, const std::shared_ptr< VertexFormat > &vertexFormat1, int ibFormat, const std::shared_ptr< Shader > &shader, const std::shared_ptr< RenderTarget > &renderTarget, const std::vector< std::shared_ptr< MetalBindGroupFormat > > &bindGroupFormats, const std::shared_ptr< BlendState > &blendState, const std::shared_ptr< DepthState > &depthState, CullMode cullMode, bool stencilEnabled, const std::shared_ptr< StencilParameters > &stencilFront, const std::shared_ptr< StencilParameters > &stencilBack, const std::shared_ptr< VertexFormat > &instancingFormat=nullptr)
uint32_t hash32Fnv1a(const uint32_t *array, size_t length)
Definition utils.cpp:10
@ PRIMITIVE_LINESTRIP
Definition mesh.h:22
@ PRIMITIVE_TRISTRIP
Definition mesh.h:24
@ PRIMITIVE_TRIANGLES
Definition mesh.h:23
Describes how vertex and index data should be interpreted for a draw call.
Definition mesh.h:33
PrimitiveType type
Definition mesh.h:34