Blender V5.0
mtl_batch.mm
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "BLI_assert.h"
12#include "BLI_span.hh"
13
14#include "BKE_global.hh"
15
16#include "GPU_batch.hh"
17#include "GPU_common.hh"
18#include "gpu_shader_private.hh"
19
20#include "mtl_batch.hh"
21#include "mtl_context.hh"
22#include "mtl_debug.hh"
23#include "mtl_index_buffer.hh"
24#include "mtl_shader.hh"
25#include "mtl_storage_buffer.hh"
26#include "mtl_vertex_buffer.hh"
27
28#include <string>
29
30namespace blender::gpu {
31
32/* -------------------------------------------------------------------- */
35void MTLBatch::draw(int v_first, int v_count, int i_first, int i_count)
36{
37 this->draw_advanced(v_first, v_count, i_first, i_count);
38}
39
40void MTLBatch::draw_indirect(StorageBuf *indirect_buf, intptr_t offset)
41{
42 this->draw_advanced_indirect(indirect_buf, offset);
43}
44
45void MTLBatch::MTLVertexDescriptorCache::vertex_descriptor_cache_init(MTLContext *ctx)
46{
47 BLI_assert(ctx != nullptr);
48 this->vertex_descriptor_cache_clear();
49 cache_context_ = ctx;
50}
51
52void MTLBatch::MTLVertexDescriptorCache::vertex_descriptor_cache_clear()
53{
54 cache_life_index_++;
55 cache_context_ = nullptr;
56}
57
58void MTLBatch::MTLVertexDescriptorCache::vertex_descriptor_cache_ensure()
59{
60 if (this->cache_context_ != nullptr) {
61
62 /* Invalidate vertex descriptor bindings cache if batch has changed. */
63 if (batch_->flag & GPU_BATCH_DIRTY) {
64 batch_->flag &= ~GPU_BATCH_DIRTY;
65 this->vertex_descriptor_cache_clear();
66 }
67 }
68
69 /* Initialize cache if not ready. */
70 if (cache_context_ == nullptr) {
71 this->vertex_descriptor_cache_init(MTLContext::get());
72 }
73}
74
75MTLBatch::VertexDescriptorShaderInterfacePair *MTLBatch::MTLVertexDescriptorCache::find(
76 const ShaderInterface *interface)
77{
78 this->vertex_descriptor_cache_ensure();
79 for (int i = 0; i < GPU_VAO_STATIC_LEN; ++i) {
80 if (cache_[i].interface == interface && cache_[i].cache_life_index == cache_life_index_) {
81 return &cache_[i];
82 }
83 }
84 return nullptr;
85}
86
87bool MTLBatch::MTLVertexDescriptorCache::insert(
88 MTLBatch::VertexDescriptorShaderInterfacePair &data)
89{
90 vertex_descriptor_cache_ensure();
91 for (int i = 0; i < GPU_VAO_STATIC_LEN; ++i) {
92 if (cache_[i].interface == nullptr || cache_[i].cache_life_index != cache_life_index_) {
93 cache_[i] = data;
94 cache_[i].cache_life_index = cache_life_index_;
95 return true;
96 }
97 }
98 return false;
99}
100
101int MTLBatch::prepare_vertex_binding(MTLVertBuf *verts,
102 MTLRenderPipelineStateDescriptor &desc,
103 const MTLShaderInterface *interface,
104 uint16_t &attr_mask)
105{
106
107 const GPUVertFormat *format = &verts->format;
108 /* Whether the current vertex buffer has been added to the buffer layout descriptor. */
109 bool buffer_added = false;
110 /* Per-vertex stride of current vertex buffer. */
111 int buffer_stride = format->stride;
112 /* Buffer binding index of the vertex buffer once added to the buffer layout descriptor. */
113 int buffer_index = -1;
114 int attribute_offset = 0;
115
116 /* Iterate over VertBuf vertex format and find attributes matching those in the active
117 * shader's interface. */
118 for (uint32_t a_idx = 0; a_idx < format->attr_len; a_idx++) {
119 const GPUVertAttr *a = &format->attrs[a_idx];
120
121 if (format->deinterleaved) {
122 attribute_offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].type.size()) *
123 verts->vertex_len;
124 buffer_stride = a->type.size();
125 }
126 else {
127 attribute_offset = a->offset;
128 }
129
130 /* Find attribute with the matching name. Attributes may have multiple compatible
131 * name aliases. */
132 for (uint32_t n_idx = 0; n_idx < a->name_len; n_idx++) {
133 const char *name = GPU_vertformat_attr_name_get(format, a, n_idx);
134 const ShaderInput *input = interface->attr_get(name);
135
136 if (input == nullptr || input->location == -1) {
137 continue;
138 }
139
140 /* Fetch metal attribute information (ShaderInput->binding is used to fetch the corresponding
141 * slot. */
142 const MTLShaderInputAttribute &mtl_attr = interface->get_attribute(input->binding);
143 BLI_assert(mtl_attr.location >= 0);
144 /* Verify that the attribute location from the shader interface
145 * matches the attribute location returned in the input table. These should always be the
146 * same. */
147 BLI_assert(mtl_attr.location == input->location);
148
149 /* Check if attribute is already present in the given slot. */
150 if ((~attr_mask) & (1 << mtl_attr.location)) {
152 " -- [Batch] Skipping attribute with input location %d (As one is already bound)",
153 mtl_attr.location);
154 }
155 else {
156
157 /* Update attribute used-slot mask. */
158 attr_mask &= ~(1 << mtl_attr.location);
159
160 /* Add buffer layout entry in descriptor if it has not yet been added
161 * for current vertex buffer. */
162 if (!buffer_added) {
163 buffer_index = desc.vertex_descriptor.num_vert_buffers;
164 desc.vertex_descriptor.buffer_layouts[buffer_index].step_function =
165 MTLVertexStepFunctionPerVertex;
166 desc.vertex_descriptor.buffer_layouts[buffer_index].step_rate = 1;
167 desc.vertex_descriptor.buffer_layouts[buffer_index].stride = buffer_stride;
168 desc.vertex_descriptor.num_vert_buffers++;
169 buffer_added = true;
170
171 MTL_LOG_DEBUG(" -- [Batch] Adding source vertex buffer (Index: %d, Stride: %d)",
172 buffer_index,
173 buffer_stride);
174 }
175 else {
176 /* Ensure stride is correct for de-interleaved attributes. */
177 desc.vertex_descriptor.buffer_layouts[buffer_index].stride = buffer_stride;
178 }
179
180 {
181 /* Handle Any required format conversions.
182 * NOTE(Metal): If there is a mismatch between the format of an attribute
183 * in the shader interface, and the specified format in the VertexBuffer VertexFormat,
184 * we need to perform a format conversion.
185 *
186 * The Metal API can perform certain conversions internally during vertex assembly:
187 * - Type Normalization e.g short2 to float2 between 0.0 to 1.0.
188 * - Type Truncation e.g. Float4 to Float2.
189 * - Type expansion e,g, Float3 to Float4 (Following 0,0,0,1 for assignment to empty
190 * elements).
191 *
192 * Certain conversion cannot be performed however, and in these cases, we need to
193 * instruct the shader to generate a specialized version with a conversion routine upon
194 * attribute read.
195 * - This handles cases such as conversion between types e.g. Integer to float without
196 * normalization.
197 *
198 * For more information on the supported and unsupported conversions, see:
199 * https://developer.apple.com/documentation/metal/mtlvertexattributedescriptor/1516081-format?language=objc
200 */
201 MTLVertexFormat converted_format;
202 bool can_use_internal_conversion = mtl_convert_vertex_format(mtl_attr.format,
203 a->type.comp_type(),
204 a->type.comp_len(),
205 a->type.fetch_mode(),
206 &converted_format);
207 bool is_floating_point_format = (a->type.comp_type() == GPU_COMP_F32);
208
209 if (can_use_internal_conversion) {
210 desc.vertex_descriptor.attributes[mtl_attr.location].format = converted_format;
211 desc.vertex_descriptor.attributes[mtl_attr.location].format_conversion_mode =
212 is_floating_point_format ? (GPUVertFetchMode)GPU_FETCH_FLOAT :
214 BLI_assert(converted_format != MTLVertexFormatInvalid);
215 }
216 else {
217 /* The internal implicit conversion is not supported.
218 * In this case, we need to handle conversion inside the shader.
219 * This is handled using `format_conversion_mode`.
220 * `format_conversion_mode` is assigned the blender-specified fetch mode (GPU_FETCH_*).
221 * This then controls how a given attribute is interpreted. The data will be read
222 * as specified and then converted appropriately to the correct form.
223 *
224 * e.g. if `GPU_FETCH_INT_TO_FLOAT` is specified, the specialized read-routine
225 * in the shader will read the data as an int, and cast this to floating point
226 * representation. (Rather than reading the source data as float).
227 *
228 * NOTE: Even if full conversion is not supported, we may still partially perform an
229 * implicit conversion where possible, such as vector truncation or expansion. */
230 MTLVertexFormat converted_format = format_resize_comp(mtl_attr.format,
231 a->type.comp_len());
232 desc.vertex_descriptor.attributes[mtl_attr.location].format = converted_format;
233 desc.vertex_descriptor.attributes[mtl_attr.location].format_conversion_mode =
234 a->type.fetch_mode();
235 BLI_assert(desc.vertex_descriptor.attributes[mtl_attr.location].format !=
236 MTLVertexFormatInvalid);
237 }
238 desc.vertex_descriptor.attributes[mtl_attr.location].offset = attribute_offset;
239 desc.vertex_descriptor.attributes[mtl_attr.location].buffer_index = buffer_index;
240 desc.vertex_descriptor.max_attribute_value =
241 ((mtl_attr.location) > desc.vertex_descriptor.max_attribute_value) ?
242 (mtl_attr.location) :
243 desc.vertex_descriptor.max_attribute_value;
244 desc.vertex_descriptor.total_attributes++;
245
246 /* NOTE: We are setting max_attribute_value to be up to the maximum found index, because
247 * of this, it is possible that we may skip over certain attributes if they were not in
248 * the source GPUVertFormat. */
250 " -- Batch Attribute(%d): ORIG Shader Format: %d, ORIG Vert format: %d, Vert "
251 "components: %d, Fetch Mode %d --> FINAL FORMAT: %d",
252 mtl_attr.location,
253 (int)mtl_attr.format,
254 (int)a->type.comp_type(),
255 (int)a->type.comp_len(),
256 (int)a->type.fetch_mode(),
257 (int)desc.vertex_descriptor.attributes[mtl_attr.location].format);
258
260 " -- [Batch] matching vertex attribute '%s' (Attribute Index: %d, Buffer index: "
261 "%d, "
262 "offset: %d)",
263 name,
264 mtl_attr.location,
265 buffer_index,
266 attribute_offset);
267 }
268 }
269 }
270 }
271 if (buffer_added) {
272 return buffer_index;
273 }
274 return -1;
275}
276
277id<MTLRenderCommandEncoder> MTLBatch::bind()
278{
279 /* Setup draw call and render pipeline state here. Called by every draw, but setup here so that
280 * MTLDrawList only needs to perform setup a single time. */
281 BLI_assert(this);
282
283 /* Fetch Metal device. */
285 if (!ctx) {
286 BLI_assert_msg(false, "No context available for rendering.");
287 return nil;
288 }
289
290 /* Fetch bound shader from context. */
291 active_shader_ = static_cast<MTLShader *>(ctx->shader);
292
293 if (active_shader_ == nullptr || !active_shader_->is_valid()) {
294 /* Skip drawing if there is no valid Metal shader.
295 * This will occur if the path through which the shader is prepared
296 * is invalid (e.g. Python without create-info), or, the source shader uses a geometry pass. */
297 BLI_assert_msg(false, "No valid Metal shader!");
298 return nil;
299 }
300
301 /* Prepare Vertex Descriptor and extract VertexBuffers to bind. */
303 int num_buffers = 0;
304
305 /* Ensure Index Buffer is ready. */
306 MTLIndexBuf *mtl_elem = static_cast<MTLIndexBuf *>(reinterpret_cast<IndexBuf *>(this->elem));
307 if (mtl_elem != nullptr) {
308 mtl_elem->upload_data();
309 }
310
311 /* Populate vertex descriptor with attribute binding information.
312 * The vertex descriptor and buffer layout descriptors describe
313 * how vertex data from bound vertex buffers maps to the
314 * shader's input.
315 * A unique vertex descriptor will result in a new PipelineStateObject
316 * being generated for the currently bound shader. */
317 prepare_vertex_descriptor_and_bindings(buffers, num_buffers);
318
319 /* Prepare Vertex Buffers - Run before RenderCommandEncoder in case BlitCommandEncoder buffer
320 * data operations are required. */
321 for (int i = 0; i < num_buffers; i++) {
322 MTLVertBuf *buf_at_index = buffers[i];
323 if (buf_at_index == nullptr) {
325 false,
326 "Total buffer count does not match highest buffer index, could be gaps in bindings");
327 continue;
328 }
329
330 MTLVertBuf *mtlvbo = static_cast<MTLVertBuf *>(reinterpret_cast<VertBuf *>(buf_at_index));
331 mtlvbo->bind();
332 }
333
334 /* Ensure render pass is active and fetch active RenderCommandEncoder. */
335 id<MTLRenderCommandEncoder> rec = ctx->ensure_begin_render_pass();
336
337 /* Fetch RenderPassState to enable resource binding for active pass. */
339
340 /* Debug Check: Ensure Frame-buffer instance is not dirty. */
342
343 /* GPU debug markers. */
344 if (G.debug & G_DEBUG_GPU) {
345 [rec pushDebugGroup:[NSString stringWithFormat:@"Draw Commands%@ (Shader: %s)",
346 this->elem ? @"(indexed)" : @"",
347 active_shader_->get_interface()->get_name()]];
348 [rec insertDebugSignpost:[NSString
349 stringWithFormat:@"Draw Commands %@ (Shader: %s)",
350 this->elem ? @"(indexed)" : @"",
351 active_shader_->get_interface()->get_name()]];
352 }
353
354 /*** Bind Vertex Buffers and Index Buffers **/
355
356 /* Ensure Context Render Pipeline State is fully setup and ready to execute the draw.
357 * This should happen after all other final rendering setup is complete. */
358 MTLPrimitiveType mtl_prim_type = gpu_prim_type_to_metal(this->prim_type);
359 if (!ctx->ensure_render_pipeline_state(mtl_prim_type)) {
360 MTL_LOG_ERROR("Failed to prepare and apply render pipeline state.");
361 BLI_assert(false);
362 return nil;
363 }
364
365 /* Bind Vertex Buffers. */
366 for (int i = 0; i < num_buffers; i++) {
367 MTLVertBuf *buf_at_index = buffers[i];
368 if (buf_at_index == nullptr) {
370 false,
371 "Total buffer count does not match highest buffer index, could be gaps in bindings");
372 continue;
373 }
374 /* Buffer handle. */
375 MTLVertBuf *mtlvbo = static_cast<MTLVertBuf *>(reinterpret_cast<VertBuf *>(buf_at_index));
376 mtlvbo->flag_used();
377
378 /* Fetch buffer from MTLVertexBuffer and bind. */
379 id<MTLBuffer> mtl_buffer = mtlvbo->get_metal_buffer();
380
381 BLI_assert(mtl_buffer != nil);
382 rps.bind_vertex_buffer(mtl_buffer, 0, i);
383 }
384
385 /* Return Render Command Encoder used with setup. */
386 return rec;
387}
388
389void MTLBatch::unbind(id<MTLRenderCommandEncoder> rec)
390{
391 /* Pop bind debug group. */
392 if (G.debug & G_DEBUG_GPU) {
393 [rec popDebugGroup];
394 }
395}
396
397void MTLBatch::prepare_vertex_descriptor_and_bindings(MTLVertBuf **buffers, int &num_buffers)
398{
399
400 /* Here we populate the MTLContext vertex descriptor and resolve which buffers need to be bound.
401 */
402 MTLStateManager *state_manager = static_cast<MTLStateManager *>(
405 const MTLShaderInterface *interface = active_shader_->get_interface();
406 uint16_t attr_mask = interface->get_enabled_attribute_mask();
407
408 /* Reset vertex descriptor to default state. */
410
411 /* Fetch Vertex Buffers. */
412 Span<MTLVertBuf *> mtl_verts(reinterpret_cast<MTLVertBuf **>(this->verts),
414
415 /* Resolve Metal vertex buffer bindings. */
416 /* Vertex Descriptors
417 * ------------------
418 * Vertex Descriptors are required to generate a pipeline state, based on the current Batch's
419 * buffer bindings. These bindings are a unique matching, depending on what input attributes a
420 * batch has in its buffers, and those which are supported by the shader interface.
421 *
422 * We iterate through the buffers and resolve which attributes satisfy the requirements of the
423 * currently bound shader. We cache this data, for a given Batch<->ShderInterface pairing in a
424 * VAO cache to avoid the need to recalculate this data. */
425
426 VertexDescriptorShaderInterfacePair *descriptor = this->vao_cache.find(interface);
427 if (descriptor) {
428 desc.vertex_descriptor = descriptor->vertex_descriptor;
429 attr_mask = descriptor->attr_mask;
430 num_buffers = descriptor->num_buffers;
431
432 for (int bid = 0; bid < GPU_BATCH_VBO_MAX_LEN; ++bid) {
433 if (descriptor->bufferIds[bid].used) {
434 buffers[bid] = mtl_verts[descriptor->bufferIds[bid].id];
435 }
436 }
437 }
438 else {
439 VertexDescriptorShaderInterfacePair pair{};
440 pair.interface = interface;
441
442 for (int i = 0; i < GPU_BATCH_VBO_MAX_LEN; ++i) {
443 pair.bufferIds[i].id = -1;
444 pair.bufferIds[i].used = 0;
445 }
446 /* NOTE: Attribute extraction order from buffer is the reverse of the OpenGL as we flag once an
447 * attribute is found, rather than pre-setting the mask. */
448
449 /* Extract Vertex attributes (First-bound vertex buffer takes priority). */
450 for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
451 if (mtl_verts[v] != nullptr) {
452 MTL_LOG_DEBUG(" -- [Batch] Checking bindings for bound vertex buffer %p", mtl_verts[v]);
453 int buffer_ind = this->prepare_vertex_binding(mtl_verts[v], desc, interface, attr_mask);
454 if (buffer_ind >= 0) {
455 buffers[buffer_ind] = mtl_verts[v];
456
457 pair.bufferIds[buffer_ind].id = v;
458 pair.bufferIds[buffer_ind].used = 1;
459 num_buffers = ((buffer_ind + 1) > num_buffers) ? (buffer_ind + 1) : num_buffers;
460 }
461 }
462 }
463
464 /* Add to VertexDescriptor cache */
465 pair.attr_mask = attr_mask;
466 pair.vertex_descriptor = desc.vertex_descriptor;
467 pair.num_buffers = num_buffers;
468 if (!this->vao_cache.insert(pair)) {
469 printf(
470 "[Performance Warning] cache is full (Size: %d), vertex descriptor will not be cached\n",
472 }
473 }
474
475/* DEBUG: verify if our attribute bindings have been fully provided as expected. */
476#if MTL_DEBUG_SHADER_ATTRIBUTES == 1
477 if (attr_mask != 0) {
478 /* Attributes are not necessarily contiguous. */
479 for (int i = 0; i < active_shader_->get_interface()->get_total_attributes(); i++) {
480 const MTLShaderInputAttribute &attr = active_shader_->get_interface()->get_attribute(i);
481 if (attr_mask & (1 << attr.location)) {
483 "Warning: Missing expected attribute '%s' with location: %u in shader %s (attr "
484 "number: %u)",
485 active_shader_->get_interface()->get_name_at_offset(attr.name_offset),
486 attr.location,
487 active_shader_->name_get(),
488 i);
489
490 /* If an attribute is not included, then format in vertex descriptor should be invalid due
491 * to nil assignment. */
492 BLI_assert(desc.vertex_descriptor.attributes[attr.location].format ==
493 MTLVertexFormatInvalid);
494 }
495 }
496 }
497#endif
498}
499
500void MTLBatch::draw_advanced(int v_first, int v_count, int i_first, int i_count)
501{
502 BLI_assert(v_count > 0 && i_count > 0);
503
504 /* Setup RenderPipelineState for batch. */
505 MTLContext *ctx = MTLContext::get();
506 id<MTLRenderCommandEncoder> rec = this->bind();
507 if (rec == nil) {
508 /* End of draw. */
509 this->unbind(rec);
510 return;
511 }
512
513 /* Fetch IndexBuffer and resolve primitive type. */
514 MTLIndexBuf *mtl_elem = static_cast<MTLIndexBuf *>(reinterpret_cast<IndexBuf *>(this->elem));
515 MTLPrimitiveType mtl_prim_type = gpu_prim_type_to_metal(this->prim_type);
516
517 /* Perform regular draw. */
518 if (mtl_elem == nullptr) {
519 /* Primitive Type topology emulation. */
520 if (mtl_needs_topology_emulation(this->prim_type)) {
521 /* Generate index buffer for primitive types requiring emulation. */
522 GPUPrimType emulated_prim_type = this->prim_type;
523 uint32_t emulated_v_count = v_count;
524 id<MTLBuffer> generated_index_buffer = this->get_emulated_toplogy_buffer(emulated_prim_type,
525 emulated_v_count);
526 BLI_assert(generated_index_buffer != nil);
527
528 MTLPrimitiveType emulated_mtl_prim_type = gpu_prim_type_to_metal(emulated_prim_type);
529
530 /* Temp: Disable culling for emulated primitive types.
531 * TODO(Metal): Support face winding in topology buffer. */
532 [rec setCullMode:MTLCullModeNone];
533
534 if (generated_index_buffer != nil) {
535 BLI_assert(emulated_mtl_prim_type == MTLPrimitiveTypeTriangle ||
536 emulated_mtl_prim_type == MTLPrimitiveTypeLine);
537 if (emulated_mtl_prim_type == MTLPrimitiveTypeTriangle) {
538 BLI_assert(emulated_v_count % 3 == 0);
539 }
540 if (emulated_mtl_prim_type == MTLPrimitiveTypeLine) {
541 BLI_assert(emulated_v_count % 2 == 0);
542 }
543
544 /* Set depth stencil state (requires knowledge of primitive type). */
545 ctx->ensure_depth_stencil_state(emulated_mtl_prim_type);
546
547 [rec drawIndexedPrimitives:emulated_mtl_prim_type
548 indexCount:emulated_v_count
549 indexType:MTLIndexTypeUInt32
550 indexBuffer:generated_index_buffer
551 indexBufferOffset:0
552 instanceCount:i_count
553 baseVertex:v_first
554 baseInstance:i_first];
555 }
556 else {
557 printf("[Note] Cannot draw batch -- Emulated Topology mode: %u not yet supported\n",
558 this->prim_type);
559 }
560 }
561 else {
562 /* Set depth stencil state (requires knowledge of primitive type). */
563 ctx->ensure_depth_stencil_state(mtl_prim_type);
564
565 /* Issue draw call. */
566 [rec drawPrimitives:mtl_prim_type
567 vertexStart:v_first
568 vertexCount:v_count
569 instanceCount:i_count
570 baseInstance:i_first];
571 }
572 ctx->main_command_buffer.register_draw_counters(v_count * i_count);
573 }
574 /* Perform indexed draw. */
575 else {
576
577 MTLIndexType index_type = MTLIndexBuf::gpu_index_type_to_metal(mtl_elem->index_type_);
578 uint32_t base_index = mtl_elem->index_base_;
579 uint32_t index_size = (mtl_elem->index_type_ == GPU_INDEX_U16) ? 2 : 4;
580 uint32_t v_first_ofs = ((v_first + mtl_elem->index_start_) * index_size);
581 BLI_assert_msg((v_first_ofs % index_size) == 0,
582 "Index offset is not 2/4-byte aligned as per METAL spec");
583
584 /* Fetch index buffer. May return an index buffer of a differing format,
585 * if index buffer optimization is used. In these cases, final_prim_type and
586 * index_count get updated with the new properties. */
587 GPUPrimType final_prim_type = this->prim_type;
588 uint index_count = v_count;
589
590 id<MTLBuffer> index_buffer = mtl_elem->get_index_buffer(final_prim_type, index_count);
591 mtl_prim_type = gpu_prim_type_to_metal(final_prim_type);
592 BLI_assert(index_buffer != nil);
593
594 if (index_buffer != nil) {
595
596 /* Set depth stencil state (requires knowledge of primitive type). */
597 ctx->ensure_depth_stencil_state(mtl_prim_type);
598
599 /* Issue draw call. */
600 [rec drawIndexedPrimitives:mtl_prim_type
601 indexCount:index_count
602 indexType:index_type
603 indexBuffer:index_buffer
604 indexBufferOffset:v_first_ofs
605 instanceCount:i_count
606 baseVertex:base_index
607 baseInstance:i_first];
608 ctx->main_command_buffer.register_draw_counters(index_count * i_count);
609 }
610 else {
611 BLI_assert_msg(false, "Index buffer does not have backing Metal buffer");
612 }
613 }
614
615 /* End of draw. */
616 this->unbind(rec);
617}
618
619void MTLBatch::draw_advanced_indirect(StorageBuf *indirect_buf, intptr_t offset)
620{
621 /* Setup RenderPipelineState for batch. */
622 MTLContext *ctx = MTLContext::get();
623 id<MTLRenderCommandEncoder> rec = this->bind();
624 if (rec == nil) {
625 printf("Failed to open Render Command encoder for DRAW INDIRECT\n");
626
627 /* End of draw. */
628 this->unbind(rec);
629 return;
630 }
631
632 /* Fetch indirect buffer Metal handle. */
633 MTLStorageBuf *mtlssbo = static_cast<MTLStorageBuf *>(indirect_buf);
634 id<MTLBuffer> mtl_indirect_buf = mtlssbo->get_metal_buffer();
635 BLI_assert(mtl_indirect_buf != nil);
636 if (mtl_indirect_buf == nil) {
637 MTL_LOG_WARNING("Metal Indirect Draw Storage Buffer is nil.");
638
639 /* End of draw. */
640 this->unbind(rec);
641 return;
642 }
643
644 /* Unsupported primitive type check. */
645 BLI_assert_msg(this->prim_type != GPU_PRIM_TRI_FAN,
646 "TriangleFan is not supported in Metal for Indirect draws.");
647
648 /* Fetch IndexBuffer and resolve primitive type. */
649 MTLIndexBuf *mtl_elem = static_cast<MTLIndexBuf *>(reinterpret_cast<IndexBuf *>(this->elem));
650 MTLPrimitiveType mtl_prim_type = gpu_prim_type_to_metal(this->prim_type);
651
652 if (mtl_needs_topology_emulation(this->prim_type)) {
653 BLI_assert_msg(false, "Metal Topology emulation unsupported for draw indirect.\n");
654
655 /* End of draw. */
656 this->unbind(rec);
657 return;
658 }
659
660 if (mtl_elem == nullptr) {
661 /* Set depth stencil state (requires knowledge of primitive type). */
662 ctx->ensure_depth_stencil_state(mtl_prim_type);
663
664 /* Issue draw call. */
665 [rec drawPrimitives:mtl_prim_type indirectBuffer:mtl_indirect_buf indirectBufferOffset:offset];
666 ctx->main_command_buffer.register_draw_counters(1);
667 }
668 else {
669 /* Fetch index buffer. May return an index buffer of a differing format,
670 * if index buffer optimization is used. In these cases, final_prim_type and
671 * index_count get updated with the new properties. */
672 MTLIndexType index_type = MTLIndexBuf::gpu_index_type_to_metal(mtl_elem->index_type_);
673 GPUPrimType final_prim_type = this->prim_type;
674 uint index_count = 0;
675
676 /* Disable index optimization for indirect draws. */
677 mtl_elem->flag_can_optimize(false);
678
679 id<MTLBuffer> index_buffer = mtl_elem->get_index_buffer(final_prim_type, index_count);
680 mtl_prim_type = gpu_prim_type_to_metal(final_prim_type);
681 BLI_assert(index_buffer != nil);
682
683 if (index_buffer != nil) {
684
685 /* Set depth stencil state (requires knowledge of primitive type). */
686 ctx->ensure_depth_stencil_state(mtl_prim_type);
687
688 /* Issue draw call. */
689 [rec drawIndexedPrimitives:mtl_prim_type
690 indexType:index_type
691 indexBuffer:index_buffer
692 indexBufferOffset:0
693 indirectBuffer:mtl_indirect_buf
694 indirectBufferOffset:offset];
695 ctx->main_command_buffer.register_draw_counters(1);
696 }
697 else {
698 BLI_assert_msg(false, "Index buffer does not have backing Metal buffer");
699 }
700 }
701
702 /* End of draw. */
703 this->unbind(rec);
704}
705
707
708/* -------------------------------------------------------------------- */
711
712id<MTLBuffer> MTLBatch::get_emulated_toplogy_buffer(GPUPrimType &in_out_prim_type,
713 uint32_t &in_out_v_count)
714{
715
716 BLI_assert(in_out_v_count > 0);
717 /* Determine emulated primitive types. */
718 GPUPrimType input_prim_type = in_out_prim_type;
719 uint32_t v_count = in_out_v_count;
720 GPUPrimType output_prim_type;
721 switch (input_prim_type) {
722 case GPU_PRIM_POINTS:
723 case GPU_PRIM_LINES:
724 case GPU_PRIM_TRIS:
725 BLI_assert_msg(false, "Optimal primitive types should not reach here.");
726 return nil;
727 break;
730 BLI_assert_msg(false, "Adjacency primitive types should not reach here.");
731 return nil;
732 break;
736 output_prim_type = GPU_PRIM_LINES;
737 break;
739 case GPU_PRIM_TRI_FAN:
740 output_prim_type = GPU_PRIM_TRIS;
741 break;
742 default:
743 BLI_assert_msg(false, "Invalid primitive type.");
744 return nil;
745 }
746
747 /* Check if topology buffer exists and is valid. */
748 if (this->emulated_topology_buffer_ != nullptr &&
749 (emulated_topology_type_ != input_prim_type || topology_buffer_input_v_count_ != v_count))
750 {
751
752 /* Release existing topology buffer. */
753 emulated_topology_buffer_->free();
754 emulated_topology_buffer_ = nullptr;
755 }
756
757 /* Generate new topology index buffer. */
758 if (this->emulated_topology_buffer_ == nullptr) {
759 /* Calculate IB len. */
760 uint32_t output_prim_count = 0;
761 switch (input_prim_type) {
764 output_prim_count = v_count - 1;
765 break;
767 output_prim_count = v_count;
768 break;
770 case GPU_PRIM_TRI_FAN:
771 output_prim_count = v_count - 2;
772 break;
773 default:
774 BLI_assert_msg(false, "Cannot generate optimized topology buffer for other types.");
775 break;
776 }
777 uint32_t output_IB_elems = output_prim_count * ((output_prim_type == GPU_PRIM_TRIS) ? 3 : 2);
778
779 /* Allocate buffer. */
780 uint32_t buffer_bytes = output_IB_elems * 4;
781 BLI_assert(buffer_bytes > 0);
782 this->emulated_topology_buffer_ = MTLContext::get_global_memory_manager()->allocate(
783 buffer_bytes, true);
784
785 /* Populate. */
786 uint32_t *data = (uint32_t *)this->emulated_topology_buffer_->get_host_ptr();
787 BLI_assert(data != nullptr);
788
789 /* TODO(Metal): Support inverse winding modes. */
790 bool winding_clockwise = false;
791 UNUSED_VARS(winding_clockwise);
792
793 switch (input_prim_type) {
794 /* Line Loop. */
795 case GPU_PRIM_LINE_LOOP: {
796 int line = 0;
797 for (line = 0; line < output_prim_count - 1; line++) {
798 data[line * 2 + 0] = line + 0;
799 data[line * 2 + 1] = line + 1;
800 }
801 /* Closing line. */
802 data[line * 2 + 0] = line + 0;
803 data[line * 2 + 1] = 0;
804 } break;
805
806 /* Triangle Fan. */
807 case GPU_PRIM_TRI_FAN: {
808 for (int triangle = 0; triangle < output_prim_count; triangle++) {
809 data[triangle * 3 + 0] = 0; /* Always 0 */
810 data[triangle * 3 + 1] = triangle + 1;
811 data[triangle * 3 + 2] = triangle + 2;
812 }
813 } break;
814
815 default:
816 BLI_assert_msg(false, "Other primitive types do not require emulation.");
817 return nil;
818 }
819
820 /* Flush. */
821 this->emulated_topology_buffer_->flush();
822 /* Assign members relating to current cached IB. */
823 topology_buffer_input_v_count_ = v_count;
824 topology_buffer_output_v_count_ = output_IB_elems;
825 emulated_topology_type_ = input_prim_type;
826 }
827
828 /* Return. */
829 in_out_v_count = topology_buffer_output_v_count_;
830 in_out_prim_type = output_prim_type;
831 return (emulated_topology_buffer_) ? emulated_topology_buffer_->get_metal_buffer() : nil;
832}
833
835
836} // namespace blender::gpu
@ G_DEBUG_GPU
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
unsigned int uint
#define UNUSED_VARS(...)
static constexpr int GPU_BATCH_VBO_MAX_LEN
Definition GPU_batch.hh:33
@ GPU_BATCH_DIRTY
Definition GPU_batch.hh:53
GPUPrimType
@ GPU_PRIM_TRI_FAN
@ GPU_PRIM_LINE_LOOP
@ GPU_PRIM_LINE_STRIP_ADJ
@ GPU_PRIM_TRIS_ADJ
@ GPU_PRIM_LINES
@ GPU_PRIM_POINTS
@ GPU_PRIM_LINES_ADJ
@ GPU_PRIM_LINE_STRIP
@ GPU_PRIM_TRI_STRIP
@ GPU_PRIM_TRIS
BLI_INLINE const char * GPU_vertformat_attr_name_get(const GPUVertFormat *format, const GPUVertAttr *attr, uint n_idx)
GPUVertFetchMode
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT
@ GPU_COMP_F32
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v
void draw_indirect(StorageBuf *indirect_buf, intptr_t offset) override
Definition mtl_batch.mm:40
id< MTLRenderCommandEncoder > bind()
Definition mtl_batch.mm:277
void draw(int v_first, int v_count, int i_first, int i_count) override
Definition mtl_batch.mm:35
void unbind(id< MTLRenderCommandEncoder > rec)
Definition mtl_batch.mm:389
gpu::MTLBuffer * allocate(uint64_t size, bool cpu_visible)
MTLRenderPassState & get_render_pass_state()
bool ensure_render_pipeline_state(MTLPrimitiveType prim_type)
id< MTLRenderCommandEncoder > ensure_begin_render_pass()
static MTLContext * get()
MTLCommandBufferManager main_command_buffer
static MTLBufferPool * get_global_memory_manager()
static MTLIndexType gpu_index_type_to_metal(GPUIndexBufType type)
void bind_vertex_buffer(id< MTLBuffer > buffer, uint64_t buffer_offset, uint index)
MTLRenderPipelineStateDescriptor & get_pipeline_descriptor()
Definition mtl_state.hh:59
static float verts[][3]
#define input
#define interface
#define printf(...)
format
descriptor
#define G(x, y, z)
#define GPU_VAO_STATIC_LEN
Definition mtl_batch.hh:26
#define MTL_LOG_WARNING(info,...)
Definition mtl_debug.hh:42
#define MTL_LOG_DEBUG(info,...)
Definition mtl_debug.hh:49
#define MTL_LOG_ERROR(info,...)
Definition mtl_debug.hh:34
MTLVertexFormat format_resize_comp(MTLVertexFormat mtl_format, uint32_t components)
static MTLPrimitiveType gpu_prim_type_to_metal(GPUPrimType prim_type)
bool mtl_convert_vertex_format(MTLVertexFormat shader_attr_format, GPUVertCompType component_type, uint32_t component_len, GPUVertFetchMode fetch_mode, MTLVertexFormat *r_convertedFormat)
static bool mtl_needs_topology_emulation(GPUPrimType prim_type)
const char * name
GPUVertFetchMode fetch_mode() const
GPUVertCompType comp_type() const
struct GPUVertAttr::Type type
MTLVertexAttributeDescriptorPSO attributes[GPU_VERT_ATTR_MAX_LEN]
i
Definition text_draw.cc:230
char * buffers[2]