Blender V4.3
mtl_shader_generator.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
9#include "BKE_global.hh"
10
11#include "BLI_string.h"
12
13#include "BLI_string.h"
14#include <algorithm>
15#include <fstream>
16#include <iostream>
17#include <map>
18#include <mutex>
19#include <regex>
20#include <sstream>
21#include <string>
22
23#include <cstring>
24
25#include "GPU_platform.hh"
26#include "GPU_vertex_format.hh"
27
29
30#include "mtl_common.hh"
31#include "mtl_context.hh"
32#include "mtl_debug.hh"
33#include "mtl_shader.hh"
36#include "mtl_texture.hh"
37
39extern char datatoc_mtl_shader_shared_h[];
40
41using namespace blender;
42using namespace blender::gpu;
43using namespace blender::gpu::shader;
44
45namespace blender::gpu {
46
47char *MSLGeneratorInterface::msl_patch_default = nullptr;
48
49/* Generator names. */
50#define FRAGMENT_OUT_STRUCT_NAME "FragmentOut"
51#define FRAGMENT_TILE_IN_STRUCT_NAME "FragmentTileIn"
52
53#define ATOMIC_DEFINE_STR "#define MTL_SUPPORTS_TEXTURE_ATOMICS 1\n"
54
55/* -------------------------------------------------------------------- */
60{
61 switch (type) {
62 case Type::FLOAT:
63 return MTL_DATATYPE_FLOAT;
64 case Type::VEC2:
66 case Type::VEC3:
68 case Type::VEC4:
70 case Type::MAT3:
72 case Type::MAT4:
74 case Type::UINT:
75 return MTL_DATATYPE_UINT;
76 case Type::UVEC2:
77 return MTL_DATATYPE_UINT2;
78 case Type::UVEC3:
79 return MTL_DATATYPE_UINT3;
80 case Type::UVEC4:
81 return MTL_DATATYPE_UINT4;
82 case Type::INT:
83 return MTL_DATATYPE_INT;
84 case Type::IVEC2:
85 return MTL_DATATYPE_INT2;
86 case Type::IVEC3:
87 return MTL_DATATYPE_INT3;
88 case Type::IVEC4:
89 return MTL_DATATYPE_INT4;
90 case Type::VEC3_101010I2:
92 case Type::BOOL:
93 return MTL_DATATYPE_BOOL;
94 case Type::UCHAR:
95 return MTL_DATATYPE_UCHAR;
96 case Type::UCHAR2:
98 case Type::UCHAR3:
100 case Type::UCHAR4:
101 return MTL_DATATYPE_UCHAR4;
102 case Type::CHAR:
103 return MTL_DATATYPE_CHAR;
104 case Type::CHAR2:
105 return MTL_DATATYPE_CHAR2;
106 case Type::CHAR3:
107 return MTL_DATATYPE_CHAR3;
108 case Type::CHAR4:
109 return MTL_DATATYPE_CHAR4;
110 case Type::USHORT:
111 return MTL_DATATYPE_USHORT;
112 case Type::USHORT2:
114 case Type::USHORT3:
116 case Type::USHORT4:
118 case Type::SHORT:
119 return MTL_DATATYPE_SHORT;
120 case Type::SHORT2:
121 return MTL_DATATYPE_SHORT2;
122 case Type::SHORT3:
123 return MTL_DATATYPE_SHORT3;
124 case Type::SHORT4:
125 return MTL_DATATYPE_SHORT4;
126 default: {
127 BLI_assert_msg(false, "Unexpected data type");
128 }
129 }
130 return MTL_DATATYPE_FLOAT;
131}
132
133static std::regex remove_non_numeric_characters("[^0-9]");
134
135static void remove_multiline_comments_func(std::string &str)
136{
137 char *current_str_begin = &*str.begin();
138 char *current_str_end = &*str.end();
139
140 bool is_inside_comment = false;
141 for (char *c = current_str_begin; c < current_str_end; c++) {
142 if (is_inside_comment) {
143 if ((*c == '*') && (c < current_str_end - 1) && (*(c + 1) == '/')) {
144 is_inside_comment = false;
145 *c = ' ';
146 *(c + 1) = ' ';
147 }
148 else {
149 *c = ' ';
150 }
151 }
152 else {
153 if ((*c == '/') && (c < current_str_end - 1) && (*(c + 1) == '*')) {
154 is_inside_comment = true;
155 *c = ' ';
156 }
157 }
158 }
159}
160
161static void remove_singleline_comments_func(std::string &str)
162{
163 char *current_str_begin = &*str.begin();
164 char *current_str_end = &*str.end();
165
166 bool is_inside_comment = false;
167 for (char *c = current_str_begin; c < current_str_end; c++) {
168 if (is_inside_comment) {
169 if (*c == '\n') {
170 is_inside_comment = false;
171 }
172 else {
173 *c = ' ';
174 }
175 }
176 else {
177 if ((*c == '/') && (c < current_str_end - 1) && (*(c + 1) == '/')) {
178 is_inside_comment = true;
179 *c = ' ';
180 }
181 }
182 }
183}
184
185static bool is_program_word(const char *chr, int *len)
186{
187 int numchars = 0;
188 for (const char *c = chr; *c != '\0'; c++) {
189 char ch = *c;
190 /* NOTE: Hash (`#`) is not valid in var names, but is used by Closure macro patterns. */
191 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
192 (numchars > 0 && ch >= '0' && ch <= '9') || ch == '_' || ch == '#')
193 {
194 numchars++;
195 }
196 else {
197 *len = numchars;
198 return (numchars > 0);
199 }
200 }
201 *len = numchars;
202 return true;
203}
204
205static int backwards_program_word_scan(const char *array_loc, const char *min)
206{
207 const char *start;
208 char last_char = ' ';
209 int numchars = 0;
210 for (start = array_loc - 1; (start >= min) && (*start != '\0'); start--) {
211 char ch = *start;
212 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') ||
213 ch == '_' || ch == '#')
214 {
215 numchars++;
216 last_char = ch;
217 }
218 else {
219 break;
220 }
221 }
222
223 if (numchars > 0) {
224 /* cannot start with numbers, so we need to invalidate the word. */
225 if ((last_char >= '0' && last_char <= '9')) {
226 numchars = 0;
227 }
228 }
229 return numchars;
230}
231
232/* Extract clipping distance usage indices, and replace syntax with metal-compatible.
233 * We need to replace syntax gl_ClipDistance[N] with gl_ClipDistance_N such that it is compatible
234 * with the Metal shaders Vertex shader output struct. */
236 MSLGeneratorInterface &msl_iface)
237{
238 char *current_str_begin = &*vertex_source.begin();
239 char *current_str_end = &*vertex_source.end();
240
241 for (char *c = current_str_begin + 2; c < current_str_end - 18; c++) {
242 char *base_search = strstr(c, "gl_ClipDistance[");
243 if (base_search == nullptr) {
244 /* No clip distances found. */
245 return;
246 }
247 c = base_search + 16;
248
249 /* Ensure closing brace. */
250 if (*(c + 1) != ']') {
251 continue;
252 }
253
254 /* Extract ID between zero and 9. */
255 if ((*c >= '0') && (*c <= '9')) {
256 char clip_distance_id = ((*c) - '0');
257 auto found = std::find(
258 msl_iface.clip_distances.begin(), msl_iface.clip_distances.end(), clip_distance_id);
259 if (found == msl_iface.clip_distances.end()) {
260 msl_iface.clip_distances.append(clip_distance_id);
261 }
262
263 /* Replace syntax (array brace removal, and replacement with underscore). */
264 *(base_search + 15) = '_';
265 *(base_search + 17) = ' ';
266 }
267 }
268}
269
276static void replace_outvars(std::string &str)
277{
278 char *current_str_begin = &*str.begin();
279 char *current_str_end = &*str.end();
280
281 for (char *c = current_str_begin + 2; c < current_str_end - 6; c++) {
282 char *start = strstr(c, "out ");
283 if (start == nullptr) {
284 return;
285 }
286 else {
287 c = start;
288 if (strncmp(c - 2, "in", 2) == 0) {
289 start = c - 2;
290 }
291
292 /* Check that the following are words. */
293 int len1, len2;
294 char *word_base1 = c + 4;
295 char *word_base2 = word_base1;
296
297 if (is_program_word(word_base1, &len1) && (*(word_base1 + len1) == ' ')) {
298 word_base2 = word_base1 + len1 + 1;
299 if (is_program_word(word_base2, &len2)) {
300 /* Match found. */
301 bool is_array = (*(word_base2 + len2) == '[');
302 if (is_array) {
303 /* Generate out-variable pattern for arrays, of form
304 * `OUT(vec2,samples,CRYPTOMATTE_LEVELS_MAX)`
305 * replacing original `out vec2 samples[SAMPLE_LEN]`
306 * using 'OUT' macro declared in `mtl_shader_defines.msl`. */
307 char *array_end = strchr(word_base2 + len2, ']');
308 if (array_end != nullptr) {
309 *start = 'O';
310 *(start + 1) = 'U';
311 *(start + 2) = 'T';
312 *(start + 3) = '(';
313 for (char *clear = start + 4; clear < c + 4; clear++) {
314 *clear = ' ';
315 }
316 *(word_base2 - 1) = ',';
317 *(word_base2 + len2) = ',';
318 *array_end = ')';
319 }
320 }
321 else {
322 /* Generate out-variable pattern of form `THD type&var` from original `out vec4 var`.
323 */
324 *start = 'T';
325 *(start + 1) = 'H';
326 *(start + 2) = 'D';
327 for (char *clear = start + 3; clear < c + 4; clear++) {
328 *clear = ' ';
329 }
330 *(word_base2 - 1) = '&';
331 }
332 }
333 }
334 }
335 }
336}
337
338static void replace_matrix_constructors(std::string &str)
339{
340
341 /* Replace matrix constructors with GLSL-compatible constructors for Metal.
342 * Base matrix constructors e.g. mat3x3 do not have as many overload variants as GLSL.
343 * To add compatibility, we declare custom constructors e.g. MAT3x3 in `mtl_shader_defines.msl`.
344 * If the GLSL syntax matches, we map mat3x3(..) -> MAT3x3(..) and implement a custom
345 * constructor. This supports both mat3(..) and mat3x3(..) style syntax. */
346 char *current_str_begin = &*str.begin();
347 char *current_str_end = &*str.end();
348
349 for (char *c = current_str_begin; c < current_str_end - 10; c++) {
350 char *base_scan = strstr(c, "mat");
351 if (base_scan == nullptr) {
352 break;
353 }
354 /* Track end of constructor. */
355 char *constructor_end = nullptr;
356
357 /* check if next character is matrix dim. */
358 c = base_scan + 3;
359 if (!(*c == '2' || *c == '3' || *c == '4')) {
360 /* Not constructor, skip. */
361 continue;
362 }
363
364 /* Possible multiple dimensional matrix constructor. Verify if next char is a dim. */
365 c++;
366 if (*c == 'x') {
367 c++;
368 if (*c == '2' || *c == '3' || *c == '4') {
369 c++;
370 }
371 else {
372 /* Not matrix constructor, continue. */
373 continue;
374 }
375 }
376
377 /* Check for constructor opening brace. */
378 if (*c == '(') {
379 constructor_end = c;
380 }
381 else {
382 /* Not matrix constructor, continue. */
383 continue;
384 }
385
386 /* If is constructor, replace with MATN(..) syntax. */
387 if (constructor_end != nullptr) {
388 ARRAY_SET_ITEMS(base_scan, 'M', 'A', 'T');
389 continue;
390 }
391 }
392}
393
394static void replace_array_initializers_func(std::string &str)
395{
396 char *current_str_begin = &*str.begin();
397 char *current_str_end = &*str.end();
398
399 for (char *c = current_str_begin; c < current_str_end - 6; c++) {
400
401 int typelen = 0;
402
403 /* first find next array brace, then work backwards to find start of program word to check if
404 * valid array syntax. */
405 char *array_scan = strchr(c, '[');
406 if (array_scan == nullptr) {
407 return;
408 }
409 typelen = backwards_program_word_scan(array_scan - 1, current_str_begin);
410 char *base_type_name = array_scan - 1 - typelen;
411
412 if (typelen > 0) {
413 // if (is_program_word(c, &typelen) && *(c + typelen) == '[') {
414
415 c = array_scan;
416 char *closing_square_brace = strchr(c, ']');
417 if (closing_square_brace != nullptr) {
418 c = closing_square_brace;
419 char *first_bracket = c + 1;
420 if (*first_bracket == '(') {
421 c += 1;
422 char *semi_colon = strchr(c, ';');
423 if (semi_colon != nullptr && *(semi_colon - 1) == ')') {
424 char *closing_bracket = semi_colon - 1;
425
426 /* Resolve to MSL-compatible array formatting. */
427 *first_bracket = '{';
428 *closing_bracket = '}';
429 for (char *clear = base_type_name; clear <= closing_square_brace; clear++) {
430 *clear = ' ';
431 }
432 }
433 }
434 }
435 else {
436 return;
437 }
438 }
439 else {
440 /* Not an array initializer, continue scanning. */
441 c = array_scan + 1;
442 continue;
443 }
444 }
445}
446
447#ifndef NDEBUG
448
449static bool balanced_braces(char *current_str_begin, char *current_str_end)
450{
451 int nested_bracket_depth = 0;
452 for (char *c = current_str_begin; c < current_str_end; c++) {
453 /* Track whether we are in global scope. */
454 if (*c == '{' || *c == '[' || *c == '(') {
455 nested_bracket_depth++;
456 continue;
457 }
458 if (*c == '}' || *c == ']' || *c == ')') {
459 nested_bracket_depth--;
460 continue;
461 }
462 }
463 return (nested_bracket_depth == 0);
464}
465
475static void extract_global_scope_constants(std::string &str,
476 std::stringstream & /*global_scope_out*/)
477{
478 char *current_str_begin = &*str.begin();
479 char *current_str_end = &*str.end();
480
481 int nested_bracket_depth = 0;
482 for (char *c = current_str_begin; c < current_str_end - 6; c++) {
483 /* Track whether we are in global scope. */
484 if (*c == '{' || *c == '[' || *c == '(') {
485 nested_bracket_depth++;
486 continue;
487 }
488 if (*c == '}' || *c == ']' || *c == ')') {
489 nested_bracket_depth--;
490 BLI_assert(nested_bracket_depth >= 0);
491 continue;
492 }
493
494 /* Check For global const declarations */
495 if (nested_bracket_depth == 0 && strncmp(c, "const ", 6) == 0 &&
496 strncmp(c, "const constant ", 15) != 0)
497 {
498 char *c_expr_end = strchr(c, ';');
499 if (c_expr_end != nullptr && balanced_braces(c, c_expr_end)) {
501 "[PERFORMANCE WARNING] Global scope constant expression found - These get allocated "
502 "per-thread in METAL - Best to use Macro's or uniforms to avoid overhead: '%.*s'",
503 (int)(c_expr_end + 1 - c),
504 c);
505
506 /* Jump ptr forward as we know we remain in global scope. */
507 c = c_expr_end - 1;
508 continue;
509 }
510 }
511 }
512}
513#endif
514
515static bool extract_ssbo_pragma_info(const MTLShader *shader,
516 const MSLGeneratorInterface & /*msl_iface*/,
517 const std::string &in_vertex_src,
518 MTLPrimitiveType &out_prim_tye,
519 uint32_t &out_num_output_verts)
520{
521 /* SSBO Vertex-fetch parameter extraction. */
522 static std::regex use_ssbo_fetch_mode_find(
523 "#pragma "
524 "USE_SSBO_VERTEX_FETCH\\(\\s*(TriangleList|LineList|TriangleStrip|\\w+)\\s*,\\s*([0-9]+)\\s*"
525 "\\)");
526
527 /* Perform regex search if pragma string found. */
528 std::smatch vertex_shader_ssbo_flags;
529 bool uses_ssbo_fetch = false;
530 if (in_vertex_src.find("#pragma USE_SSBO_VERTEX_FETCH") != std::string::npos) {
531 uses_ssbo_fetch = std::regex_search(
532 in_vertex_src, vertex_shader_ssbo_flags, use_ssbo_fetch_mode_find);
533 }
534 if (uses_ssbo_fetch) {
535 /* Extract Expected output primitive type:
536 * #pragma USE_SSBO_VERTEX_FETCH(Output Prim Type, num output vertices per input primitive)
537 *
538 * Supported Primitive Types (Others can be added if needed, but List types for efficiency):
539 * - TriangleList
540 * - LineList
541 * - TriangleStrip (To be used with caution).
542 *
543 * Output vertex count is determined by calculating the number of input primitives, and
544 * multiplying that by the number of output vertices specified. */
545 std::string str_output_primitive_type = vertex_shader_ssbo_flags[1].str();
546 std::string str_output_prim_count_per_vertex = vertex_shader_ssbo_flags[2].str();
547
548 /* Ensure output primitive type is valid. */
549 if (str_output_primitive_type == "TriangleList") {
550 out_prim_tye = MTLPrimitiveTypeTriangle;
551 }
552 else if (str_output_primitive_type == "LineList") {
553 out_prim_tye = MTLPrimitiveTypeLine;
554 }
555 else if (str_output_primitive_type == "TriangleStrip") {
556 out_prim_tye = MTLPrimitiveTypeTriangleStrip;
557 }
558 else {
559 MTL_LOG_ERROR("Unsupported output primitive type for SSBO VERTEX FETCH MODE. Shader: %s",
560 shader->name_get());
561 return false;
562 }
563
564 /* Assign output num vertices per primitive. */
565 out_num_output_verts = std::stoi(
566 std::regex_replace(str_output_prim_count_per_vertex, remove_non_numeric_characters, ""));
567 BLI_assert(out_num_output_verts > 0);
568 return true;
569 }
570
571 /* SSBO Vertex fetchmode not used. */
572 return false;
573}
574
575/* Extract shared memory declaration and their parameters.
576 * Inserts extracted cases as entries in MSLGeneratorInterface's shared memory block
577 * list. These will later be used to generate shared memory declarations within the entry point.
578 *
579 * TODO(Metal/GPU): Move shared memory declarations to GPUShaderCreateInfo. This is currently a
580 * necessary workaround to match GLSL functionality and enable full compute shader support. In the
581 * long term, best to avoid needing to perform this operation. */
583 std::string &glsl_compute_source)
584{
585 msl_iface.shared_memory_blocks.clear();
586 char *current_str_begin = &*glsl_compute_source.begin();
587 char *current_str_end = &*glsl_compute_source.end();
588
589 for (char *c = current_str_begin; c < current_str_end - 6; c++) {
590 /* Find first instance of "shared ". */
591 char *c_expr_start = strstr(c, "shared ");
592 if (c_expr_start == nullptr) {
593 break;
594 }
595 /* Check if "shared" was part of a previous word. If so, this is not valid. */
596 if (next_word_in_range(c_expr_start - 1, c_expr_start) != nullptr) {
597 c += 7; /* Jump forward by length of "shared ". */
598 continue;
599 }
600
601 /* Jump to shared declaration and detect end of statement. */
602 c = c_expr_start;
603 char *c_expr_end = strstr(c, ";");
604 if (c_expr_end == nullptr) {
605 break;
606 }
607
608 /* Prepare MSLSharedMemoryBlock instance. */
609 MSLSharedMemoryBlock new_shared_block;
610 char buf[256];
611
612 /* Read type-name. */
613 c += 7; /* Jump forward by length of "shared ". */
614 c = next_word_in_range(c, c_expr_end);
615 if (c == nullptr) {
616 c = c_expr_end + 1;
617 continue;
618 }
619
620 char *c_next_space = next_symbol_in_range(c, c_expr_end, ' ');
621 if (c_next_space == nullptr) {
622 c = c_expr_end + 1;
623 continue;
624 }
625 int len = c_next_space - c;
626 BLI_assert(len < 256);
627 BLI_strncpy(buf, c, len + 1);
628 new_shared_block.type_name = std::string(buf);
629
630 /* Read var-name.
631 * `varname` can either come right before the final semi-colon, or
632 * with following array syntax.
633 * spaces may exist before closing symbol. */
634 c = c_next_space + 1;
635 c = next_word_in_range(c, c_expr_end);
636 if (c == nullptr) {
637 c = c_expr_end + 1;
638 continue;
639 }
640
641 char *c_array_begin = next_symbol_in_range(c, c_expr_end, '[');
642 c_next_space = next_symbol_in_range(c, c_expr_end, ' ');
643
644 char *varname_end = nullptr;
645 if (c_array_begin != nullptr) {
646 /* Array path. */
647 if (c_next_space != nullptr) {
648 varname_end = (c_next_space < c_array_begin) ? c_next_space : c_array_begin;
649 }
650 else {
651 varname_end = c_array_begin;
652 }
653 new_shared_block.is_array = true;
654 }
655 else {
656 /* Ending semi-colon. */
657 if (c_next_space != nullptr) {
658 varname_end = (c_next_space < c_expr_end) ? c_next_space : c_expr_end;
659 }
660 else {
661 varname_end = c_expr_end;
662 }
663 new_shared_block.is_array = false;
664 }
665 len = varname_end - c;
666 BLI_assert(len < 256);
667 BLI_strncpy(buf, c, len + 1);
668 new_shared_block.varname = std::string(buf);
669
670 /* Determine if array. */
671 if (new_shared_block.is_array) {
672 int len = c_expr_end - c_array_begin;
673 BLI_strncpy(buf, c_array_begin, len + 1);
674 new_shared_block.array_decl = std::string(buf);
675 }
676
677 /* Shared block is valid, add it to the list and replace declaration with class member.
678 * reference. This declaration needs to have one of the formats:
679 * TG int& varname;
680 * TG int (&varname)[len][len]
681 *
682 * In order to fit in the same space, replace `threadgroup` with `TG` macro.
683 */
684 for (char *c = c_expr_start; c <= c_expr_end; c++) {
685 *c = ' ';
686 }
687 std::string out_str = "TG ";
688 out_str += new_shared_block.type_name;
689 out_str += (new_shared_block.is_array) ? "(&" : "&";
690 out_str += new_shared_block.varname;
691 if (new_shared_block.is_array) {
692 out_str += ")" + new_shared_block.array_decl;
693 }
694 out_str += ";;";
695 memcpy(c_expr_start, out_str.c_str(), (out_str.length() - 1) * sizeof(char));
696
697 /* Jump to end of statement. */
698 c = c_expr_end + 1;
699
700 msl_iface.shared_memory_blocks.append(new_shared_block);
701 }
702}
703
706/* -------------------------------------------------------------------- */
710static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &res)
711{
712 switch (res.bind_type) {
713 case ShaderCreateInfo::Resource::BindType::SAMPLER:
714 break;
715 case ShaderCreateInfo::Resource::BindType::IMAGE:
716 break;
717 case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER: {
718 int64_t array_offset = res.uniformbuf.name.find_first_of("[");
719 if (array_offset == -1) {
720 /* Create local class member as constant pointer reference to bound UBO buffer.
721 * Given usage within a shader follows ubo_name.ubo_element syntax, we can
722 * dereference the pointer as the compiler will optimize this data fetch.
723 * To do this, we also give the UBO name a post-fix of `_local` to avoid
724 * macro accessor collisions. */
725 os << "constant " << res.uniformbuf.type_name << " *" << res.uniformbuf.name
726 << "_local;\n";
727 os << "#define " << res.uniformbuf.name << " (*" << res.uniformbuf.name << "_local)\n";
728 }
729 else {
730 /* For arrays, we can directly provide the constant access pointer, as the array
731 * syntax will de-reference this at the correct fetch index. */
732 StringRef name_no_array = StringRef(res.uniformbuf.name.c_str(), array_offset);
733 os << "constant " << res.uniformbuf.type_name << " *" << name_no_array << ";\n";
734 }
735 break;
736 }
737 case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER: {
738 int64_t array_offset = res.storagebuf.name.find_first_of("[");
739 bool writeable = (res.storagebuf.qualifiers & shader::Qualifier::WRITE) ==
741 const char *memory_scope = ((writeable) ? "device " : "constant ");
742 if (array_offset == -1) {
743 /* Create local class member as device pointer reference to bound SSBO.
744 * Given usage within a shader follows ssbo_name.ssbo_element syntax, we can
745 * dereference the pointer as the compiler will optimize this data fetch.
746 * To do this, we also give the UBO name a post-fix of `_local` to avoid
747 * macro accessor collisions. */
748
749 os << memory_scope << res.storagebuf.type_name << " *" << res.storagebuf.name
750 << "_local;\n";
751 os << "#define " << res.storagebuf.name << " (*" << res.storagebuf.name << "_local)\n";
752 }
753 else {
754 /* For arrays, we can directly provide the constant access pointer, as the array
755 * syntax will de-reference this at the correct fetch index. */
756 StringRef name_no_array = StringRef(res.storagebuf.name.c_str(), array_offset);
757 os << memory_scope << res.storagebuf.type_name << " *" << name_no_array << ";\n";
758 }
759 break;
760 }
761 }
762}
763
764std::string MTLShader::resources_declare(const ShaderCreateInfo &info) const
765{
766 /* NOTE(Metal): We only use the upfront preparation functions to populate members which
767 * would exist in the original non-create-info variant.
768 *
769 * This function is only used to generate resource structs.
770 * Global-scope handles for Uniforms, UBOs, textures and samplers
771 * are generated during class-wrapper construction in `generate_msl_from_glsl`. */
772 std::stringstream ss;
773
774 /* Generate resource stubs for UBOs and textures. */
775 ss << "\n/* Pass Resources. */\n";
776 for (const ShaderCreateInfo::Resource &res : info.pass_resources_) {
777 print_resource(ss, res);
778 }
779 ss << "\n/* Batch Resources. */\n";
780 for (const ShaderCreateInfo::Resource &res : info.batch_resources_) {
781 print_resource(ss, res);
782 }
783 ss << "\n/* Geometry Resources. */\n";
784 for (const ShaderCreateInfo::Resource &res : info.geometry_resources_) {
785 print_resource(ss, res);
786 }
787 /* NOTE: Push constant uniform data is generated during `generate_msl_from_glsl`
788 * as the generated output is needed for all paths. This includes generation
789 * of the push constant data structure (struct PushConstantBlock).
790 * As all shader generation paths require creation of this. */
791 return ss.str();
792}
793
795{
796 /* NOTE(Metal): We only use the upfront preparation functions to populate members which
797 * would exist in the original non-create-info variant.
798 *
799 * Here we generate the variables within class wrapper scope to allow reading of
800 * input attributes by the main code. */
801 std::stringstream ss;
802 ss << "\n/* Vertex Inputs. */\n";
803 for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) {
804 ss << to_string(attr.type) << " " << attr.name << ";\n";
805 }
806 return ss.str();
807}
808
810{
811 /* For shaders generated from MSL, the fragment-output struct is generated as part of the entry
812 * stub during glsl->MSL conversion in `generate_msl_from_glsl`.
813 * Here, we can instead generate the global-scope variables which will be populated during
814 * execution.
815 *
816 * NOTE: The output declaration for location and blend index are generated in the entry-point
817 * struct. This is simply a mirror class member which stores the value during main shader body
818 * execution. */
819 std::stringstream ss;
820 ss << "\n/* Fragment Outputs. */\n";
821 for (const ShaderCreateInfo::FragOut &output : info.fragment_outputs_) {
822 ss << to_string(output.type) << " " << output.name << ";\n";
823 }
824 ss << "\n";
825
826 ss << "\n/* Fragment Tile inputs. */\n";
827 for (const ShaderCreateInfo::SubpassIn &input : info.subpass_inputs_) {
828 ss << to_string(input.type) << " " << input.name << ";\n";
829 }
830 ss << "\n";
831
832 return ss.str();
833}
834
835std::string MTLShader::MTLShader::geometry_interface_declare(
836 const shader::ShaderCreateInfo & /*info*/) const
837{
838 BLI_assert_msg(false, "Geometry shading unsupported by Metal");
839 return "";
840}
841
843{
844 BLI_assert_msg(false, "Geometry shading unsupported by Metal");
845 return "";
846}
847
848std::string MTLShader::compute_layout_declare(const ShaderCreateInfo & /*info*/) const
849{
850 /* Metal supports compute shaders. THis function is a pass-through.
851 * Compute shader interface population happens during mtl_shader_generator, as part of GLSL
852 * conversion. */
853 return "";
854}
855
858/* -------------------------------------------------------------------- */
863{
864 if (msl_patch_default != nullptr) {
865 return msl_patch_default;
866 }
867
868 std::stringstream ss_patch;
869 ss_patch << datatoc_mtl_shader_defines_msl << std::endl;
870 ss_patch << datatoc_mtl_shader_shared_h << std::endl;
871 size_t len = strlen(ss_patch.str().c_str()) + 1;
872
873 msl_patch_default = (char *)malloc(len * sizeof(char));
874 memcpy(msl_patch_default, ss_patch.str().c_str(), len * sizeof(char));
875 return msl_patch_default;
876}
877
878/* Specialization constants will evaluate using a dynamic value if provided at PSO compile time. */
880 std::stringstream &ss)
881{
883 for (const SpecializationConstant &sc : info->specialization_constants_) {
884 /* TODO(Metal): Output specialization constant chain. */
885 ss << "constant " << sc.type << " " << sc.name << " [[function_constant(" << index << ")]];\n";
886 index++;
887 }
888}
889
890bool MTLShader::generate_msl_from_glsl(const shader::ShaderCreateInfo *info)
891{
892 /* Verify if create-info is available.
893 * NOTE(Metal): For now, only support creation from CreateInfo.
894 * If needed, we can perform source translation without this using
895 * manual reflection. */
896 bool uses_create_info = info != nullptr;
897 if (!uses_create_info) {
898 MTL_LOG_WARNING("Unable to compile shader %p '%s' as no create-info was provided!",
899 this,
900 this->name_get());
901 valid_ = false;
902 return false;
903 }
904
905 /* Compute shaders use differing compilation path. */
906 if (shd_builder_->glsl_compute_source_.size() > 0) {
907 return this->generate_msl_from_glsl_compute(info);
908 }
909
910 /* #MSLGeneratorInterface is a class populated to describe all parameters, resources, bindings
911 * and features used by the source GLSL shader. This information is then used to generate the
912 * appropriate Metal entry points and perform any required source translation. */
913 MSLGeneratorInterface msl_iface(*this);
914 BLI_assert(shd_builder_ != nullptr);
915
916 /* Populate #MSLGeneratorInterface from Create-Info.
917 * NOTE: this is a separate path as #MSLGeneratorInterface can also be manually populated
918 * from parsing, if support for shaders without create-info is required. */
919 msl_iface.prepare_from_createinfo(info);
920
921 /* Verify Source sizes are greater than zero. */
922 BLI_assert(shd_builder_->glsl_vertex_source_.size() > 0);
923 if (!msl_iface.uses_transform_feedback) {
924 BLI_assert(shd_builder_->glsl_fragment_source_.size() > 0);
925 }
926
927 if (transform_feedback_type_ != GPU_SHADER_TFB_NONE) {
928 /* Ensure #TransformFeedback is configured correctly. */
929 BLI_assert(tf_output_name_list_.size() > 0);
930 msl_iface.uses_transform_feedback = true;
931 }
932
933 /* Concatenate msl_shader_defines to provide functionality mapping
934 * from GLSL to MSL. Also include additional GPU defines for
935 * optional high-level feature support. */
936 std::string msl_defines_string = "#define GPU_ARB_shader_draw_parameters 1\n";
937
938 /* NOTE(Metal): textureGather appears to not function correctly on non-Apple-silicon GPUs.
939 * Manifests as selection outlines not showing up (#103412). Disable texture gather if
940 * not suitable for use. */
941 if (MTLBackend::get_capabilities().supports_texture_gather) {
942 msl_defines_string += "#define GPU_ARB_texture_gather 1\n";
943 }
944
945 shd_builder_->glsl_vertex_source_ = msl_defines_string + shd_builder_->glsl_vertex_source_;
946 if (!msl_iface.uses_transform_feedback) {
947 shd_builder_->glsl_fragment_source_ = msl_defines_string + shd_builder_->glsl_fragment_source_;
948 }
949
950 /* Extract SSBO usage information from shader pragma:
951 *
952 * #pragma USE_SSBO_VERTEX_FETCH(Output Prim Type, num output vertices per input primitive)
953 *
954 * This will determine whether SSBO-vertex-fetch
955 * mode is used for this shader. Returns true if used, and populates output reference
956 * values with the output prim type and output number of vertices. */
957 MTLPrimitiveType vertex_fetch_ssbo_output_prim_type = MTLPrimitiveTypeTriangle;
958 uint32_t vertex_fetch_ssbo_num_output_verts = 0;
959 msl_iface.uses_ssbo_vertex_fetch_mode = extract_ssbo_pragma_info(
960 this,
961 msl_iface,
962 shd_builder_->glsl_vertex_source_,
963 vertex_fetch_ssbo_output_prim_type,
964 vertex_fetch_ssbo_num_output_verts);
965
966 if (msl_iface.uses_ssbo_vertex_fetch_mode) {
968 "[Shader] SSBO VERTEX FETCH Enabled for Shader '%s' With Output primitive type: %s, "
969 "vertex count: %u\n",
970 this->name_get(),
971 output_primitive_type.c_str(),
972 vertex_fetch_ssbo_num_output_verts);
973 }
974
975 /* Special condition - mat3 and array constructor replacement. */
978
979 if (!msl_iface.uses_transform_feedback) {
982 }
983
984 /**** Extract usage of GL globals. ****/
985 /* NOTE(METAL): Currently still performing fallback string scan, as info->builtins_ does
986 * not always contain the usage flag. This can be removed once all appropriate create-info's
987 * have been updated. In some cases, this may incur a false positive if access is guarded
988 * behind a macro. Though in these cases, unused code paths and parameters will be
989 * optimized out by the Metal shader compiler. */
990
992 msl_iface.uses_gl_VertexID = bool(info->builtins_ & BuiltinBits::VERTEX_ID) ||
993 shd_builder_->glsl_vertex_source_.find("gl_VertexID") !=
994 std::string::npos;
995 msl_iface.uses_gl_InstanceID = bool(info->builtins_ & BuiltinBits::INSTANCE_ID) ||
996 shd_builder_->glsl_vertex_source_.find("gl_InstanceID") !=
997 std::string::npos ||
998 shd_builder_->glsl_vertex_source_.find("gpu_InstanceIndex") !=
999 std::string::npos ||
1000 msl_iface.uses_ssbo_vertex_fetch_mode;
1001
1002 /* instance ID in GL is `[0, instance_count]` in metal it is
1003 * `[base_instance, base_instance + instance_count]`,
1004 * so we need to offset instance_ID by base instance in Metal --
1005 * Thus we expose the `[[base_instance]]` attribute if instance ID is used at all. */
1006 msl_iface.uses_gl_BaseInstanceARB = msl_iface.uses_gl_InstanceID ||
1007 shd_builder_->glsl_vertex_source_.find(
1008 "gl_BaseInstanceARB") != std::string::npos ||
1009 shd_builder_->glsl_vertex_source_.find("gpu_BaseInstance") !=
1010 std::string::npos;
1011 msl_iface.uses_gl_Position = shd_builder_->glsl_vertex_source_.find("gl_Position") !=
1012 std::string::npos;
1013 msl_iface.uses_gl_PointSize = shd_builder_->glsl_vertex_source_.find("gl_PointSize") !=
1014 std::string::npos;
1015 msl_iface.uses_gpu_layer = bool(info->builtins_ & BuiltinBits::LAYER);
1016 msl_iface.uses_gpu_viewport_index = bool(info->builtins_ & BuiltinBits::VIEWPORT_INDEX);
1017
1019 if (!msl_iface.uses_transform_feedback) {
1020 std::smatch gl_special_cases;
1021 msl_iface.uses_gl_PointCoord = bool(info->builtins_ & BuiltinBits::POINT_COORD) ||
1022 shd_builder_->glsl_fragment_source_.find("gl_PointCoord") !=
1023 std::string::npos;
1024 msl_iface.uses_barycentrics = bool(info->builtins_ & BuiltinBits::BARYCENTRIC_COORD);
1025 msl_iface.uses_gl_FrontFacing = bool(info->builtins_ & BuiltinBits::FRONT_FACING) ||
1026 shd_builder_->glsl_fragment_source_.find("gl_FrontFacing") !=
1027 std::string::npos;
1028 msl_iface.uses_gl_PrimitiveID = bool(info->builtins_ & BuiltinBits::PRIMITIVE_ID) ||
1029 shd_builder_->glsl_fragment_source_.find("gl_PrimitiveID") !=
1030 std::string::npos;
1031
1032 /* NOTE(Metal): If FragColor is not used, then we treat the first fragment output attachment
1033 * as the primary output. */
1034 msl_iface.uses_gl_FragColor = shd_builder_->glsl_fragment_source_.find("gl_FragColor") !=
1035 std::string::npos;
1036
1037 /* NOTE(Metal): FragDepth output mode specified in create-info 'DepthWrite depth_write_'.
1038 * If parsing without create-info, manual extraction will be required. */
1039 msl_iface.uses_gl_FragDepth = (info->depth_write_ != DepthWrite::UNCHANGED) &&
1040 shd_builder_->glsl_fragment_source_.find("gl_FragDepth") !=
1041 std::string::npos;
1042
1043 /* TODO(fclem): Add to create info. */
1044 msl_iface.uses_gl_FragStencilRefARB = shd_builder_->glsl_fragment_source_.find(
1045 "gl_FragStencilRefARB") != std::string::npos;
1046
1047 msl_iface.depth_write = info->depth_write_;
1048
1049 /* Early fragment tests. */
1050 msl_iface.uses_early_fragment_test = info->early_fragment_test_;
1051 }
1052
1053 /* Generate SSBO vertex fetch mode uniform data hooks. */
1054 if (msl_iface.uses_ssbo_vertex_fetch_mode) {
1055 msl_iface.prepare_ssbo_vertex_fetch_uniforms();
1056 }
1057
1058 /* Extract gl_ClipDistances. */
1060
1061 /* Replace 'out' attribute on function parameters with pass-by-reference. */
1062 replace_outvars(shd_builder_->glsl_vertex_source_);
1063 if (!msl_iface.uses_transform_feedback) {
1065 }
1066
1067 /**** METAL Shader source generation. ****/
1068 /* Setup `stringstream` for populating generated MSL shader vertex/frag shaders. */
1069 std::stringstream ss_vertex;
1070 std::stringstream ss_fragment;
1071 ss_vertex << "#line 1 \"msl_wrapper_code\"\n";
1072 ss_fragment << "#line 1 \"msl_wrapper_code\"\n";
1073
1074 if (bool(info->builtins_ & BuiltinBits::TEXTURE_ATOMIC) &&
1076 {
1077 ss_vertex << ATOMIC_DEFINE_STR;
1078 ss_fragment << ATOMIC_DEFINE_STR;
1079 }
1080
1081 /* Generate specialization constants. */
1084
1085 /*** Generate VERTEX Stage ***/
1086 /* Conditional defines. */
1087 if (msl_iface.use_argument_buffer_for_samplers()) {
1088 ss_vertex << "#define USE_ARGUMENT_BUFFER_FOR_SAMPLERS 1" << std::endl;
1089 ss_vertex << "#define ARGUMENT_BUFFER_NUM_SAMPLERS "
1090 << msl_iface.max_sampler_index_for_stage(ShaderStage::VERTEX) + 1 << std::endl;
1091 }
1092 if (msl_iface.uses_ssbo_vertex_fetch_mode) {
1093 ss_vertex << "#define MTL_SSBO_VERTEX_FETCH 1" << std::endl;
1094 for (const MSLVertexInputAttribute &attr : msl_iface.vertex_input_attributes) {
1095 ss_vertex << "#define SSBO_ATTR_TYPE_" << attr.name << " " << attr.type << std::endl;
1096 }
1097
1098 /* Macro's */
1099 ss_vertex << "#define "
1100 "UNIFORM_SSBO_USES_INDEXED_RENDERING_STR " UNIFORM_SSBO_USES_INDEXED_RENDERING_STR
1101 "\n"
1102 "#define UNIFORM_SSBO_INDEX_MODE_U16_STR " UNIFORM_SSBO_INDEX_MODE_U16_STR
1103 "\n"
1104 "#define UNIFORM_SSBO_INPUT_PRIM_TYPE_STR " UNIFORM_SSBO_INPUT_PRIM_TYPE_STR
1105 "\n"
1106 "#define UNIFORM_SSBO_INPUT_VERT_COUNT_STR " UNIFORM_SSBO_INPUT_VERT_COUNT_STR
1107 "\n"
1108 "#define UNIFORM_SSBO_INDEX_BASE_STR " UNIFORM_SSBO_INDEX_BASE_STR
1109 "\n"
1110 "#define UNIFORM_SSBO_OFFSET_STR " UNIFORM_SSBO_OFFSET_STR
1111 "\n"
1112 "#define UNIFORM_SSBO_STRIDE_STR " UNIFORM_SSBO_STRIDE_STR
1113 "\n"
1114 "#define UNIFORM_SSBO_FETCHMODE_STR " UNIFORM_SSBO_FETCHMODE_STR
1115 "\n"
1116 "#define UNIFORM_SSBO_VBO_ID_STR " UNIFORM_SSBO_VBO_ID_STR
1117 "\n"
1118 "#define UNIFORM_SSBO_TYPE_STR " UNIFORM_SSBO_TYPE_STR "\n";
1119 }
1120
1121 /* Inject common Metal header. */
1122 ss_vertex << msl_iface.msl_patch_default_get() << std::endl << std::endl;
1123
1124#ifndef NDEBUG
1125 /* Performance warning: Extract global-scope expressions.
1126 * NOTE: This is dependent on stripping out comments
1127 * to remove false positives. */
1130 extract_global_scope_constants(shd_builder_->glsl_vertex_source_, ss_vertex);
1131#endif
1132
1133 /* Generate additional shader interface struct members from create-info. */
1134 for (const StageInterfaceInfo *iface : info->vertex_out_interfaces_) {
1135
1136 /* Only generate struct for ones with instance names */
1137 if (!iface->instance_name.is_empty()) {
1138 ss_vertex << "struct " << iface->name << " {" << std::endl;
1139 for (const StageInterfaceInfo::InOut &inout : iface->inouts) {
1140 ss_vertex << to_string(inout.type) << " " << inout.name << " "
1141 << to_string_msl(inout.interp) << ";" << std::endl;
1142 }
1143 ss_vertex << "};" << std::endl;
1144 }
1145 }
1146
1147 /* Wrap entire GLSL source inside class to create
1148 * a scope within the class to enable use of global variables.
1149 * e.g. global access to attributes, uniforms, UBOs, textures etc; */
1150 ss_vertex << "class " << get_stage_class_name(ShaderStage::VERTEX) << " {" << std::endl;
1151 ss_vertex << "public:" << std::endl;
1152
1153 /* Generate additional shader interface struct members from create-info. */
1154 for (const StageInterfaceInfo *iface : info->vertex_out_interfaces_) {
1155
1156 bool is_inside_struct = false;
1157 if (!iface->instance_name.is_empty()) {
1158 /* If shader stage interface has an instance name, then it
1159 * is using a struct format and as such we only need a local
1160 * class member for the struct, not each element. */
1161 ss_vertex << iface->name << " " << iface->instance_name << ";" << std::endl;
1162 is_inside_struct = true;
1163 }
1164
1165 /* Generate local variables, populate elems for vertex out struct gen. */
1166 for (const StageInterfaceInfo::InOut &inout : iface->inouts) {
1167
1168 /* Only output individual elements if they are not part of an interface struct instance. */
1169 if (!is_inside_struct) {
1170 ss_vertex << to_string(inout.type) << " " << inout.name << ";" << std::endl;
1171 }
1172
1173 const char *arraystart = strchr(inout.name.c_str(), '[');
1174 bool is_array = (arraystart != nullptr);
1175 int array_len = (is_array) ? std::stoi(std::regex_replace(
1176 arraystart, remove_non_numeric_characters, "")) :
1177 0;
1178
1179 /* Remove array from string name. */
1180 std::string out_name = inout.name.c_str();
1181 std::size_t pos = out_name.find('[');
1182 if (is_array && pos != std::string::npos) {
1183 out_name.resize(pos);
1184 }
1185
1186 /* Add to vertex-output interface. */
1187 msl_iface.vertex_output_varyings.append(
1188 {to_string(inout.type),
1189 out_name.c_str(),
1190 ((is_inside_struct) ? iface->instance_name.c_str() : ""),
1191 to_string(inout.interp),
1192 is_array,
1193 array_len});
1194
1195 /* Add to fragment-input interface. */
1196 msl_iface.fragment_input_varyings.append(
1197 {to_string(inout.type),
1198 out_name.c_str(),
1199 ((is_inside_struct) ? iface->instance_name.c_str() : ""),
1200 to_string(inout.interp),
1201 is_array,
1202 array_len});
1203 }
1204 }
1205
1207 /* Generate VertexIn struct. */
1208 if (!msl_iface.uses_ssbo_vertex_fetch_mode) {
1209 ss_vertex << msl_iface.generate_msl_vertex_in_struct();
1210 }
1211 /* Generate Uniform data structs. */
1212 ss_vertex << msl_iface.generate_msl_uniform_structs(ShaderStage::VERTEX);
1213
1214 /* Conditionally use global GL variables. */
1215 if (msl_iface.uses_gl_Position) {
1216 ss_vertex << "float4 gl_Position;" << std::endl;
1217 }
1218 if (msl_iface.uses_gl_PointSize) {
1219 ss_vertex << "float gl_PointSize = 1.0;" << std::endl;
1220 }
1221 if (msl_iface.uses_gl_VertexID) {
1222 ss_vertex << "int gl_VertexID;" << std::endl;
1223 }
1224 if (msl_iface.uses_gl_InstanceID) {
1225 ss_vertex << "int gl_InstanceID;" << std::endl;
1226 }
1227 if (msl_iface.uses_gl_BaseInstanceARB) {
1228 ss_vertex << "int gl_BaseInstanceARB;" << std::endl;
1229 }
1230 for (const int cd : IndexRange(msl_iface.clip_distances.size())) {
1231 ss_vertex << "float gl_ClipDistance_" << cd << ";" << std::endl;
1232 }
1233
1234 /* Render target array index if using multilayered rendering. */
1235 if (msl_iface.uses_gpu_layer) {
1236 ss_vertex << "int gpu_Layer = 0;" << std::endl;
1237 }
1238 if (msl_iface.uses_gpu_viewport_index) {
1239 ss_vertex << "int gpu_ViewportIndex = 0;" << std::endl;
1240 }
1241
1242 /* Global vertex data pointers when using SSBO vertex fetch mode.
1243 * Bound vertex buffers passed in via the entry point function
1244 * are assigned to these pointers to be globally accessible
1245 * from any function within the GLSL source shader. */
1246 if (msl_iface.uses_ssbo_vertex_fetch_mode) {
1247 ss_vertex << "constant uchar** MTL_VERTEX_DATA;" << std::endl;
1248 ss_vertex << "constant ushort* MTL_INDEX_DATA_U16 = nullptr;" << std::endl;
1249 ss_vertex << "constant uint32_t* MTL_INDEX_DATA_U32 = nullptr;" << std::endl;
1250 }
1251
1252 /* Add Texture members.
1253 * These members pack both a texture and a sampler into a single
1254 * struct, as both are needed within texture functions.
1255 * e.g. `_mtl_combined_image_sampler_2d<float, access::read>`
1256 * The exact typename is generated inside `get_msl_typestring_wrapper()`. */
1257 for (const MSLTextureResource &tex : msl_iface.texture_samplers) {
1258 if (bool(tex.stage & ShaderStage::VERTEX)) {
1259 ss_vertex << "\tthread " << tex.get_msl_typestring_wrapper(false) << ";" << std::endl;
1260 }
1261 }
1262 ss_vertex << std::endl;
1263
1264 /* Inject main GLSL source into output stream. */
1265 ss_vertex << shd_builder_->glsl_vertex_source_ << std::endl;
1266
1267 /* Generate VertexOut and TransformFeedbackOutput structs. */
1268 ss_vertex << msl_iface.generate_msl_vertex_out_struct(ShaderStage::VERTEX);
1269 if (msl_iface.uses_transform_feedback) {
1270 ss_vertex << msl_iface.generate_msl_vertex_transform_feedback_out_struct(ShaderStage::VERTEX);
1271 }
1272
1273 /* Class Closing Bracket to end shader global scope. */
1274 ss_vertex << "};" << std::endl;
1275
1276 /* Generate Vertex shader entry-point function containing resource bindings. */
1277 ss_vertex << msl_iface.generate_msl_vertex_entry_stub();
1278
1279 /*** Generate FRAGMENT Stage. ***/
1280 if (!msl_iface.uses_transform_feedback) {
1281
1282 /* Conditional defines. */
1283 if (msl_iface.use_argument_buffer_for_samplers()) {
1284 ss_fragment << "#define USE_ARGUMENT_BUFFER_FOR_SAMPLERS 1" << std::endl;
1285 ss_fragment << "#define ARGUMENT_BUFFER_NUM_SAMPLERS "
1286 << msl_iface.max_sampler_index_for_stage(ShaderStage::FRAGMENT) + 1 << std::endl;
1287 }
1288
1289 /* Inject common Metal header. */
1290 ss_fragment << msl_iface.msl_patch_default_get() << std::endl << std::endl;
1291
1292#ifndef NDEBUG
1293 /* Performance warning: Identify global-scope expressions.
1294 * These cause excessive register pressure due to global arrays being instantiated per-thread.
1295 * NOTE: This is dependent on stripping out comments to remove false positives. */
1298 extract_global_scope_constants(shd_builder_->glsl_fragment_source_, ss_fragment);
1299#endif
1300
1301 /* Generate additional shader interface struct members from create-info. */
1302 for (const StageInterfaceInfo *iface : info->vertex_out_interfaces_) {
1303
1304 /* Only generate struct for ones with instance names. */
1305 if (!iface->instance_name.is_empty()) {
1306 ss_fragment << "struct " << iface->name << " {" << std::endl;
1307 for (const StageInterfaceInfo::InOut &inout : iface->inouts) {
1308 ss_fragment << to_string(inout.type) << " " << inout.name << ""
1309 << to_string_msl(inout.interp) << ";" << std::endl;
1310 }
1311 ss_fragment << "};" << std::endl;
1312 }
1313 }
1314
1315 /* Wrap entire GLSL source inside class to create
1316 * a scope within the class to enable use of global variables. */
1317 ss_fragment << "class " << get_stage_class_name(ShaderStage::FRAGMENT) << " {" << std::endl;
1318 ss_fragment << "public:" << std::endl;
1319
1320 /* In/out interface values */
1321 /* Generate additional shader interface struct members from create-info. */
1322 for (const StageInterfaceInfo *iface : info->vertex_out_interfaces_) {
1323 bool is_inside_struct = false;
1324 if (!iface->instance_name.is_empty()) {
1325 /* Struct local variable. */
1326 ss_fragment << iface->name << " " << iface->instance_name << ";" << std::endl;
1327 is_inside_struct = true;
1328 }
1329
1330 /* Generate local variables, populate elems for vertex out struct gen. */
1331 for (const StageInterfaceInfo::InOut &inout : iface->inouts) {
1332 /* Only output individual elements if they are not part of an interface struct instance.
1333 */
1334 if (!is_inside_struct) {
1335 ss_fragment << to_string(inout.type) << " " << inout.name << ";" << std::endl;
1336 }
1337 }
1338 }
1339
1340 /* Generate global structs */
1341 ss_fragment << msl_iface.generate_msl_vertex_out_struct(ShaderStage::FRAGMENT);
1342 if (msl_iface.fragment_tile_inputs.size() > 0) {
1343 ss_fragment << msl_iface.generate_msl_fragment_struct(true);
1344 }
1345 ss_fragment << msl_iface.generate_msl_fragment_struct(false);
1346 ss_fragment << msl_iface.generate_msl_uniform_structs(ShaderStage::FRAGMENT);
1347
1349 /* gl_FragCoord will always be assigned to the output position from vertex shading. */
1350 ss_fragment << "float4 gl_FragCoord;" << std::endl;
1351 if (msl_iface.uses_gl_FragColor) {
1352 ss_fragment << "float4 gl_FragColor;" << std::endl;
1353 }
1354 if (msl_iface.uses_gl_FragDepth) {
1355 ss_fragment << "float gl_FragDepth;" << std::endl;
1356 }
1357 if (msl_iface.uses_gl_FragStencilRefARB) {
1358 ss_fragment << "int gl_FragStencilRefARB;" << std::endl;
1359 }
1360 if (msl_iface.uses_gl_PointCoord) {
1361 ss_fragment << "float2 gl_PointCoord;" << std::endl;
1362 }
1363 if (msl_iface.uses_gl_FrontFacing) {
1364 ss_fragment << "bool gl_FrontFacing;" << std::endl;
1365 }
1366 if (msl_iface.uses_gl_PrimitiveID) {
1367 ss_fragment << "uint gl_PrimitiveID;" << std::endl;
1368 }
1369
1370 /* Global barycentrics. */
1371 if (msl_iface.uses_barycentrics) {
1372 ss_fragment << "vec3 gpu_BaryCoord;\n";
1373 }
1374
1375 /* Render target array index and viewport array index passed from vertex shader. */
1376 if (msl_iface.uses_gpu_layer) {
1377 ss_fragment << "int gpu_Layer = 0;" << std::endl;
1378 }
1379 if (msl_iface.uses_gpu_viewport_index) {
1380 ss_fragment << "int gpu_ViewportIndex = 0;" << std::endl;
1381 }
1382
1383 /* Add Texture members. */
1384 for (const MSLTextureResource &tex : msl_iface.texture_samplers) {
1385 if (bool(tex.stage & ShaderStage::FRAGMENT)) {
1386 ss_fragment << "\tthread " << tex.get_msl_typestring_wrapper(false) << ";" << std::endl;
1387 }
1388 }
1389
1390 /* Inject Main GLSL Fragment Source into output stream. */
1391 ss_fragment << shd_builder_->glsl_fragment_source_ << std::endl;
1392
1393 /* Class Closing Bracket to end shader global scope. */
1394 ss_fragment << "};" << std::endl;
1395
1396 /* Generate Fragment entry-point function. */
1397 ss_fragment << msl_iface.generate_msl_fragment_entry_stub();
1398 }
1399
1400 /* DEBUG: Export source to file for manual verification. */
1401#if MTL_SHADER_DEBUG_EXPORT_SOURCE
1402 NSFileManager *sharedFM = [NSFileManager defaultManager];
1403 NSURL *app_bundle_url = [[NSBundle mainBundle] bundleURL];
1404 NSURL *shader_dir = [[app_bundle_url URLByDeletingLastPathComponent]
1405 URLByAppendingPathComponent:@"Shaders/"
1406 isDirectory:YES];
1407 [sharedFM createDirectoryAtURL:shader_dir
1408 withIntermediateDirectories:YES
1409 attributes:nil
1410 error:nil];
1411 const char *path_cstr = [shader_dir fileSystemRepresentation];
1412
1413 std::ofstream vertex_fs;
1414 vertex_fs.open(
1415 (std::string(path_cstr) + "/" + std::string(this->name) + "_GeneratedVertexShader.msl")
1416 .c_str());
1417 vertex_fs << ss_vertex.str();
1418 vertex_fs.close();
1419
1420 if (!msl_iface.uses_transform_feedback) {
1421 std::ofstream fragment_fs;
1422 fragment_fs.open(
1423 (std::string(path_cstr) + "/" + std::string(this->name) + "_GeneratedFragmentShader.msl")
1424 .c_str());
1425 fragment_fs << ss_fragment.str();
1426 fragment_fs.close();
1427 }
1428
1430 "Vertex Shader Saved to: %s\n",
1431 (std::string(path_cstr) + std::string(this->name) + "_GeneratedFragmentShader.msl").c_str());
1432#endif
1433
1434 /* Set MSL source NSString's. Required by Metal API. */
1435 NSString *msl_final_vert = [NSString stringWithUTF8String:ss_vertex.str().c_str()];
1436 NSString *msl_final_frag = (msl_iface.uses_transform_feedback) ?
1437 (@"") :
1438 ([NSString stringWithUTF8String:ss_fragment.str().c_str()]);
1439
1440 this->shader_source_from_msl(msl_final_vert, msl_final_frag);
1441
1442#ifndef NDEBUG
1443 /* In debug mode, we inject the name of the shader into the entry-point function
1444 * name, as these are what show up in the Xcode GPU debugger. */
1446 [[NSString stringWithFormat:@"vertex_function_entry_%s", this->name] retain]);
1448 [[NSString stringWithFormat:@"fragment_function_entry_%s", this->name] retain]);
1449#else
1450 this->set_vertex_function_name(@"vertex_function_entry");
1451 this->set_fragment_function_name(@"fragment_function_entry");
1452#endif
1453
1454 /* Bake shader interface. */
1455 this->set_interface(msl_iface.bake_shader_interface(this->name, info));
1456
1457 /* Update other shader properties. */
1458 uses_gpu_layer = msl_iface.uses_gpu_layer;
1459 uses_gpu_viewport_index = msl_iface.uses_gpu_viewport_index;
1460 use_ssbo_vertex_fetch_mode_ = msl_iface.uses_ssbo_vertex_fetch_mode;
1461 if (msl_iface.uses_ssbo_vertex_fetch_mode) {
1462 ssbo_vertex_fetch_output_prim_type_ = vertex_fetch_ssbo_output_prim_type;
1463 ssbo_vertex_fetch_output_num_verts_ = vertex_fetch_ssbo_num_output_verts;
1465 }
1466
1467 /* Successfully completed GLSL to MSL translation. */
1468 return true;
1469}
1470
1471bool MTLShader::generate_msl_from_glsl_compute(const shader::ShaderCreateInfo *info)
1472{
1473 /* #MSLGeneratorInterface is a class populated to describe all parameters, resources, bindings
1474 * and features used by the source GLSL shader. This information is then used to generate the
1475 * appropriate Metal entry points and perform any required source translation. */
1476 MSLGeneratorInterface msl_iface(*this);
1477 BLI_assert(shd_builder_ != nullptr);
1478
1479 /* Populate #MSLGeneratorInterface from Create-Info.
1480 * NOTE: this is a separate path as #MSLGeneratorInterface can also be manually populated
1481 * from parsing, if support for shaders without create-info is required. */
1482 msl_iface.prepare_from_createinfo(info);
1483
1484 /* Verify Source sizes are greater than zero. */
1485 BLI_assert(shd_builder_->glsl_compute_source_.size() > 0);
1486
1487 /*** Source cleanup. ***/
1490
1491 /**** Extract usage of GL globals. ****/
1492 /* NOTE(METAL): Currently still performing fallback string scan, as info->builtins_ does
1493 * not always contain the usage flag. This can be removed once all appropriate create-info's
1494 * have been updated. In some cases, this may incur a false positive if access is guarded
1495 * behind a macro. Though in these cases, unused code paths and parameters will be
1496 * optimized out by the Metal shader compiler. */
1497
1498 /* gl_GlobalInvocationID. */
1499 msl_iface.uses_gl_GlobalInvocationID =
1500 bool(info->builtins_ & BuiltinBits::GLOBAL_INVOCATION_ID) ||
1501 shd_builder_->glsl_compute_source_.find("gl_GlobalInvocationID") != std::string::npos;
1502 /* gl_WorkGroupSize. */
1503 msl_iface.uses_gl_WorkGroupSize = bool(info->builtins_ & BuiltinBits::WORK_GROUP_SIZE) ||
1504 shd_builder_->glsl_compute_source_.find("gl_WorkGroupSize") !=
1505 std::string::npos;
1506 /* gl_WorkGroupID. */
1507 msl_iface.uses_gl_WorkGroupID = bool(info->builtins_ & BuiltinBits::WORK_GROUP_ID) ||
1508 shd_builder_->glsl_compute_source_.find("gl_WorkGroupID") !=
1509 std::string::npos;
1510 /* gl_NumWorkGroups. */
1511 msl_iface.uses_gl_NumWorkGroups = bool(info->builtins_ & BuiltinBits::NUM_WORK_GROUP) ||
1512 shd_builder_->glsl_compute_source_.find("gl_NumWorkGroups") !=
1513 std::string::npos;
1514 /* gl_LocalInvocationIndex. */
1515 msl_iface.uses_gl_LocalInvocationIndex =
1516 bool(info->builtins_ & BuiltinBits::LOCAL_INVOCATION_INDEX) ||
1517 shd_builder_->glsl_compute_source_.find("gl_LocalInvocationIndex") != std::string::npos;
1518 /* gl_LocalInvocationID. */
1519 msl_iface.uses_gl_LocalInvocationID = bool(info->builtins_ & BuiltinBits::LOCAL_INVOCATION_ID) ||
1520 shd_builder_->glsl_compute_source_.find(
1521 "gl_LocalInvocationID") != std::string::npos;
1522
1523 /* Performance warning: Extract global-scope expressions.
1524 * NOTE: This is dependent on stripping out comments
1525 * to remove false positives. */
1528
1537 extract_shared_memory_blocks(msl_iface, shd_builder_->glsl_compute_source_);
1538
1539 /* Replace 'out' attribute on function parameters with pass-by-reference. */
1541
1543 std::stringstream ss_compute;
1544 ss_compute << "#line 1 \"msl_wrapper_code\"\n";
1545
1546 ss_compute << "#define GPU_ARB_shader_draw_parameters 1\n";
1547 if (bool(info->builtins_ & BuiltinBits::TEXTURE_ATOMIC) &&
1549 {
1550 ss_compute << ATOMIC_DEFINE_STR;
1551 }
1552
1554
1555#ifndef NDEBUG
1556 extract_global_scope_constants(shd_builder_->glsl_compute_source_, ss_compute);
1557#endif
1558
1559 /* Conditional defines. */
1560 if (msl_iface.use_argument_buffer_for_samplers()) {
1561 ss_compute << "#define USE_ARGUMENT_BUFFER_FOR_SAMPLERS 1" << std::endl;
1562 ss_compute << "#define ARGUMENT_BUFFER_NUM_SAMPLERS "
1563 << msl_iface.max_sampler_index_for_stage(ShaderStage::COMPUTE) + 1 << std::endl;
1564 }
1565
1566 /* Inject static workgroup sizes. */
1567 if (msl_iface.uses_gl_WorkGroupSize) {
1568 }
1569
1570 /* Inject constant work group sizes. */
1571 if (msl_iface.uses_gl_WorkGroupSize) {
1572 ss_compute << "#define MTL_USE_WORKGROUP_SIZE 1" << std::endl;
1573 ss_compute << "#define MTL_WORKGROUP_SIZE_X " << info->compute_layout_.local_size_x
1574 << std::endl;
1575 ss_compute << "#define MTL_WORKGROUP_SIZE_Y "
1576 << ((info->compute_layout_.local_size_y != -1) ?
1578 1)
1579 << std::endl;
1580 ss_compute << "#define MTL_WORKGROUP_SIZE_Z "
1581 << ((info->compute_layout_.local_size_y != -1) ?
1583 1)
1584 << std::endl;
1585 }
1586
1587 /* Inject common Metal header. */
1588 ss_compute << msl_iface.msl_patch_default_get() << std::endl << std::endl;
1589
1590 /* Wrap entire GLSL source inside class to create
1591 * a scope within the class to enable use of global variables.
1592 * e.g. global access to attributes, uniforms, UBOs, textures etc; */
1593 ss_compute << "class " << get_stage_class_name(ShaderStage::COMPUTE) << " {" << std::endl;
1594 ss_compute << "public:" << std::endl;
1595
1596 /* Generate Uniform data structs. */
1597 ss_compute << msl_iface.generate_msl_uniform_structs(ShaderStage::VERTEX);
1598
1599 /* Add Texture members.
1600 * These members pack both a texture and a sampler into a single
1601 * struct, as both are needed within texture functions.
1602 * e.g. `_mtl_combined_image_sampler_2d<float, access::read>`
1603 * The exact typename is generated inside `get_msl_typestring_wrapper()`. */
1604 for (const MSLTextureResource &tex : msl_iface.texture_samplers) {
1605 if (bool(tex.stage & ShaderStage::COMPUTE)) {
1606 ss_compute << "\tthread " << tex.get_msl_typestring_wrapper(false) << ";" << std::endl;
1607 }
1608 }
1609 ss_compute << std::endl;
1610
1611 /* Conditionally use global GL variables. */
1612 if (msl_iface.uses_gl_GlobalInvocationID) {
1613 ss_compute << "uint3 gl_GlobalInvocationID;" << std::endl;
1614 }
1615 if (msl_iface.uses_gl_WorkGroupID) {
1616 ss_compute << "uint3 gl_WorkGroupID;" << std::endl;
1617 }
1618 if (msl_iface.uses_gl_NumWorkGroups) {
1619 ss_compute << "uint3 gl_NumWorkGroups;" << std::endl;
1620 }
1621 if (msl_iface.uses_gl_LocalInvocationIndex) {
1622 ss_compute << "uint gl_LocalInvocationIndex;" << std::endl;
1623 }
1624 if (msl_iface.uses_gl_LocalInvocationID) {
1625 ss_compute << "uint3 gl_LocalInvocationID;" << std::endl;
1626 }
1627
1628 /* Inject main GLSL source into output stream. */
1629 ss_compute << shd_builder_->glsl_compute_source_ << std::endl;
1630
1631 /* Compute constructor for Shared memory blocks, as we must pass
1632 * local references from entry-point function scope into the class
1633 * instantiation. */
1634 ss_compute << get_stage_class_name(ShaderStage::COMPUTE) << "(";
1635 bool first = true;
1636 if (msl_iface.shared_memory_blocks.size() > 0) {
1637 for (const MSLSharedMemoryBlock &block : msl_iface.shared_memory_blocks) {
1638 if (!first) {
1639 ss_compute << ",";
1640 }
1641 if (block.is_array) {
1642 ss_compute << "TG " << block.type_name << " (&_" << block.varname << ")"
1643 << block.array_decl;
1644 }
1645 else {
1646 ss_compute << "TG " << block.type_name << " &_" << block.varname;
1647 }
1648 ss_compute << std::endl;
1649 first = false;
1650 }
1651 ss_compute << ") : ";
1652 first = true;
1653 for (const MSLSharedMemoryBlock &block : msl_iface.shared_memory_blocks) {
1654 if (!first) {
1655 ss_compute << ",";
1656 }
1657 ss_compute << block.varname << "(_" << block.varname << ")";
1658 first = false;
1659 }
1660 }
1661 else {
1662 ss_compute << ") ";
1663 }
1664 ss_compute << "{ }" << std::endl;
1665
1666 /* Class Closing Bracket to end shader global scope. */
1667 ss_compute << "};" << std::endl;
1668
1669 /* Generate Vertex shader entry-point function containing resource bindings. */
1670 ss_compute << msl_iface.generate_msl_compute_entry_stub();
1671
1672#ifndef NDEBUG
1673 /* In debug mode, we inject the name of the shader into the entry-point function
1674 * name, as these are what show up in the Xcode GPU debugger. */
1676 [[NSString stringWithFormat:@"compute_function_entry_%s", this->name] retain]);
1677#else
1678 this->set_compute_function_name(@"compute_function_entry");
1679#endif
1680
1681 /* DEBUG: Export source to file for manual verification. */
1682#if MTL_SHADER_DEBUG_EXPORT_SOURCE
1683 NSFileManager *sharedFM = [NSFileManager defaultManager];
1684 NSURL *app_bundle_url = [[NSBundle mainBundle] bundleURL];
1685 NSURL *shader_dir = [[app_bundle_url URLByDeletingLastPathComponent]
1686 URLByAppendingPathComponent:@"Shaders/"
1687 isDirectory:YES];
1688 [sharedFM createDirectoryAtURL:shader_dir
1689 withIntermediateDirectories:YES
1690 attributes:nil
1691 error:nil];
1692 const char *path_cstr = [shader_dir fileSystemRepresentation];
1693
1694 std::ofstream compute_fs;
1695 compute_fs.open(
1696 (std::string(path_cstr) + "/" + std::string(this->name) + "_GeneratedComputeShader.msl")
1697 .c_str());
1698 compute_fs << ss_compute.str();
1699 compute_fs.close();
1700
1702 "Compute Shader Saved to: %s\n",
1703 (std::string(path_cstr) + std::string(this->name) + "_GeneratedComputeShader.msl").c_str());
1704#endif
1705
1706 NSString *msl_final_compute = [NSString stringWithUTF8String:ss_compute.str().c_str()];
1707 this->shader_compute_source_from_msl(msl_final_compute);
1708
1709 /* Bake shader interface. */
1710 this->set_interface(msl_iface.bake_shader_interface(this->name, info));
1711
1712 /* Compute dims. */
1713 this->compute_pso_common_state_.set_compute_workgroup_size(
1717
1718 /* Successfully completed GLSL to MSL translation. */
1719 return true;
1720}
1721
1722constexpr size_t const_strlen(const char *str)
1723{
1724 return (*str == '\0') ? 0 : const_strlen(str + 1) + 1;
1725}
1726
1728{
1729 BLI_assert(use_ssbo_vertex_fetch_mode_);
1730
1731 /* Cache global SSBO-vertex-fetch uniforms locations. */
1734 const ShaderInput *inp_uses_indexed_rendering = interface->uniform_get(
1736 const ShaderInput *inp_uses_index_mode_u16 = interface->uniform_get(
1739
1740 this->uni_ssbo_input_prim_type_loc = (inp_prim_type != nullptr) ? inp_prim_type->location : -1;
1741 this->uni_ssbo_input_vert_count_loc = (inp_vert_count != nullptr) ? inp_vert_count->location :
1742 -1;
1743 this->uni_ssbo_index_base_loc = (inp_index_base != nullptr) ? inp_index_base->location : -1;
1744
1745 this->uni_ssbo_uses_indexed_rendering = (inp_uses_indexed_rendering != nullptr) ?
1746 inp_uses_indexed_rendering->location :
1747 -1;
1748 this->uni_ssbo_uses_index_mode_u16 = (inp_uses_index_mode_u16 != nullptr) ?
1749 inp_uses_index_mode_u16->location :
1750 -1;
1751
1753 "uni_ssbo_input_prim_type_loc uniform location invalid!");
1755 "uni_ssbo_input_vert_count_loc uniform location invalid!");
1757 "uni_ssbo_uses_indexed_rendering uniform location invalid!");
1759 "uni_ssbo_uses_index_mode_u16 uniform location invalid!");
1761 "uni_ssbo_index_base_loc uniform location invalid!");
1762
1763 /* Prepare SSBO-vertex-fetch attribute uniform location cache. */
1764 MTLShaderInterface *mtl_interface = this->get_interface();
1765 for (int i = 0; i < mtl_interface->get_total_attributes(); i++) {
1766 const MTLShaderInputAttribute &mtl_shader_attribute = mtl_interface->get_attribute(i);
1767 const char *attr_name = mtl_interface->get_name_at_offset(mtl_shader_attribute.name_offset);
1768
1769 /* SSBO-vertex-fetch Attribute data is passed via uniforms. here we need to extract the uniform
1770 * address for each attribute, and we can cache it for later use. */
1771 ShaderSSBOAttributeBinding &cached_ssbo_attr = cached_ssbo_attribute_bindings_[i];
1772 cached_ssbo_attr.attribute_index = i;
1773
1774 constexpr int len_UNIFORM_SSBO_STRIDE_STR = const_strlen(UNIFORM_SSBO_STRIDE_STR);
1775 constexpr int len_UNIFORM_SSBO_OFFSET_STR = const_strlen(UNIFORM_SSBO_OFFSET_STR);
1776 constexpr int len_UNIFORM_SSBO_FETCHMODE_STR = const_strlen(UNIFORM_SSBO_FETCHMODE_STR);
1777 constexpr int len_UNIFORM_SSBO_VBO_ID_STR = const_strlen(UNIFORM_SSBO_VBO_ID_STR);
1778 constexpr int len_UNIFORM_SSBO_TYPE_STR = const_strlen(UNIFORM_SSBO_TYPE_STR);
1779
1780 char strattr_buf_stride[GPU_VERT_ATTR_MAX_LEN + len_UNIFORM_SSBO_STRIDE_STR + 1] =
1782 char strattr_buf_offset[GPU_VERT_ATTR_MAX_LEN + len_UNIFORM_SSBO_OFFSET_STR + 1] =
1784 char strattr_buf_fetchmode[GPU_VERT_ATTR_MAX_LEN + len_UNIFORM_SSBO_FETCHMODE_STR + 1] =
1786 char strattr_buf_vbo_id[GPU_VERT_ATTR_MAX_LEN + len_UNIFORM_SSBO_VBO_ID_STR + 1] =
1788 char strattr_buf_type[GPU_VERT_ATTR_MAX_LEN + len_UNIFORM_SSBO_TYPE_STR + 1] =
1790
1792 &strattr_buf_stride[len_UNIFORM_SSBO_STRIDE_STR], attr_name, GPU_VERT_ATTR_MAX_LEN);
1794 &strattr_buf_offset[len_UNIFORM_SSBO_OFFSET_STR], attr_name, GPU_VERT_ATTR_MAX_LEN);
1796 &strattr_buf_fetchmode[len_UNIFORM_SSBO_FETCHMODE_STR], attr_name, GPU_VERT_ATTR_MAX_LEN);
1798 &strattr_buf_vbo_id[len_UNIFORM_SSBO_VBO_ID_STR], attr_name, GPU_VERT_ATTR_MAX_LEN);
1799 BLI_strncpy(&strattr_buf_type[len_UNIFORM_SSBO_TYPE_STR], attr_name, GPU_VERT_ATTR_MAX_LEN);
1800
1801 /* Fetch uniform locations and cache for fast access. */
1802 const ShaderInput *inp_unf_stride = mtl_interface->uniform_get(strattr_buf_stride);
1803 const ShaderInput *inp_unf_offset = mtl_interface->uniform_get(strattr_buf_offset);
1804 const ShaderInput *inp_unf_fetchmode = mtl_interface->uniform_get(strattr_buf_fetchmode);
1805 const ShaderInput *inp_unf_vbo_id = mtl_interface->uniform_get(strattr_buf_vbo_id);
1806 const ShaderInput *inp_unf_attr_type = mtl_interface->uniform_get(strattr_buf_type);
1807
1808 BLI_assert(inp_unf_stride != nullptr);
1809 BLI_assert(inp_unf_offset != nullptr);
1810 BLI_assert(inp_unf_fetchmode != nullptr);
1811 BLI_assert(inp_unf_vbo_id != nullptr);
1812 BLI_assert(inp_unf_attr_type != nullptr);
1813
1814 cached_ssbo_attr.uniform_stride = (inp_unf_stride != nullptr) ? inp_unf_stride->location : -1;
1815 cached_ssbo_attr.uniform_offset = (inp_unf_offset != nullptr) ? inp_unf_offset->location : -1;
1816 cached_ssbo_attr.uniform_fetchmode = (inp_unf_fetchmode != nullptr) ?
1817 inp_unf_fetchmode->location :
1818 -1;
1819 cached_ssbo_attr.uniform_vbo_id = (inp_unf_vbo_id != nullptr) ? inp_unf_vbo_id->location : -1;
1820 cached_ssbo_attr.uniform_attr_type = (inp_unf_attr_type != nullptr) ?
1821 inp_unf_attr_type->location :
1822 -1;
1823
1824 BLI_assert(cached_ssbo_attr.uniform_offset != -1);
1825 BLI_assert(cached_ssbo_attr.uniform_stride != -1);
1826 BLI_assert(cached_ssbo_attr.uniform_fetchmode != -1);
1827 BLI_assert(cached_ssbo_attr.uniform_vbo_id != -1);
1828 BLI_assert(cached_ssbo_attr.uniform_attr_type != -1);
1829 }
1830}
1831
1833{
1835 create_info_ = info;
1836
1839 MSLUniform uniform(push_constant.type,
1840 push_constant.name,
1841 bool(push_constant.array_size > 1),
1842 push_constant.array_size);
1843 uniforms.append(uniform);
1844 }
1845
1847 for (const auto &constant : create_info_->specialization_constants_) {
1848 constants.append(MSLConstant(constant.type, constant.name));
1849 }
1850
1851 /* Prepare textures and uniform blocks.
1852 * Perform across both resource categories and extract both
1853 * texture samplers and image types. */
1854
1855 /* NOTE: Metal requires Samplers and images to share slots. We will re-map these.
1856 * If `auto_resource_location_` is not used, then slot collision could occur and
1857 * this should be resolved in the original create-info.
1858 * UBOs and SSBOs also share the same bind table. */
1859 int texture_slot_id = 0;
1860 int ubo_buffer_slot_id_ = 0;
1861 int storage_buffer_slot_id_ = 0;
1862
1863 uint max_storage_buffer_location = 0;
1864
1866
1867 /* Determine max sampler slot for image resource offset, when not using auto resource location,
1868 * as image resources cannot overlap sampler ranges. */
1869 int max_sampler_slot = 0;
1870 if (!create_info_->auto_resource_location_) {
1871 for (const ShaderCreateInfo::Resource &res : all_resources) {
1873 max_sampler_slot = max_ii(res.slot, max_sampler_slot);
1874 }
1875 }
1876 }
1877
1878 for (const ShaderCreateInfo::Resource &res : all_resources) {
1879 /* TODO(Metal): Consider adding stage flags to textures in create info. */
1880 /* Handle sampler types. */
1881 switch (res.bind_type) {
1883
1884 /* Samplers to have access::sample by default. */
1886 /* TextureBuffers must have read/write/read-write access pattern. */
1887 if (res.sampler.type == ImageType::FLOAT_BUFFER ||
1888 res.sampler.type == ImageType::INT_BUFFER ||
1889 res.sampler.type == ImageType::UINT_BUFFER)
1890 {
1892 }
1893
1894 MSLTextureResource msl_tex;
1895 msl_tex.stage = ShaderStage::ANY;
1896 msl_tex.type = res.sampler.type;
1897 msl_tex.name = res.sampler.name;
1898 msl_tex.access = access;
1899 msl_tex.slot = texture_slot_id++;
1900 msl_tex.location = (create_info_->auto_resource_location_) ? msl_tex.slot : res.slot;
1901 msl_tex.is_texture_sampler = true;
1903
1904 texture_samplers.append(msl_tex);
1906 } break;
1907
1909 /* Flatten qualifier flags into final access state. */
1911 if ((res.image.qualifiers & Qualifier::READ_WRITE) == Qualifier::READ_WRITE) {
1913 }
1914 else if (bool(res.image.qualifiers & Qualifier::WRITE)) {
1916 }
1917 else {
1919 }
1920
1921 /* Writeable image targets only assigned to Fragment and compute shaders. */
1922 MSLTextureResource msl_image;
1924 msl_image.type = res.image.type;
1925 msl_image.name = res.image.name;
1926 msl_image.access = access;
1927 msl_image.slot = texture_slot_id++;
1928 msl_image.location = (create_info_->auto_resource_location_) ? msl_image.slot : res.slot;
1929 msl_image.is_texture_sampler = false;
1931
1932 texture_samplers.append(msl_image);
1934 } break;
1935
1937 MSLBufferBlock ubo;
1938 BLI_assert(res.uniformbuf.type_name.size() > 0);
1939 BLI_assert(res.uniformbuf.name.size() > 0);
1940 int64_t array_offset = res.uniformbuf.name.find_first_of("[");
1941
1942 /* We maintain two bind indices. "Slot" refers to the storage index buffer(N) in which
1943 * we will bind the resource. "Location" refers to the explicit bind index specified
1944 * in ShaderCreateInfo.
1945 * NOTE: ubo.slot is offset by one, as first UBO slot is reserved for push constant data.
1946 */
1947 ubo.slot = 1 + (ubo_buffer_slot_id_++);
1948 ubo.location = (create_info_->auto_resource_location_) ? ubo.slot : res.slot;
1949
1951
1953 ubo.type_name = res.uniformbuf.type_name;
1954 ubo.is_texture_buffer = false;
1955 ubo.is_array = (array_offset > -1);
1956 if (ubo.is_array) {
1957 /* If is array UBO, strip out array tag from name. */
1958 StringRef name_no_array = StringRef(res.uniformbuf.name.c_str(), array_offset);
1959 ubo.name = name_no_array;
1960 }
1961 else {
1962 ubo.name = res.uniformbuf.name;
1963 }
1964 ubo.stage = ShaderStage::ANY;
1965 uniform_blocks.append(ubo);
1966 } break;
1967
1969 MSLBufferBlock ssbo;
1970 BLI_assert(res.storagebuf.type_name.size() > 0);
1971 BLI_assert(res.storagebuf.name.size() > 0);
1972 int64_t array_offset = res.storagebuf.name.find_first_of("[");
1973
1974 /* We maintain two bind indices. "Slot" refers to the storage index buffer(N) in which
1975 * we will bind the resource. "Location" refers to the explicit bind index specified
1976 * in ShaderCreateInfo. */
1977 ssbo.slot = storage_buffer_slot_id_++;
1978 ssbo.location = (create_info_->auto_resource_location_) ? ssbo.slot : res.slot;
1979
1980 max_storage_buffer_location = max_uu(max_storage_buffer_location, ssbo.location);
1981
1983
1984 ssbo.qualifiers = res.storagebuf.qualifiers;
1985 ssbo.type_name = res.storagebuf.type_name;
1986 ssbo.is_texture_buffer = false;
1987 ssbo.is_array = (array_offset > -1);
1988 if (ssbo.is_array) {
1989 /* If is array UBO, strip out array tag from name. */
1990 StringRef name_no_array = StringRef(res.storagebuf.name.c_str(), array_offset);
1991 ssbo.name = name_no_array;
1992 }
1993 else {
1994 ssbo.name = res.storagebuf.name;
1995 }
1996 ssbo.stage = ShaderStage::ANY;
1997 storage_blocks.append(ssbo);
1998 } break;
1999 }
2000 }
2001
2002 /* For texture atomic fallback support, bind texture source buffers and data buffer as storage
2003 * blocks. */
2004 if (!MTLBackend::get_capabilities().supports_texture_atomics) {
2005 uint atomic_fallback_buffer_count = 0;
2007 if (ELEM(tex.type,
2008 ImageType::UINT_2D_ATOMIC,
2009 ImageType::UINT_2D_ARRAY_ATOMIC,
2010 ImageType::UINT_3D_ATOMIC,
2011 ImageType::INT_2D_ATOMIC,
2012 ImageType::INT_2D_ARRAY_ATOMIC,
2013 ImageType::INT_3D_ATOMIC))
2014 {
2015 /* Add storage-buffer bind-point. */
2016 MSLBufferBlock ssbo;
2017
2018 /* We maintain two bind indices. "Slot" refers to the storage index buffer(N) in which
2019 * we will bind the resource. "Location" refers to the explicit bind index specified
2020 * in ShaderCreateInfo.
2021 * NOTE: For texture buffers, we will accumulate these after all other storage buffers.
2022 */
2023 ssbo.slot = storage_buffer_slot_id_++;
2024 ssbo.location = max_storage_buffer_location + 1 + atomic_fallback_buffer_count;
2025
2026 /* Flag atomic fallback buffer id and location.
2027 * ID is used to determine order for accessing parameters, while
2028 * location is used to extract the explicit bind point for the buffer. */
2029 tex.atomic_fallback_buffer_ssbo_id = storage_blocks.size();
2030
2032
2033 /* Qualifier should be read write and type is either uint or int. */
2034 ssbo.qualifiers = Qualifier::READ_WRITE;
2035 ssbo.type_name = tex.get_msl_return_type_str();
2036 ssbo.is_array = false;
2037 ssbo.name = tex.name + "_storagebuf";
2038 ssbo.stage = ShaderStage::ANY;
2039 ssbo.is_texture_buffer = true;
2040 storage_blocks.append(ssbo);
2041
2042 /* Add uniform for metadata. */
2043 MSLUniform uniform(shader::Type::IVEC4, tex.name + "_metadata", false, 1);
2044 uniforms.append(uniform);
2045
2046 atomic_fallback_buffer_count++;
2047 }
2048 }
2049 }
2050
2051 /* Assign maximum buffer. */
2052 max_buffer_slot = storage_buffer_slot_id_ + ubo_buffer_slot_id_ + 1;
2053
2055 bool all_attr_location_assigned = true;
2056 for (const ShaderCreateInfo::VertIn &attr : info->vertex_inputs_) {
2057
2058 /* Validate input. */
2059 BLI_assert(attr.name.size() > 0);
2060
2061 /* NOTE(Metal): Input attributes may not have a location specified.
2062 * unset locations are resolved during: `resolve_input_attribute_locations`. */
2063 MSLVertexInputAttribute msl_attr;
2064 bool attr_location_assigned = (attr.index >= 0);
2065 all_attr_location_assigned = all_attr_location_assigned && attr_location_assigned;
2066 msl_attr.layout_location = attr_location_assigned ? attr.index : -1;
2067 msl_attr.type = attr.type;
2068 msl_attr.name = attr.name;
2069 vertex_input_attributes.append(msl_attr);
2070 }
2071
2072 /* Ensure all attributes are assigned a location. */
2073 if (!all_attr_location_assigned) {
2075 }
2076
2078 for (const shader::ShaderCreateInfo::FragOut &frag_out : create_info_->fragment_outputs_) {
2079
2080 /* Validate input. */
2081 BLI_assert(frag_out.name.size() > 0);
2082 BLI_assert(frag_out.index >= 0);
2083
2084 /* Populate MSLGenerator attribute. */
2085 MSLFragmentOutputAttribute mtl_frag_out;
2086 mtl_frag_out.layout_location = frag_out.index;
2087 mtl_frag_out.layout_index = (frag_out.blend != DualBlend::NONE) ?
2088 ((frag_out.blend == DualBlend::SRC_0) ? 0 : 1) :
2089 -1;
2090 mtl_frag_out.type = frag_out.type;
2091 mtl_frag_out.name = frag_out.name;
2092 mtl_frag_out.raster_order_group = frag_out.raster_order_group;
2093
2094 fragment_outputs.append(mtl_frag_out);
2095 }
2096
2098 const bool is_tile_based_arch = (GPU_platform_architecture() == GPU_ARCHITECTURE_TBDR);
2099 if (is_tile_based_arch) {
2101 }
2102 else {
2103 /* NOTE: If emulating tile input reads, we must ensure we also expose position data. */
2105 }
2106
2107 /* Fragment tile inputs. */
2108 for (const shader::ShaderCreateInfo::SubpassIn &frag_tile_in : create_info_->subpass_inputs_) {
2109
2110 /* Validate input. */
2111 BLI_assert(frag_tile_in.name.size() > 0);
2112 BLI_assert(frag_tile_in.index >= 0);
2113
2114 /* Populate MSLGenerator attribute. */
2116 mtl_frag_in.layout_location = frag_tile_in.index;
2117 mtl_frag_in.layout_index = (frag_tile_in.blend != DualBlend::NONE) ?
2118 ((frag_tile_in.blend == DualBlend::SRC_0) ? 0 : 1) :
2119 -1;
2120 mtl_frag_in.type = frag_tile_in.type;
2121 mtl_frag_in.name = frag_tile_in.name;
2122 mtl_frag_in.raster_order_group = frag_tile_in.raster_order_group;
2123
2124 fragment_tile_inputs.append(mtl_frag_in);
2125
2126 /* If we do not support native tile inputs, generate an image-binding per input. */
2128 /* Determine type: */
2129 bool is_layered_fb = bool(create_info_->builtins_ & BuiltinBits::LAYER);
2130 /* Start with invalid value to detect failure cases. */
2131 ImageType image_type = ImageType::FLOAT_BUFFER;
2132 switch (frag_tile_in.type) {
2133 case Type::FLOAT:
2134 image_type = is_layered_fb ? ImageType::FLOAT_2D_ARRAY : ImageType::FLOAT_2D;
2135 break;
2136 case Type::INT:
2137 image_type = is_layered_fb ? ImageType::INT_2D_ARRAY : ImageType::INT_2D;
2138 break;
2139 case Type::UINT:
2140 image_type = is_layered_fb ? ImageType::UINT_2D_ARRAY : ImageType::UINT_2D;
2141 break;
2142 default:
2143 break;
2144 }
2145 BLI_assert(image_type != ImageType::FLOAT_BUFFER);
2146
2147 /* Generate texture binding resource. */
2148 MSLTextureResource msl_image;
2149 msl_image.stage = ShaderStage::FRAGMENT;
2150 msl_image.type = image_type;
2151 msl_image.name = frag_tile_in.name + "_subpass_img";
2153 msl_image.slot = texture_slot_id++;
2154 /* WATCH: We don't have a great place to generate the image bindings.
2155 * So we will use the subpass binding index and check if it collides with an existing
2156 * binding. */
2157 msl_image.location = frag_tile_in.index;
2158 msl_image.is_texture_sampler = false;
2161
2162 /* Check existing samplers. */
2163 for (const auto &tex : texture_samplers) {
2165 BLI_assert(tex.location != msl_image.location);
2166 }
2167
2168 texture_samplers.append(msl_image);
2170 }
2171 }
2172
2173 /* Transform feedback. */
2175 (create_info_->tf_names_.size() > 0);
2176}
2177
2179{
2180 /* We can only use argument buffers IF highest sampler index exceeds static limit of 16,
2181 * AND we can support more samplers with an argument buffer. */
2182 bool use_argument_buffer = (texture_samplers.size() >= 15 || max_tex_bind_index >= 14) &&
2183 GPU_max_samplers() > 15;
2184
2185#ifndef NDEBUG
2186 /* Due to explicit bind location support, we may be below the sampler limit, but forced to offset
2187 * bindings due to the range being high. Introduce debug check here to issue warning. In these
2188 * cases, if explicit bind location support is not required, best to use auto_resource_location
2189 * to optimize bind point packing. */
2190 if (use_argument_buffer && texture_samplers.size() < 15) {
2192 "Compiled Shader '%s' is falling back to bindless via argument buffers due to having a "
2193 "texture sampler of Index: %u Which exceeds the limit of 15+1. However shader only uses "
2194 "%d textures. Consider optimising bind points with .auto_resource_location(true).",
2195 parent_shader_.name_get(),
2197 (int)texture_samplers.size());
2198 }
2199#endif
2200
2201 return use_argument_buffer;
2202}
2203
2205{
2206 /* NOTE: Sampler bindings and argument buffer shared across stages,
2207 * in case stages share texture/sampler bindings. */
2208 return texture_samplers.size();
2209}
2210
2212{
2213 /* NOTE: Sampler bindings and argument buffer shared across stages,
2214 * in case stages share texture/sampler bindings. */
2215 return max_tex_bind_index;
2216}
2217
2219{
2220 /* NOTE: Shader stage must be a singular index. Compound shader masks are not valid for this
2221 * function. */
2226 }
2227
2228 /* Sampler argument buffer to follow UBOs and PushConstantBlock. */
2231}
2232
2234{
2236
2237 /* Add Special Uniforms for SSBO vertex fetch mode. */
2238 this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_INPUT_PRIM_TYPE_STR, false));
2239 this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_INPUT_VERT_COUNT_STR, false));
2240 this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_USES_INDEXED_RENDERING_STR, false));
2241 this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_INDEX_MODE_U16_STR, false));
2242 this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_INDEX_BASE_STR, false));
2243
2244 for (const MSLVertexInputAttribute &attr : this->vertex_input_attributes) {
2245 const std::string &uname = attr.name;
2246 this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_STRIDE_STR + uname, false));
2247 this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_OFFSET_STR + uname, false));
2248 this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_FETCHMODE_STR + uname, false));
2249 this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_VBO_ID_STR + uname, false));
2250 this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_TYPE_STR + uname, false));
2251 }
2252}
2253
2255{
2256 static const char *shader_stage_inst_name = get_shader_stage_instance_name(ShaderStage::VERTEX);
2257
2258 std::stringstream out;
2259 out << std::endl << "/*** AUTO-GENERATED MSL VERETX SHADER STUB. ***/" << std::endl;
2260
2261 /* Un-define texture defines from main source - avoid conflict with MSL texture. */
2262 out << "#undef texture" << std::endl;
2263 out << "#undef textureLod" << std::endl;
2264
2265 /* Disable special case for booleans being treated as ints in GLSL. */
2266 out << "#undef bool" << std::endl;
2267
2268 /* Un-define uniform mappings to avoid name collisions. */
2270
2271 /* Generate function entry point signature w/ resource bindings and inputs. */
2272 out << "vertex ";
2273 if (this->uses_transform_feedback) {
2274 out << "void ";
2275 }
2276 else {
2277 out << get_stage_class_name(ShaderStage::VERTEX) << "::VertexOut ";
2278 }
2279#ifndef NDEBUG
2280 out << "vertex_function_entry_" << parent_shader_.name_get() << "(\n\t";
2281#else
2282 out << "vertex_function_entry(\n\t";
2283#endif
2284
2285 out << this->generate_msl_vertex_inputs_string();
2286 out << ") {" << std::endl << std::endl;
2287 out << "\t" << get_stage_class_name(ShaderStage::VERTEX) << "::VertexOut output;" << std::endl
2288 << "\t" << get_stage_class_name(ShaderStage::VERTEX) << " " << shader_stage_inst_name << ";"
2289 << std::endl;
2290
2291 /* Copy Vertex Globals. */
2292 if (this->uses_gl_VertexID) {
2293 out << shader_stage_inst_name << ".gl_VertexID = gl_VertexID;" << std::endl;
2294 }
2295 if (this->uses_gl_InstanceID) {
2296 out << shader_stage_inst_name << ".gl_InstanceID = gl_InstanceID-gl_BaseInstanceARB;"
2297 << std::endl;
2298 }
2299 if (this->uses_gl_BaseInstanceARB) {
2300 out << shader_stage_inst_name << ".gl_BaseInstanceARB = gl_BaseInstanceARB;" << std::endl;
2301 }
2302
2303 /* Copy vertex attributes into local variables. */
2305
2306 /* Populate Uniforms and uniform blocks. */
2310
2311 /* Execute original 'main' function within class scope. */
2312 out << "\t/* Execute Vertex main function */\t" << std::endl
2313 << "\t" << shader_stage_inst_name << ".main();" << std::endl
2314 << std::endl;
2315
2316 /* Populate Output values. */
2318
2319 /* Final point size,
2320 * This is only compiled if the `MTL_global_pointsize` is specified
2321 * as a function specialization in the PSO. This is restricted to
2322 * point primitive types. */
2323 out << "if(is_function_constant_defined(MTL_global_pointsize)){ output.pointsize = "
2324 "(MTL_global_pointsize > 0.0)?MTL_global_pointsize:output.pointsize; }"
2325 << std::endl;
2326
2327 /* Populate transform feedback buffer. */
2328 if (this->uses_transform_feedback) {
2330 }
2331 else {
2332 out << "\treturn output;" << std::endl;
2333 }
2334 out << "}";
2335 return out.str();
2336}
2337
2339{
2340 static const char *shader_stage_inst_name = get_shader_stage_instance_name(
2342 std::stringstream out;
2343 out << std::endl << "/*** AUTO-GENERATED MSL FRAGMENT SHADER STUB. ***/" << std::endl;
2344
2345 /* Undefine texture defines from main source - avoid conflict with MSL texture. */
2346 out << "#undef texture" << std::endl;
2347 out << "#undef textureLod" << std::endl;
2348
2349 /* Disable special case for booleans being treated as integers in GLSL. */
2350 out << "#undef bool" << std::endl;
2351
2352 /* Undefine uniform mappings to avoid name collisions. */
2354
2355 /* Early fragment tests. */
2357 out << "[[early_fragment_tests]]" << std::endl;
2358 }
2359
2360 /* Generate function entry point signature w/ resource bindings and inputs. */
2361#ifndef NDEBUG
2362 out << "fragment " << get_stage_class_name(ShaderStage::FRAGMENT)
2363 << "::" FRAGMENT_OUT_STRUCT_NAME " fragment_function_entry_" << parent_shader_.name_get()
2364 << "(\n\t";
2365#else
2366 out << "fragment " << get_stage_class_name(ShaderStage::FRAGMENT)
2367 << "::" FRAGMENT_OUT_STRUCT_NAME " fragment_function_entry(\n\t";
2368#endif
2370 out << ") {" << std::endl << std::endl;
2372 << "::" FRAGMENT_OUT_STRUCT_NAME " output;" << std::endl
2373 << "\t" << get_stage_class_name(ShaderStage::FRAGMENT) << " " << shader_stage_inst_name
2374 << ";" << std::endl;
2375
2376 /* Copy Fragment Globals. */
2377 if (this->uses_gl_PointCoord) {
2378 out << shader_stage_inst_name << ".gl_PointCoord = gl_PointCoord;" << std::endl;
2379 }
2380 if (this->uses_gl_FrontFacing) {
2381 out << shader_stage_inst_name << ".gl_FrontFacing = gl_FrontFacing;" << std::endl;
2382 }
2383 if (this->uses_gl_PrimitiveID) {
2384 out << "fragment_shader_instance.gl_PrimitiveID = gl_PrimitiveID;" << std::endl;
2385 }
2386
2387 /* Copy vertex attributes into local variable.s */
2389
2390 /* Barycentrics. */
2391 if (this->uses_barycentrics) {
2392 out << shader_stage_inst_name << ".gpu_BaryCoord = mtl_barycentric_coord.xyz;" << std::endl;
2393 }
2394
2395 /* Populate Uniforms and uniform blocks. */
2399
2400 /* Populate fragment tile-in members. */
2401 if (this->fragment_tile_inputs.size() > 0) {
2403 }
2404
2405 /* Execute original 'main' function within class scope. */
2406 out << "\t/* Execute Fragment main function */\t" << std::endl
2407 << "\t" << shader_stage_inst_name << ".main();" << std::endl
2408 << std::endl;
2409
2410 /* Populate Output values. */
2412 out << " return output;" << std::endl << "}";
2413
2414 return out.str();
2415}
2416
2418{
2419 static const char *shader_stage_inst_name = get_shader_stage_instance_name(ShaderStage::COMPUTE);
2420 std::stringstream out;
2421 out << std::endl << "/*** AUTO-GENERATED MSL COMPUTE SHADER STUB. ***/" << std::endl;
2422
2423 /* Un-define texture defines from main source - avoid conflict with MSL texture. */
2424 out << "#undef texture" << std::endl;
2425 out << "#undef textureLod" << std::endl;
2426
2427 /* Disable special case for booleans being treated as ints in GLSL. */
2428 out << "#undef bool" << std::endl;
2429
2430 /* Un-define uniform mappings to avoid name collisions. */
2432
2433 /* Generate function entry point signature w/ resource bindings and inputs. */
2434 out << "kernel void ";
2435#ifndef NDEBUG
2436 out << "compute_function_entry_" << parent_shader_.name_get() << "(\n\t";
2437#else
2438 out << "compute_function_entry(\n\t";
2439#endif
2440
2442 out << ") {" << std::endl << std::endl;
2443 /* Generate Compute shader instance constructor. If shared memory blocks are used,
2444 * these must be declared and then passed into the constructor. */
2445 std::string stage_instance_constructor = "";
2446 bool first = true;
2447 if (shared_memory_blocks.size() > 0) {
2448 stage_instance_constructor += "(";
2449 for (const MSLSharedMemoryBlock &block : shared_memory_blocks) {
2450 if (block.is_array) {
2451 out << "TG " << block.type_name << " " << block.varname << block.array_decl << ";";
2452 }
2453 else {
2454 out << "TG " << block.type_name << " " << block.varname << ";";
2455 }
2456 stage_instance_constructor += ((!first) ? "," : "") + block.varname;
2457 first = false;
2458
2459 out << std::endl;
2460 }
2461 stage_instance_constructor += ")";
2462 }
2463 out << "\t" << get_stage_class_name(ShaderStage::COMPUTE) << " " << shader_stage_inst_name
2464 << stage_instance_constructor << ";" << std::endl;
2465
2466 /* Copy global variables. */
2467 /* Entry point parameters for gl Globals. */
2468 if (this->uses_gl_GlobalInvocationID) {
2469 out << shader_stage_inst_name << ".gl_GlobalInvocationID = gl_GlobalInvocationID;"
2470 << std::endl;
2471 }
2472 if (this->uses_gl_WorkGroupID) {
2473 out << shader_stage_inst_name << ".gl_WorkGroupID = gl_WorkGroupID;" << std::endl;
2474 }
2475 if (this->uses_gl_NumWorkGroups) {
2476 out << shader_stage_inst_name << ".gl_NumWorkGroups = gl_NumWorkGroups;" << std::endl;
2477 }
2478 if (this->uses_gl_LocalInvocationIndex) {
2479 out << shader_stage_inst_name << ".gl_LocalInvocationIndex = gl_LocalInvocationIndex;"
2480 << std::endl;
2481 }
2482 if (this->uses_gl_LocalInvocationID) {
2483 out << shader_stage_inst_name << ".gl_LocalInvocationID = gl_LocalInvocationID;" << std::endl;
2484 }
2485
2486 /* Populate Uniforms and uniform blocks. */
2490
2491 /* Execute original 'main' function within class scope. */
2492 out << "\t/* Execute Compute main function */\t" << std::endl
2493 << "\t" << shader_stage_inst_name << ".main();" << std::endl
2494 << std::endl;
2495
2496 out << "}";
2497 return out.str();
2498}
2499
2500/* If first parameter in function signature, do not print out a comma.
2501 * Update first parameter flag to false for future invocations. */
2502static char parameter_delimiter(bool &is_first_parameter)
2503{
2504 if (is_first_parameter) {
2505 is_first_parameter = false;
2506 return ' ';
2507 }
2508 return ',';
2509}
2510
2513 bool &is_first_parameter)
2514{
2515 /* NOTE: Shader stage must be specified as the singular stage index for which the input
2516 * is generating. Compound stages are not valid inputs. */
2519 /* Generate texture signatures for textures used by this stage. */
2521 for (const MSLTextureResource &tex : this->texture_samplers) {
2522 if (bool(tex.stage & stage)) {
2523 out << parameter_delimiter(is_first_parameter) << "\n\t" << tex.get_msl_typestring(false)
2524 << " [[texture(" << tex.slot << ")]]";
2525 }
2526 }
2527
2528 /* Generate sampler signatures. */
2529 /* NOTE: Currently textures and samplers share indices across shading stages, so the limit is
2530 * shared.
2531 * If we exceed the hardware-supported limit, then follow a bind-less model using argument
2532 * buffers. */
2534 out << parameter_delimiter(is_first_parameter)
2535 << "\n\tconstant SStruct& samplers [[buffer(MTL_uniform_buffer_base_index+"
2536 << (this->get_sampler_argument_buffer_bind_index(stage)) << ")]]";
2537 }
2538 else {
2539 /* Maximum Limit of samplers defined in the function argument table is
2540 * `MTL_MAX_DEFAULT_SAMPLERS=16`. */
2542 for (const MSLTextureResource &tex : this->texture_samplers) {
2543 if (bool(tex.stage & stage)) {
2544 out << parameter_delimiter(is_first_parameter) << "\n\tsampler " << tex.name
2545 << "_sampler [[sampler(" << tex.slot << ")]]";
2546 }
2547 }
2548
2549 /* Fallback. */
2550 if (this->texture_samplers.size() > 16) {
2552 "[Metal] Warning: Shader exceeds limit of %u samplers on current hardware\n",
2554 }
2555 }
2556}
2557
2560 bool &is_first_parameter)
2561{
2562 for (const MSLBufferBlock &ubo : this->uniform_blocks) {
2563 if (bool(ubo.stage & stage)) {
2564 /* For literal/existing global types, we do not need the class name-space accessor. */
2565 out << parameter_delimiter(is_first_parameter) << "\n\tconstant ";
2566 if (!is_builtin_type(ubo.type_name)) {
2567 out << get_stage_class_name(stage) << "::";
2568 }
2569 /* #UniformBuffer bind indices start at `MTL_uniform_buffer_base_index + 1`, as
2570 * MTL_uniform_buffer_base_index is reserved for the #PushConstantBlock (push constants).
2571 * MTL_uniform_buffer_base_index is an offset depending on the number of unique VBOs
2572 * bound for the current PSO specialization. */
2573 out << ubo.type_name << "* " << ubo.name << "[[buffer(MTL_uniform_buffer_base_index+"
2574 << ubo.slot << ")]]";
2575 }
2576 }
2577
2578 /* Storage buffers. */
2579 for (const MSLBufferBlock &ssbo : this->storage_blocks) {
2580 if (bool(ssbo.stage & stage)) {
2581 /* For literal/existing global types, we do not need the class name-space accessor. */
2582 bool writeable = (ssbo.qualifiers & shader::Qualifier::WRITE) == shader::Qualifier::WRITE;
2583 const char *memory_scope = ((writeable) ? "device " : "constant ");
2584 out << parameter_delimiter(is_first_parameter) << "\n\t" << memory_scope;
2585 if (!is_builtin_type(ssbo.type_name)) {
2586 out << get_stage_class_name(stage) << "::";
2587 }
2588 /* #StorageBuffer bind indices start at `MTL_storage_buffer_base_index`.
2589 * MTL_storage_buffer_base_index follows immediately after all uniform blocks.
2590 * such that MTL_storage_buffer_base_index = MTL_uniform_buffer_base_index +
2591 * uniform_blocks.size() + 1. Where the additional buffer is reserved for the
2592 * #PushConstantBlock (push constants). */
2593 out << ssbo.type_name << "* " << ssbo.name << "[[buffer(MTL_storage_buffer_base_index+"
2594 << (ssbo.slot) << ")]]";
2595 }
2596 }
2597}
2598
2600{
2601 std::stringstream out;
2602 bool is_first_parameter = true;
2603
2604 if (this->uses_ssbo_vertex_fetch_mode) {
2605 /* Vertex Buffers bound as raw buffers. */
2606 for (int i = 0; i < MTL_SSBO_VERTEX_FETCH_MAX_VBOS; i++) {
2607 out << parameter_delimiter(is_first_parameter) << "\tconstant uchar* MTL_VERTEX_DATA_" << i
2608 << " [[buffer(" << i << ")]]\n";
2609 }
2610 out << parameter_delimiter(is_first_parameter)
2611 << "\tconstant ushort* MTL_INDEX_DATA[[buffer(MTL_SSBO_VERTEX_FETCH_IBO_INDEX)]]";
2612 }
2613 else {
2614 if (this->vertex_input_attributes.size() > 0) {
2615 /* Vertex Buffers use input assembly. */
2616 out << get_stage_class_name(ShaderStage::VERTEX) << "::VertexIn v_in [[stage_in]]";
2617 is_first_parameter = false;
2618 }
2619 }
2620
2621 if (this->uniforms.size() > 0) {
2622 out << parameter_delimiter(is_first_parameter) << "\n\tconstant "
2624 << "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]";
2625 is_first_parameter = false;
2626 }
2627
2628 this->generate_msl_uniforms_input_string(out, ShaderStage::VERTEX, is_first_parameter);
2629
2630 /* Transform feedback buffer binding. */
2631 if (this->uses_transform_feedback) {
2632 out << parameter_delimiter(is_first_parameter) << "\n\tdevice "
2634 << "::VertexOut_TF* "
2635 "transform_feedback_results[[buffer(MTL_transform_feedback_buffer_index)]]";
2636 }
2637
2638 /* Generate texture signatures. */
2639 this->generate_msl_textures_input_string(out, ShaderStage::VERTEX, is_first_parameter);
2640
2641 /* Entry point parameters for gl Globals. */
2642 if (this->uses_gl_VertexID) {
2643 out << parameter_delimiter(is_first_parameter)
2644 << "\n\tconst uint32_t gl_VertexID [[vertex_id]]";
2645 }
2646 if (this->uses_gl_InstanceID) {
2647 out << parameter_delimiter(is_first_parameter)
2648 << "\n\tconst uint32_t gl_InstanceID [[instance_id]]";
2649 }
2650 if (this->uses_gl_BaseInstanceARB) {
2651 out << parameter_delimiter(is_first_parameter)
2652 << "\n\tconst uint32_t gl_BaseInstanceARB [[base_instance]]";
2653 }
2654 return out.str();
2655}
2656
2658{
2659 bool is_first_parameter = true;
2660 std::stringstream out;
2662 << "::VertexOut v_in [[stage_in]]";
2663
2664 if (this->uniforms.size() > 0) {
2665 out << parameter_delimiter(is_first_parameter) << "\n\tconstant "
2667 << "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]";
2668 }
2669
2670 this->generate_msl_uniforms_input_string(out, ShaderStage::FRAGMENT, is_first_parameter);
2671
2672 /* Generate texture signatures. */
2673 this->generate_msl_textures_input_string(out, ShaderStage::FRAGMENT, is_first_parameter);
2674
2675 if (this->uses_gl_PointCoord) {
2676 out << parameter_delimiter(is_first_parameter)
2677 << "\n\tconst float2 gl_PointCoord [[point_coord]]";
2678 }
2679 if (this->uses_gl_FrontFacing) {
2680 out << parameter_delimiter(is_first_parameter)
2681 << "\n\tconst bool gl_FrontFacing [[front_facing]]";
2682 }
2683 if (this->uses_gl_PrimitiveID) {
2684 out << parameter_delimiter(is_first_parameter)
2685 << "\n\tconst uint gl_PrimitiveID [[primitive_id]]";
2686 }
2687
2688 /* Barycentrics. */
2689 if (this->uses_barycentrics) {
2690 out << parameter_delimiter(is_first_parameter)
2691 << "\n\tconst float3 mtl_barycentric_coord [[barycentric_coord]]";
2692 }
2693
2694 /* Fragment tile-inputs. */
2695 if (this->fragment_tile_inputs.size() > 0) {
2696 out << parameter_delimiter(is_first_parameter) << "\n\t"
2698 << "::" FRAGMENT_TILE_IN_STRUCT_NAME " fragment_tile_in";
2699 }
2700 return out.str();
2701}
2702
2704{
2705 bool is_first_parameter = true;
2706 std::stringstream out;
2707 if (this->uniforms.size() > 0) {
2708 out << parameter_delimiter(is_first_parameter) << "constant "
2710 << "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]";
2711 }
2712
2713 this->generate_msl_uniforms_input_string(out, ShaderStage::COMPUTE, is_first_parameter);
2714
2715 /* Generate texture signatures. */
2716 this->generate_msl_textures_input_string(out, ShaderStage::COMPUTE, is_first_parameter);
2717
2718 /* Entry point parameters for gl Globals. */
2719 if (this->uses_gl_GlobalInvocationID) {
2720 out << parameter_delimiter(is_first_parameter)
2721 << "\n\tconst uint3 gl_GlobalInvocationID [[thread_position_in_grid]]";
2722 }
2723 if (this->uses_gl_WorkGroupID) {
2724 out << parameter_delimiter(is_first_parameter)
2725 << "\n\tconst uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]";
2726 }
2727 if (this->uses_gl_NumWorkGroups) {
2728 out << parameter_delimiter(is_first_parameter)
2729 << "\n\tconst uint3 gl_NumWorkGroups [[threadgroups_per_grid]]";
2730 }
2731 if (this->uses_gl_LocalInvocationIndex) {
2732 out << parameter_delimiter(is_first_parameter)
2733 << "\n\tconst uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]";
2734 }
2735 if (this->uses_gl_LocalInvocationID) {
2736 out << parameter_delimiter(is_first_parameter)
2737 << "\n\tconst uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]";
2738 }
2739
2740 return out.str();
2741}
2742
2744{
2745 /* Only generate PushConstantBlock if we have uniforms. */
2746 if (this->uniforms.size() == 0) {
2747 return "";
2748 }
2749 BLI_assert(shader_stage == ShaderStage::VERTEX || shader_stage == ShaderStage::FRAGMENT);
2750 UNUSED_VARS_NDEBUG(shader_stage);
2751 std::stringstream out;
2752
2753 /* Common Uniforms. */
2754 out << "typedef struct {" << std::endl;
2755
2756 for (const MSLUniform &uniform : this->uniforms) {
2757 if (uniform.is_array) {
2758 out << "\t" << to_string(uniform.type) << " " << uniform.name << "[" << uniform.array_elems
2759 << "];" << std::endl;
2760 }
2761 else {
2762 out << "\t" << to_string(uniform.type) << " " << uniform.name << ";" << std::endl;
2763 }
2764 }
2765 out << "} PushConstantBlock;\n\n";
2766
2767 /* Member UBO block reference. */
2768 out << std::endl << "const constant PushConstantBlock *global_uniforms;" << std::endl;
2769
2770 /* Macro define chain.
2771 * To access uniforms, we generate a macro such that the uniform name can
2772 * be used directly without using the struct's handle. */
2773 for (const MSLUniform &uniform : this->uniforms) {
2774 out << "#define " << uniform.name << " global_uniforms->" << uniform.name << std::endl;
2775 }
2776 out << std::endl;
2777 return out.str();
2778}
2779
2780/* NOTE: Uniform macro definition vars can conflict with other parameters. */
2782{
2783 std::stringstream out;
2784
2785 /* Macro undef chain. */
2786 for (const MSLUniform &uniform : this->uniforms) {
2787 out << "#undef " << uniform.name << std::endl;
2788 }
2789 /* UBO block undef. */
2790 for (const MSLBufferBlock &ubo : this->uniform_blocks) {
2791 out << "#undef " << ubo.name << std::endl;
2792 }
2793 /* SSBO block undef. */
2794 for (const MSLBufferBlock &ssbo : this->storage_blocks) {
2795 out << "#undef " << ssbo.name << std::endl;
2796 }
2797 return out.str();
2798}
2799
2801{
2802 std::stringstream out;
2803
2804 /* Skip struct if no vert attributes. */
2805 if (this->vertex_input_attributes.size() == 0) {
2806 return "";
2807 }
2808
2809 /* Output */
2810 out << "typedef struct {" << std::endl;
2811 for (const MSLVertexInputAttribute &in_attr : this->vertex_input_attributes) {
2812 /* Matrix and array attributes are not trivially supported and thus
2813 * require each element to be passed as an individual attribute.
2814 * This requires shader source generation of sequential elements.
2815 * The matrix type is then re-packed into a Mat4 inside the entry function.
2816 *
2817 * e.g.
2818 * float4 __internal_modelmatrix_0 [[attribute(0)]];
2819 * float4 __internal_modelmatrix_1 [[attribute(1)]];
2820 * float4 __internal_modelmatrix_2 [[attribute(2)]];
2821 * float4 __internal_modelmatrix_3 [[attribute(3)]];
2822 */
2823 if (is_matrix_type(in_attr.type) && !this->uses_ssbo_vertex_fetch_mode) {
2824 for (int elem = 0; elem < get_matrix_location_count(in_attr.type); elem++) {
2825 out << "\t" << get_matrix_subtype(in_attr.type) << " __internal_" << in_attr.name << elem
2826 << " [[attribute(" << (in_attr.layout_location + elem) << ")]];" << std::endl;
2827 }
2828 }
2829 else {
2830 out << "\t" << in_attr.type << " " << in_attr.name << " [[attribute("
2831 << in_attr.layout_location << ")]];" << std::endl;
2832 }
2833 }
2834
2835 out << "} VertexIn;" << std::endl << std::endl;
2836
2837 return out.str();
2838}
2839
2841{
2842 BLI_assert(shader_stage == ShaderStage::VERTEX || shader_stage == ShaderStage::FRAGMENT);
2843 std::stringstream out;
2844
2845 /* Vertex output struct. */
2846 out << "typedef struct {" << std::endl;
2847
2848 /* If we use GL position, our standard output variable will be mapped to '_default_position_'.
2849 * Otherwise, we use the FIRST element in the output array.
2850 * If transform feedback is enabled, we do not need to output position, unless it
2851 * is explicitly specified as a tf output. */
2852 bool first_attr_is_position = false;
2853 if (this->uses_gl_Position) {
2854
2855 /* If invariance is available, utilize this to consistently mitigate depth fighting artifacts
2856 * by ensuring that vertex position is consistently calculated between subsequent passes
2857 * with maximum precision. */
2858 out << "\tfloat4 _default_position_ [[position]]";
2859 out << " [[invariant]]";
2860 out << ";" << std::endl;
2861 }
2862 else {
2863 if (!this->uses_transform_feedback) {
2864 /* Use first output element for position. */
2865 BLI_assert(this->vertex_output_varyings.size() > 0);
2866 BLI_assert(this->vertex_output_varyings[0].type == "vec4");
2867
2868 /* Use invariance if available. See above for detail. */
2869 out << "\tfloat4 " << this->vertex_output_varyings[0].name << " [[position]];";
2870 out << " [[invariant]]";
2871 out << ";" << std::endl;
2872 first_attr_is_position = true;
2873 }
2874 }
2875
2876 /* Generate other vertex output members. */
2877 bool skip_first_index = first_attr_is_position;
2878 for (const MSLVertexOutputAttribute &v_out : this->vertex_output_varyings) {
2879
2880 /* Skip first index if used for position. */
2881 if (skip_first_index) {
2882 skip_first_index = false;
2883 continue;
2884 }
2885
2886 if (v_out.is_array) {
2887 /* Array types cannot be trivially passed between shading stages.
2888 * Instead we pass each component individually. E.g. vec4 pos[2]
2889 * will be converted to: `vec4 pos_0; vec4 pos_1;`
2890 * The specified interpolation qualifier will be applied per element. */
2891 /* TODO(Metal): Support array of matrix in-out types if required
2892 * e.g. Mat4 out_matrices[3]. */
2893 for (int i = 0; i < v_out.array_elems; i++) {
2894 out << "\t" << v_out.type << " " << v_out.instance_name << "_" << v_out.name << i
2895 << v_out.get_mtl_interpolation_qualifier() << ";" << std::endl;
2896 }
2897 }
2898 else {
2899 /* Matrix types need to be expressed as their vector sub-components. */
2900 if (is_matrix_type(v_out.type)) {
2901 BLI_assert(v_out.get_mtl_interpolation_qualifier() == " [[flat]]" &&
2902 "Matrix varying types must have [[flat]] interpolation");
2903 std::string subtype = get_matrix_subtype(v_out.type);
2904 for (int elem = 0; elem < get_matrix_location_count(v_out.type); elem++) {
2905 out << "\t" << subtype << v_out.instance_name << " __matrix_" << v_out.name << elem
2906 << v_out.get_mtl_interpolation_qualifier() << ";" << std::endl;
2907 }
2908 }
2909 else {
2910 out << "\t" << v_out.type << " " << v_out.instance_name << "_" << v_out.name
2911 << v_out.get_mtl_interpolation_qualifier() << ";" << std::endl;
2912 }
2913 }
2914 }
2915
2916 /* Add gl_PointSize if written to. */
2917 if (shader_stage == ShaderStage::VERTEX) {
2918 if (this->uses_gl_PointSize) {
2919 /* If `gl_PointSize` is explicitly written to,
2920 * we will output the written value directly.
2921 * This value can still be overridden by the
2922 * global point-size value. */
2923 out << "\tfloat pointsize [[point_size]];" << std::endl;
2924 }
2925 else {
2926 /* Otherwise, if point-size is not written to inside the shader,
2927 * then its usage is controlled by whether the `MTL_global_pointsize`
2928 * function constant has been specified.
2929 * This function constant is enabled for all point primitives being rendered. */
2930 out << "\tfloat pointsize [[point_size, function_constant(MTL_global_pointsize)]];"
2931 << std::endl;
2932 }
2933 }
2934
2935 /* Add gl_ClipDistance[n]. */
2936 if (shader_stage == ShaderStage::VERTEX) {
2937 out << "#if defined(USE_CLIP_PLANES) || defined(USE_WORLD_CLIP_PLANES)" << std::endl;
2938 if (this->clip_distances.size() > 1) {
2939 /* Output array of clip distances if specified. */
2940 out << "\tfloat clipdistance [[clip_distance, "
2941 "function_constant(MTL_clip_distances_enabled)]] ["
2942 << this->clip_distances.size() << "];" << std::endl;
2943 }
2944 else if (this->clip_distances.size() > 0) {
2945 out << "\tfloat clipdistance [[clip_distance, "
2946 "function_constant(MTL_clip_distances_enabled)]];"
2947 << std::endl;
2948 }
2949 out << "#endif" << std::endl;
2950 }
2951
2952 /* Add MTL render target array index for multilayered rendering support. */
2953 if (uses_gpu_layer) {
2954 out << "\tuint gpu_Layer [[render_target_array_index]];" << std::endl;
2955 }
2956
2957 /* Add Viewport Index output */
2959 out << "\tuint gpu_ViewportIndex [[viewport_array_index]];" << std::endl;
2960 }
2961
2962 out << "} VertexOut;" << std::endl << std::endl;
2963
2964 return out.str();
2965}
2966
2968 ShaderStage shader_stage)
2969{
2970 BLI_assert(shader_stage == ShaderStage::VERTEX || shader_stage == ShaderStage::FRAGMENT);
2971 UNUSED_VARS_NDEBUG(shader_stage);
2972 std::stringstream out;
2974
2975 out << "typedef struct {" << std::endl;
2976
2977 /* If we use GL position, our standard output variable will be mapped to '_default_position_'.
2978 * Otherwise, we use the FIRST element in the output array -- If transform feedback is enabled,
2979 * we do not need to output position */
2980 bool first_attr_is_position = false;
2981 if (this->uses_gl_Position) {
2982
2983 if (parent_shader_.has_transform_feedback_varying("gl_Position")) {
2984 out << "\tfloat4 pos [[position]];" << std::endl;
2985 vertex_output_varyings_tf.append({.type = "vec4",
2986 .name = "gl_Position",
2987 .interpolation_qualifier = "",
2988 .is_array = false,
2989 .array_elems = 1});
2990 }
2991 }
2992 else {
2993 if (!this->uses_transform_feedback) {
2994 /* Use first output element for position */
2995 BLI_assert(this->vertex_output_varyings.size() > 0);
2996 BLI_assert(this->vertex_output_varyings[0].type == "vec4");
2997 first_attr_is_position = true;
2998 }
2999 }
3000
3001 /* Generate other vertex outputs. */
3002 bool skip_first_index = first_attr_is_position;
3003 for (const MSLVertexOutputAttribute &v_out : this->vertex_output_varyings) {
3004
3005 /* Skip first index if used for position. */
3006 if (skip_first_index) {
3007 skip_first_index = false;
3008 continue;
3009 }
3010
3011 if (!parent_shader_.has_transform_feedback_varying(v_out.name)) {
3012 continue;
3013 }
3014 vertex_output_varyings_tf.append(v_out);
3015
3016 if (v_out.is_array) {
3017 /* TODO(Metal): Support array of matrix types if required. */
3018 for (int i = 0; i < v_out.array_elems; i++) {
3019 out << "\t" << v_out.type << " " << v_out.name << i
3020 << v_out.get_mtl_interpolation_qualifier() << ";" << std::endl;
3021 }
3022 }
3023 else {
3024 /* Matrix types need to be expressed as their vector sub-components. */
3025 if (is_matrix_type(v_out.type)) {
3026 BLI_assert(v_out.get_mtl_interpolation_qualifier() == " [[flat]]" &&
3027 "Matrix varying types must have [[flat]] interpolation");
3028 std::string subtype = get_matrix_subtype(v_out.type);
3029 for (int elem = 0; elem < get_matrix_location_count(v_out.type); elem++) {
3030 out << "\t" << subtype << " __matrix_" << v_out.name << elem
3031 << v_out.get_mtl_interpolation_qualifier() << ";" << std::endl;
3032 }
3033 }
3034 else {
3035 out << "\t" << v_out.type << " " << v_out.name << v_out.get_mtl_interpolation_qualifier()
3036 << ";" << std::endl;
3037 }
3038 }
3039 }
3040
3041 out << "} VertexOut_TF;" << std::endl << std::endl;
3042
3043 return out.str();
3044}
3045
3047{
3048 std::stringstream out;
3049
3050 auto &fragment_interface_src = (is_input) ? this->fragment_tile_inputs : this->fragment_outputs;
3051
3052 /* Output. */
3053 out << "typedef struct {" << std::endl;
3054 for (int f_output = 0; f_output < fragment_interface_src.size(); f_output++) {
3055 out << "\t" << to_string(fragment_interface_src[f_output].type) << " "
3056 << fragment_interface_src[f_output].name << " [[color("
3057 << fragment_interface_src[f_output].layout_location << ")";
3058 if (fragment_interface_src[f_output].layout_index >= 0) {
3059 out << ", index(" << fragment_interface_src[f_output].layout_index << ")";
3060 }
3061 if (fragment_interface_src[f_output].raster_order_group >= 0) {
3062 out << ", raster_order_group(" << fragment_interface_src[f_output].raster_order_group << ")";
3063 }
3064 out << "]]"
3065 << ";" << std::endl;
3066 }
3067 /* Add gl_FragDepth output if used. */
3068 if (this->uses_gl_FragDepth) {
3069 std::string out_depth_argument = ((this->depth_write == DepthWrite::GREATER) ?
3070 "greater" :
3071 ((this->depth_write == DepthWrite::LESS) ? "less" :
3072 "any"));
3073 out << "\tfloat fragdepth [[depth(" << out_depth_argument << ")]];" << std::endl;
3074 }
3075 /* Add gl_FragStencilRefARB output if used. */
3076 if (!is_input && this->uses_gl_FragStencilRefARB) {
3077 out << "\tuint fragstencil [[stencil]];" << std::endl;
3078 }
3079 if (is_input) {
3080 out << "} " FRAGMENT_TILE_IN_STRUCT_NAME ";" << std::endl;
3081 }
3082 else {
3083 out << "} " FRAGMENT_OUT_STRUCT_NAME ";" << std::endl;
3084 }
3085 out << std::endl;
3086 return out.str();
3087}
3088
3090{
3091 if (this->uniforms.size() == 0) {
3092 return "";
3093 }
3094 /* Populate Global Uniforms. */
3095 std::stringstream out;
3096
3097 /* Copy UBO block ref. */
3098 out << "\t/* Copy Uniform block member reference */" << std::endl;
3099 out << "\t" << get_shader_stage_instance_name(stage) << "."
3100 << "global_uniforms = uniforms;" << std::endl;
3101
3102 return out.str();
3103}
3104
3106{
3107 std::stringstream out;
3108
3109 /* Native tile read is supported on tile-based architectures (Apple Silicon). */
3111 for (const MSLFragmentTileInputAttribute &tile_input : this->fragment_tile_inputs) {
3113 << tile_input.name << " = "
3114 << "fragment_tile_in." << tile_input.name << ";" << std::endl;
3115 }
3116 }
3117 else {
3118 for (const MSLFragmentTileInputAttribute &tile_input : this->fragment_tile_inputs) {
3119 /* Get read swizzle mask. */
3120 char swizzle[] = "xyzw";
3121 swizzle[to_component_count(tile_input.type)] = '\0';
3122
3123 bool is_layered_fb = bool(create_info_->builtins_ & BuiltinBits::LAYER);
3124 std::string texel_co = (is_layered_fb) ?
3125 "ivec3(ivec2(v_in._default_position_.xy), int(v_in.gpu_Layer))" :
3126 "ivec2(v_in._default_position_.xy)";
3127
3129 << tile_input.name << " = texelFetch("
3130 << get_shader_stage_instance_name(ShaderStage::FRAGMENT) << "." << tile_input.name
3131 << "_subpass_img, " << texel_co << ", 0)." << swizzle << ";\n";
3132 }
3133 }
3134 return out.str();
3135}
3136
3138{
3139 /* Populate Global Uniforms. */
3140 std::stringstream out;
3141 out << "\t/* Copy UBO block references into local class variables */" << std::endl;
3142 for (const MSLBufferBlock &ubo : this->uniform_blocks) {
3143
3144 /* Only include blocks which are used within this stage. */
3145 if (bool(ubo.stage & stage)) {
3146 /* Generate UBO reference assignment.
3147 * NOTE(Metal): We append `_local` post-fix onto the class member name
3148 * for the ubo to avoid name collision with the UBO accessor macro.
3149 * We only need to add this post-fix for the non-array access variant,
3150 * as the array is indexed directly, rather than requiring a dereference. */
3151 out << "\t" << get_shader_stage_instance_name(stage) << "." << ubo.name;
3152 if (!ubo.is_array) {
3153 out << "_local";
3154 }
3155 out << " = " << ubo.name << ";" << std::endl;
3156 }
3157 }
3158
3159 /* Populate storage buffer references. */
3160 out << "\t/* Copy SSBO block references into local class variables */" << std::endl;
3161 for (const MSLBufferBlock &ssbo : this->storage_blocks) {
3162
3163 /* Only include blocks which are used within this stage. */
3164 if (bool(ssbo.stage & stage) && !ssbo.is_texture_buffer) {
3165 /* Generate UBO reference assignment.
3166 * NOTE(Metal): We append `_local` post-fix onto the class member name
3167 * for the ubo to avoid name collision with the UBO accessor macro.
3168 * We only need to add this post-fix for the non-array access variant,
3169 * as the array is indexed directly, rather than requiring a dereference. */
3170 out << "\t" << get_shader_stage_instance_name(stage) << "." << ssbo.name;
3171 if (!ssbo.is_array) {
3172 out << "_local";
3173 }
3174 out << " = " << ssbo.name << ";" << std::endl;
3175 }
3176 }
3177
3178 out << std::endl;
3179 return out.str();
3180}
3181
3182/* Copy input attributes from stage_in into class local variables. */
3184{
3185 static const char *shader_stage_inst_name = get_shader_stage_instance_name(ShaderStage::VERTEX);
3186
3187 /* SSBO Vertex Fetch mode does not require local attribute population,
3188 * we only need to pass over the buffer pointer references. */
3189 if (this->uses_ssbo_vertex_fetch_mode) {
3190 std::stringstream out;
3191 out << "const constant uchar* GLOBAL_MTL_VERTEX_DATA[MTL_SSBO_VERTEX_FETCH_MAX_VBOS] = {"
3192 << std::endl;
3193 for (int i = 0; i < MTL_SSBO_VERTEX_FETCH_MAX_VBOS; i++) {
3194 char delimiter = (i < MTL_SSBO_VERTEX_FETCH_MAX_VBOS - 1) ? ',' : ' ';
3195 out << "\t\tMTL_VERTEX_DATA_" << i << delimiter << std::endl;
3196 }
3197 out << "};" << std::endl;
3198 out << "\t" << shader_stage_inst_name << ".MTL_VERTEX_DATA = GLOBAL_MTL_VERTEX_DATA;"
3199 << std::endl;
3200 out << "\t" << shader_stage_inst_name << ".MTL_INDEX_DATA_U16 = MTL_INDEX_DATA;" << std::endl;
3201 out << "\t" << shader_stage_inst_name
3202 << ".MTL_INDEX_DATA_U32 = reinterpret_cast<constant "
3203 "uint32_t*>(MTL_INDEX_DATA);"
3204 << std::endl;
3205 return out.str();
3206 }
3207
3208 /* Populate local attribute variables. */
3209 std::stringstream out;
3210 out << "\t/* Copy Vertex Stage-in attributes into local variables */" << std::endl;
3211 for (int attribute = 0; attribute < this->vertex_input_attributes.size(); attribute++) {
3212
3213 if (is_matrix_type(this->vertex_input_attributes[attribute].type)) {
3214 /* Reading into an internal matrix from split attributes: Should generate the following:
3215 * vertex_shader_instance.mat_attribute_type =
3216 * mat4(v_in.__internal_mat_attribute_type0,
3217 * v_in.__internal_mat_attribute_type1,
3218 * v_in.__internal_mat_attribute_type2,
3219 * v_in.__internal_mat_attribute_type3). */
3220 out << "\t" << shader_stage_inst_name << "." << this->vertex_input_attributes[attribute].name
3221 << " = " << this->vertex_input_attributes[attribute].type << "(v_in.__internal_"
3222 << this->vertex_input_attributes[attribute].name << 0;
3223 for (int elem = 1;
3224 elem < get_matrix_location_count(this->vertex_input_attributes[attribute].type);
3225 elem++)
3226 {
3227 out << ",\n"
3228 << "v_in.__internal_" << this->vertex_input_attributes[attribute].name << elem;
3229 }
3230 out << ");";
3231 }
3232 else {
3233 /* OpenGL uses the `GPU_FETCH_*` functions which can alter how an attribute value is
3234 * interpreted. In Metal, we cannot support all implicit conversions within the vertex
3235 * descriptor/vertex stage-in, so we need to perform value transformation on-read.
3236 *
3237 * This is handled by wrapping attribute reads to local shader registers in a
3238 * suitable conversion function `attribute_conversion_func_name`.
3239 * This conversion function performs a specific transformation on the source
3240 * vertex data, depending on the specified GPU_FETCH_* mode for the current
3241 * vertex format.
3242 *
3243 * The fetch_mode is specified per-attribute using specialization constants
3244 * on the PSO, wherein a unique set of constants is passed in per vertex
3245 * buffer/format configuration. Efficiently enabling pass-through reads
3246 * if no special fetch is required. */
3247 bool do_attribute_conversion_on_read = false;
3248 std::string attribute_conversion_func_name = get_attribute_conversion_function(
3249 &do_attribute_conversion_on_read, this->vertex_input_attributes[attribute].type);
3250
3251 if (do_attribute_conversion_on_read) {
3252 BLI_assert(this->vertex_input_attributes[attribute].layout_location >= 0);
3253 out << "\t" << attribute_conversion_func_name << "(MTL_AttributeConvert"
3254 << this->vertex_input_attributes[attribute].layout_location << ", v_in."
3255 << this->vertex_input_attributes[attribute].name << ", " << shader_stage_inst_name
3256 << "." << this->vertex_input_attributes[attribute].name << ");" << std::endl;
3257 }
3258 else {
3259 out << "\t" << shader_stage_inst_name << "."
3260 << this->vertex_input_attributes[attribute].name << " = v_in."
3261 << this->vertex_input_attributes[attribute].name << ";" << std::endl;
3262 }
3263 }
3264 }
3265 out << std::endl;
3266 return out.str();
3267}
3268
3269/* Copy post-main, modified, local class variables into vertex-output struct. */
3271{
3272 static const char *shader_stage_inst_name = get_shader_stage_instance_name(ShaderStage::VERTEX);
3273 std::stringstream out;
3274 out << "\t/* Copy Vertex Outputs into output struct */" << std::endl;
3275
3276 /* Output gl_Position with conversion to Metal coordinate-space. */
3277 if (this->uses_gl_Position) {
3278 out << "\toutput._default_position_ = " << shader_stage_inst_name << ".gl_Position;"
3279 << std::endl;
3280
3281 /* Invert Y and rescale depth range.
3282 * This is an alternative method to modifying all projection matrices. */
3283 out << "\toutput._default_position_.y = -output._default_position_.y;" << std::endl;
3284 out << "\toutput._default_position_.z = "
3285 "(output._default_position_.z+output._default_position_.w)/2.0;"
3286 << std::endl;
3287 }
3288
3289 /* Output Point-size. */
3290 if (this->uses_gl_PointSize) {
3291 out << "\toutput.pointsize = " << shader_stage_inst_name << ".gl_PointSize;" << std::endl;
3292 }
3293
3294 /* Output render target array Index. */
3295 if (uses_gpu_layer) {
3296 out << "\toutput.gpu_Layer = " << shader_stage_inst_name << ".gpu_Layer;" << std::endl;
3297 }
3298
3299 /* Output Viewport Index. */
3301 out << "\toutput.gpu_ViewportIndex = " << shader_stage_inst_name << ".gpu_ViewportIndex;"
3302 << std::endl;
3303 }
3304
3305 /* Output clip-distances.
3306 * Clip distances are only written to if both clipping planes are turned on for the shader,
3307 * and the clipping planes are enabled. Enablement is controlled on a per-plane basis
3308 * via function constants in the shader pipeline state object (PSO). */
3309 out << "#if defined(USE_CLIP_PLANES) || defined(USE_WORLD_CLIP_PLANES)" << std::endl
3310 << "if(MTL_clip_distances_enabled) {" << std::endl;
3311 if (this->clip_distances.size() > 1) {
3312 for (int cd = 0; cd < this->clip_distances.size(); cd++) {
3313 /* Default value when clipping is disabled >= 0.0 to ensure primitive is not clipped. */
3314 out << "\toutput.clipdistance[" << cd
3315 << "] = (is_function_constant_defined(MTL_clip_distance_enabled" << cd << "))?"
3316 << shader_stage_inst_name << ".gl_ClipDistance_" << cd << ":1.0;" << std::endl;
3317 }
3318 }
3319 else if (this->clip_distances.size() > 0) {
3320 out << "\toutput.clipdistance = " << shader_stage_inst_name << ".gl_ClipDistance_0;"
3321 << std::endl;
3322 }
3323 out << "}" << std::endl << "#endif" << std::endl;
3324
3325 /* Populate output vertex variables. */
3326 int output_id = 0;
3327 for (const MSLVertexOutputAttribute &v_out : this->vertex_output_varyings) {
3328 if (v_out.is_array) {
3329
3330 for (int i = 0; i < v_out.array_elems; i++) {
3331 out << "\toutput." << v_out.instance_name << "_" << v_out.name << i << " = "
3332 << shader_stage_inst_name << ".";
3333
3334 if (v_out.instance_name != "") {
3335 out << v_out.instance_name << ".";
3336 }
3337
3338 out << v_out.name << "[" << i << "]"
3339 << ";" << std::endl;
3340 }
3341 }
3342 else {
3343 /* Matrix types are split into vectors and need to be reconstructed. */
3344 if (is_matrix_type(v_out.type)) {
3345 for (int elem = 0; elem < get_matrix_location_count(v_out.type); elem++) {
3346 out << "\toutput." << v_out.instance_name << "__matrix_" << v_out.name << elem << " = "
3347 << shader_stage_inst_name << ".";
3348
3349 if (v_out.instance_name != "") {
3350 out << v_out.instance_name << ".";
3351 }
3352
3353 out << v_out.name << "[" << elem << "];" << std::endl;
3354 }
3355 }
3356 else {
3357 /* If we are not using gl_Position, first vertex output is used for position.
3358 * Ensure it is vec4. If transform feedback is enabled, we do not need position. */
3359 if (!this->uses_gl_Position && output_id == 0 && !this->uses_transform_feedback) {
3360
3361 out << "\toutput." << v_out.instance_name << "_" << v_out.name << " = to_vec4("
3362 << shader_stage_inst_name << "." << v_out.name << ");" << std::endl;
3363
3364 /* Invert Y */
3365 out << "\toutput." << v_out.instance_name << "_" << v_out.name << ".y = -output."
3366 << v_out.name << ".y;" << std::endl;
3367 }
3368 else {
3369
3370 /* Assign vertex output. */
3371 out << "\toutput." << v_out.instance_name << "_" << v_out.name << " = "
3372 << shader_stage_inst_name << ".";
3373
3374 if (v_out.instance_name != "") {
3375 out << v_out.instance_name << ".";
3376 }
3377
3378 out << v_out.name << ";" << std::endl;
3379 }
3380 }
3381 }
3382 output_id++;
3383 }
3384 out << std::endl;
3385 return out.str();
3386}
3387
3388/* Copy desired output varyings into transform feedback structure */
3390{
3391 static const char *shader_stage_inst_name = get_shader_stage_instance_name(ShaderStage::VERTEX);
3392 std::stringstream out;
3393 out << "\t/* Copy Vertex TF Outputs into transform feedback buffer */" << std::endl;
3394
3395 /* Populate output vertex variables */
3396 /* TODO(Metal): Currently do not need to support output matrix types etc; but may need to
3397 * verify for other configurations if these occur in any cases. */
3398 for (int v_output = 0; v_output < this->vertex_output_varyings_tf.size(); v_output++) {
3399 out << "transform_feedback_results[gl_VertexID]."
3400 << this->vertex_output_varyings_tf[v_output].name << " = " << shader_stage_inst_name << "."
3401 << this->vertex_output_varyings_tf[v_output].name << ";" << std::endl;
3402 }
3403 out << std::endl;
3404 return out.str();
3405}
3406
3407/* Copy fragment stage inputs (Vertex Outputs) into local class variables. */
3409{
3410 static const char *shader_stage_inst_name = get_shader_stage_instance_name(
3412 /* Populate local attribute variables. */
3413 std::stringstream out;
3414 out << "\t/* Copy Fragment input into local variables. */" << std::endl;
3415
3416 /* Special common case for gl_FragCoord, assigning to input position. */
3417 if (this->uses_gl_Position) {
3418 out << "\t" << shader_stage_inst_name << ".gl_FragCoord = v_in._default_position_;"
3419 << std::endl;
3420 }
3421 else {
3422 /* When gl_Position is not set, first VertexIn element is used for position. */
3423 out << "\t" << shader_stage_inst_name << ".gl_FragCoord = v_in."
3424 << this->vertex_output_varyings[0].name << ";" << std::endl;
3425 }
3426
3427 /* Assign default gl_FragDepth.
3428 * If gl_FragDepth is used, it should default to the original depth value. Resolves #107159 where
3429 * overlay_wireframe_frag may not write to gl_FragDepth. */
3430 if (this->uses_gl_FragDepth) {
3431 out << "\t" << shader_stage_inst_name << ".gl_FragDepth = " << shader_stage_inst_name
3432 << ".gl_FragCoord.z;" << std::endl;
3433 }
3434
3435 /* Input render target array index received from vertex shader. */
3436 if (uses_gpu_layer) {
3437 out << "\t" << shader_stage_inst_name << ".gpu_Layer = v_in.gpu_Layer;" << std::endl;
3438 }
3439
3440 /* Input viewport array index received from vertex shader. */
3442 out << "\t" << shader_stage_inst_name << ".gpu_ViewportIndex = v_in.gpu_ViewportIndex;"
3443 << std::endl;
3444 }
3445
3446 /* NOTE: We will only assign to the intersection of the vertex output and fragment input.
3447 * Fragment input represents varying variables which are declared (but are not necessarily
3448 * used). The Vertex out defines the set which is passed into the fragment shader, which
3449 * contains out variables declared in the vertex shader, though these are not necessarily
3450 * consumed by the fragment shader.
3451 *
3452 * In the cases where the fragment shader expects a variable, but it does not exist in the
3453 * vertex shader, a warning will be provided. */
3454 for (int f_input = (this->uses_gl_Position) ? 0 : 1;
3455 f_input < this->fragment_input_varyings.size();
3456 f_input++)
3457 {
3458 bool exists_in_vertex_output = false;
3459 for (int v_o = 0; v_o < this->vertex_output_varyings.size() && !exists_in_vertex_output; v_o++)
3460 {
3461 if (this->fragment_input_varyings[f_input].name == this->vertex_output_varyings[v_o].name) {
3462 exists_in_vertex_output = true;
3463 }
3464 }
3465 if (!exists_in_vertex_output) {
3467 "[Warning] Fragment shader expects varying input '%s', but this is not passed from "
3468 "the "
3469 "vertex shader\n",
3470 this->fragment_input_varyings[f_input].name.c_str());
3471 continue;
3472 }
3473 if (this->fragment_input_varyings[f_input].is_array) {
3474 for (int i = 0; i < this->fragment_input_varyings[f_input].array_elems; i++) {
3475 out << "\t" << shader_stage_inst_name << ".";
3476
3477 if (this->fragment_input_varyings[f_input].instance_name != "") {
3478 out << this->fragment_input_varyings[f_input].instance_name << ".";
3479 }
3480
3481 out << this->fragment_input_varyings[f_input].name << "[" << i << "] = v_in."
3482 << this->fragment_input_varyings[f_input].instance_name << "_"
3483 << this->fragment_input_varyings[f_input].name << i << ";" << std::endl;
3484 }
3485 }
3486 else {
3487 /* Matrix types are split into components and need to be regrouped into a matrix. */
3488 if (is_matrix_type(this->fragment_input_varyings[f_input].type)) {
3489 out << "\t" << shader_stage_inst_name << ".";
3490
3491 if (this->fragment_input_varyings[f_input].instance_name != "") {
3492 out << this->fragment_input_varyings[f_input].instance_name << ".";
3493 }
3494
3495 out << this->fragment_input_varyings[f_input].name << " = "
3496 << this->fragment_input_varyings[f_input].type;
3497 int count = get_matrix_location_count(this->fragment_input_varyings[f_input].type);
3498 for (int elem = 0; elem < count; elem++) {
3499 out << ((elem == 0) ? "(" : "") << "v_in."
3500 << this->fragment_input_varyings[f_input].instance_name << "__matrix_"
3501 << this->fragment_input_varyings[f_input].name << elem
3502 << ((elem < count - 1) ? ",\n" : "");
3503 }
3504 out << ");" << std::endl;
3505 }
3506 else {
3507 out << "\t" << shader_stage_inst_name << ".";
3508
3509 if (this->fragment_input_varyings[f_input].instance_name != "") {
3510 out << this->fragment_input_varyings[f_input].instance_name << ".";
3511 }
3512
3513 out << this->fragment_input_varyings[f_input].name << " = v_in."
3514 << this->fragment_input_varyings[f_input].instance_name << "_"
3515 << this->fragment_input_varyings[f_input].name << ";" << std::endl;
3516 }
3517 }
3518 }
3519 out << std::endl;
3520 return out.str();
3521}
3522
3523/* Copy post-main, modified, local class variables into fragment-output struct. */
3525{
3526 static const char *shader_stage_inst_name = get_shader_stage_instance_name(
3528 /* Populate output fragment variables. */
3529 std::stringstream out;
3530 out << "\t/* Copy Fragment Outputs into output struct. */" << std::endl;
3531
3532 /* Output gl_FragDepth. */
3533 if (this->uses_gl_FragDepth) {
3534 out << "\toutput.fragdepth = " << shader_stage_inst_name << ".gl_FragDepth;" << std::endl;
3535 }
3536
3537 /* Output gl_FragStencilRefARB. */
3538 if (this->uses_gl_FragStencilRefARB) {
3539 out << "\toutput.fragstencil = uint(" << shader_stage_inst_name << ".gl_FragStencilRefARB);"
3540 << std::endl;
3541 }
3542
3543 /* Output attributes. */
3544 for (int f_output = 0; f_output < this->fragment_outputs.size(); f_output++) {
3545
3546 out << "\toutput." << this->fragment_outputs[f_output].name << " = " << shader_stage_inst_name
3547 << "." << this->fragment_outputs[f_output].name << ";" << std::endl;
3548 }
3549 out << std::endl;
3550 return out.str();
3551}
3552
3554{
3555 /* NOTE: Shader stage must be a singular stage index. Compound stage is not valid for this
3556 * function. */
3557 BLI_assert(shader_stage == ShaderStage::VERTEX || shader_stage == ShaderStage::FRAGMENT ||
3558 shader_stage == ShaderStage::COMPUTE);
3559
3560 std::stringstream out;
3561 out << "\t/* Populate local texture and sampler members */" << std::endl;
3562 for (int i = 0; i < this->texture_samplers.size(); i++) {
3563 if (bool(this->texture_samplers[i].stage & shader_stage)) {
3564
3565 /* Assign texture reference. */
3566 out << "\t" << get_shader_stage_instance_name(shader_stage) << "."
3567 << this->texture_samplers[i].name << ".texture = &" << this->texture_samplers[i].name
3568 << ";" << std::endl;
3569
3570 /* Assign sampler reference. */
3572 out << "\t" << get_shader_stage_instance_name(shader_stage) << "."
3573 << this->texture_samplers[i].name << ".samp = &samplers.sampler_args["
3574 << this->texture_samplers[i].slot << "];" << std::endl;
3575 }
3576 else {
3577 out << "\t" << get_shader_stage_instance_name(shader_stage) << "."
3578 << this->texture_samplers[i].name << ".samp = &" << this->texture_samplers[i].name
3579 << "_sampler;" << std::endl;
3580 }
3581
3582 /* Assign texture buffer reference and uniform metadata (if used). */
3583 int tex_buf_id = this->texture_samplers[i].atomic_fallback_buffer_ssbo_id;
3584 if (tex_buf_id != -1) {
3585 MSLBufferBlock &ssbo = this->storage_blocks[tex_buf_id];
3586 out << "\t" << get_shader_stage_instance_name(shader_stage) << "."
3587 << this->texture_samplers[i].name << ".buffer = " << ssbo.name << ";" << std::endl;
3588 out << "\t" << get_shader_stage_instance_name(shader_stage) << "."
3589 << this->texture_samplers[i].name << ".aligned_width = uniforms->"
3590 << this->texture_samplers[i].name << "_metadata.w;" << std::endl;
3591
3592 /* Buffer-backed 2D Array and 3D texture types are not natively supported so texture size
3593 * is passed in as uniform metadata for 3D to 2D coordinate remapping. */
3594 if (ELEM(this->texture_samplers[i].type,
3595 ImageType::UINT_2D_ARRAY_ATOMIC,
3596 ImageType::UINT_3D_ATOMIC,
3597 ImageType::INT_2D_ARRAY_ATOMIC,
3598 ImageType::INT_3D_ATOMIC))
3599 {
3600 out << "\t" << get_shader_stage_instance_name(shader_stage) << "."
3601 << this->texture_samplers[i].name << ".texture_size = ushort3(uniforms->"
3602 << this->texture_samplers[i].name << "_metadata.xyz);" << std::endl;
3603 }
3604 }
3605 }
3606 }
3607 out << std::endl;
3608 return out.str();
3609}
3610
3612{
3613 /* Determine used-attribute-location mask. */
3614 uint32_t used_locations = 0;
3616 if (attr.layout_location >= 0) {
3617 /* Matrix and array types span multiple location slots. */
3618 uint32_t location_element_count = get_matrix_location_count(attr.type);
3619 for (uint32_t i = 1; i <= location_element_count; i++) {
3620 /* Ensure our location hasn't already been used. */
3621 uint32_t location_mask = (i << attr.layout_location);
3622 BLI_assert((used_locations & location_mask) == 0);
3623 used_locations = used_locations | location_mask;
3624 }
3625 }
3626 }
3627
3628 /* Assign unused location slots to other attributes. */
3630 if (attr.layout_location == -1) {
3631 /* Determine number of locations required. */
3632 uint32_t required_attr_slot_count = get_matrix_location_count(attr.type);
3633
3634 /* Determine free location.
3635 * Starting from 1 is slightly less efficient, however,
3636 * given multi-sized attributes, an earlier slot may remain free.
3637 * given GPU_VERT_ATTR_MAX_LEN is small, this wont matter. */
3638 for (int loc = 0; loc < GPU_VERT_ATTR_MAX_LEN - (required_attr_slot_count - 1); loc++) {
3639
3640 uint32_t location_mask = (1 << loc);
3641 /* Generate sliding mask using location and required number of slots,
3642 * to ensure contiguous slots are free.
3643 * slot mask will be a number containing N binary 1's, where N is the
3644 * number of attributes needed.
3645 * e.g. N=4 -> 1111. */
3646 uint32_t location_slot_mask = (1 << required_attr_slot_count) - 1;
3647 uint32_t sliding_location_slot_mask = location_slot_mask << location_mask;
3648 if ((used_locations & sliding_location_slot_mask) == 0) {
3649 /* Assign location and update mask. */
3650 attr.layout_location = loc;
3651 used_locations = used_locations | location_slot_mask;
3652 continue;
3653 }
3654 }
3655
3656 /* Error if could not assign attribute. */
3657 MTL_LOG_ERROR("Could not assign attribute location to attribute %s for shader %s",
3658 attr.name.c_str(),
3659 this->parent_shader_.name_get());
3660 }
3661 }
3662}
3663
3665{
3666 int running_location_ind = 0;
3667
3668 /* This code works under the assumption that either all layout_locations are set,
3669 * or none are. */
3670 for (int i = 0; i < this->fragment_outputs.size(); i++) {
3672 ((running_location_ind > 0) ? (this->fragment_outputs[i].layout_location == -1) : true),
3673 "Error: Mismatched input attributes, some with location specified, some without");
3674 if (this->fragment_outputs[i].layout_location == -1) {
3675 this->fragment_outputs[i].layout_location = running_location_ind;
3676 running_location_ind++;
3677 }
3678 }
3679}
3680
3685static uint32_t name_buffer_copystr(char **name_buffer_ptr,
3686 const char *str_to_copy,
3687 uint32_t &name_buffer_size,
3688 uint32_t &name_buffer_offset)
3689{
3690 /* Verify input is valid. */
3691 BLI_assert(str_to_copy != nullptr);
3692
3693 /* Determine length of new string, and ensure name buffer is large enough. */
3694 uint32_t ret_len = strlen(str_to_copy);
3695 BLI_assert(ret_len > 0);
3696
3697 /* If required name buffer size is larger, increase by at least 128 bytes. */
3698 if (name_buffer_offset + ret_len + 1 > name_buffer_size) {
3699 name_buffer_size = name_buffer_offset + max_ii(128, ret_len + 1);
3700 *name_buffer_ptr = (char *)MEM_reallocN(*name_buffer_ptr, name_buffer_size);
3701 }
3702
3703 /* Copy string into name buffer. */
3704 uint32_t insert_offset = name_buffer_offset;
3705 char *current_offset = (*name_buffer_ptr) + insert_offset;
3706 memcpy(current_offset, str_to_copy, (ret_len + 1) * sizeof(char));
3707
3708 /* Adjust offset including null terminator. */
3709 name_buffer_offset += ret_len + 1;
3710
3711 /* Return offset into name buffer for inserted string. */
3712 return insert_offset;
3713}
3714
3716 const char *name, const shader::ShaderCreateInfo *info)
3717{
3718 MTLShaderInterface *interface = new MTLShaderInterface(name);
3719 interface->init();
3720
3721 /* Name buffer. */
3722 /* Initialize name buffer. */
3723 uint32_t name_buffer_size = 256;
3724 uint32_t name_buffer_offset = 0;
3725 interface->name_buffer_ = (char *)MEM_mallocN(name_buffer_size, "name_buffer");
3726
3727 /* Prepare Interface Input Attributes. */
3728 int c_offset = 0;
3729 for (int attribute = 0; attribute < this->vertex_input_attributes.size(); attribute++) {
3730
3731 /* We need a special case for handling matrix types, which splits the matrix into its vector
3732 * components. */
3733 if (is_matrix_type(this->vertex_input_attributes[attribute].type)) {
3734
3735 eMTLDataType mtl_type = to_mtl_type(
3736 get_matrix_subtype(this->vertex_input_attributes[attribute].type));
3737 int size = mtl_get_data_type_size(mtl_type);
3738 for (int elem = 0;
3739 elem < get_matrix_location_count(this->vertex_input_attributes[attribute].type);
3740 elem++)
3741 {
3742 /* First attribute matches the core name -- subsequent attributes tagged with
3743 * `__internal_<name><index>`. */
3744 std::string _internal_name = (elem == 0) ?
3745 this->vertex_input_attributes[attribute].name :
3746 "__internal_" +
3748 std::to_string(elem);
3749
3750 /* IF Using SSBO vertex Fetch, we do not need to expose other dummy attributes in the
3751 * shader interface, only the first one for the whole matrix, as we can pass whatever data
3752 * we want in this mode, and do not need to split attributes. */
3753 if (elem == 0 || !this->uses_ssbo_vertex_fetch_mode) {
3754 interface->add_input_attribute(
3755 name_buffer_copystr(&interface->name_buffer_,
3756 _internal_name.c_str(),
3757 name_buffer_size,
3758 name_buffer_offset),
3759 this->vertex_input_attributes[attribute].layout_location + elem,
3761 0,
3762 size,
3763 c_offset,
3764 (elem == 0) ?
3765 get_matrix_location_count(this->vertex_input_attributes[attribute].type) :
3766 0);
3767 }
3768 c_offset += size;
3769 }
3771 "[Note] Matrix Type '%s' added to shader interface as vertex attribute. (Elem Count: "
3772 "%d)\n",
3773 this->vertex_input_attributes[attribute].name.c_str(),
3774 get_matrix_location_count(this->vertex_input_attributes[attribute].type));
3775 }
3776 else {
3777
3778 /* Normal attribute types. */
3779 eMTLDataType mtl_type = to_mtl_type(this->vertex_input_attributes[attribute].type);
3780 int size = mtl_get_data_type_size(mtl_type);
3781 interface->add_input_attribute(
3782 name_buffer_copystr(&interface->name_buffer_,
3783 this->vertex_input_attributes[attribute].name.c_str(),
3784 name_buffer_size,
3785 name_buffer_offset),
3786 this->vertex_input_attributes[attribute].layout_location,
3788 0,
3789 size,
3790 c_offset);
3791 c_offset += size;
3792 }
3793
3794 /* Used in `GPU_shader_get_attribute_info`. */
3795 interface->attr_types_[this->vertex_input_attributes[attribute].layout_location] = uint8_t(
3796 this->vertex_input_attributes[attribute].type);
3797 }
3798
3799 /* Prepare Interface Default Uniform Block. */
3800 interface->add_push_constant_block(name_buffer_copystr(
3801 &interface->name_buffer_, "PushConstantBlock", name_buffer_size, name_buffer_offset));
3802
3803 for (int uniform = 0; uniform < this->uniforms.size(); uniform++) {
3804 interface->add_uniform(
3805 name_buffer_copystr(&interface->name_buffer_,
3806 this->uniforms[uniform].name.c_str(),
3807 name_buffer_size,
3808 name_buffer_offset),
3809 to_mtl_type(this->uniforms[uniform].type),
3810 (this->uniforms[uniform].is_array) ? this->uniforms[uniform].array_elems : 1);
3811 }
3812
3813 /* Prepare Interface Uniform Blocks. */
3814 for (int uniform_block = 0; uniform_block < this->uniform_blocks.size(); uniform_block++) {
3815 interface->add_uniform_block(
3816 name_buffer_copystr(&interface->name_buffer_,
3817 this->uniform_blocks[uniform_block].name.c_str(),
3818 name_buffer_size,
3819 name_buffer_offset),
3820 this->uniform_blocks[uniform_block].slot,
3821 this->uniform_blocks[uniform_block].location,
3822 0,
3823 this->uniform_blocks[uniform_block].stage);
3824 }
3825
3826 /* Prepare Interface Storage Blocks. */
3827 for (int storage_block = 0; storage_block < this->storage_blocks.size(); storage_block++) {
3828 interface->add_storage_block(
3829 name_buffer_copystr(&interface->name_buffer_,
3830 this->storage_blocks[storage_block].name.c_str(),
3831 name_buffer_size,
3832 name_buffer_offset),
3833 this->storage_blocks[storage_block].slot,
3834 this->storage_blocks[storage_block].location,
3835 0,
3836 this->storage_blocks[storage_block].stage);
3837 }
3838
3839 /* Texture/sampler bindings to interface. */
3840 for (const MSLTextureResource &input_texture : this->texture_samplers) {
3841 /* Determine SSBO bind location for buffer-baked texture's data. */
3842 uint tex_buf_ssbo_location = -1;
3843 uint tex_buf_ssbo_id = input_texture.atomic_fallback_buffer_ssbo_id;
3844 if (tex_buf_ssbo_id != -1) {
3845 tex_buf_ssbo_location = this->storage_blocks[tex_buf_ssbo_id].location;
3846 }
3847
3848 interface->add_texture(name_buffer_copystr(&interface->name_buffer_,
3849 input_texture.name.c_str(),
3850 name_buffer_size,
3851 name_buffer_offset),
3852 input_texture.slot,
3853 input_texture.location,
3854 input_texture.get_texture_binding_type(),
3855 input_texture.get_sampler_format(),
3856 input_texture.is_texture_sampler,
3857 input_texture.stage,
3858 tex_buf_ssbo_location);
3859 }
3860
3861 /* Specialization Constants. */
3862 for (const MSLConstant &constant : this->constants) {
3863 interface->add_constant(name_buffer_copystr(
3864 &interface->name_buffer_, constant.name.c_str(), name_buffer_size, name_buffer_offset));
3865 }
3866
3867 /* Sampler Parameters. */
3868 interface->set_sampler_properties(
3873
3874 /* Map Metal bindings to standardized ShaderInput struct name/binding index. */
3875 interface->prepare_common_shader_inputs(info);
3876
3877 /* Resize name buffer to save some memory. */
3878 if (name_buffer_offset < name_buffer_size) {
3879 interface->name_buffer_ = (char *)MEM_reallocN(interface->name_buffer_, name_buffer_offset);
3880 }
3881
3882 return interface;
3883}
3884
3886{
3887 bool supports_native_atomics = MTLBackend::get_capabilities().supports_texture_atomics;
3888 /* Add Types as needed. */
3889 switch (this->type) {
3890 case ImageType::FLOAT_1D: {
3891 return "texture1d";
3892 }
3893 case ImageType::FLOAT_2D: {
3894 return "texture2d";
3895 }
3896 case ImageType::FLOAT_3D: {
3897 return "texture3d";
3898 }
3899 case ImageType::FLOAT_CUBE: {
3900 return "texturecube";
3901 }
3902 case ImageType::FLOAT_1D_ARRAY: {
3903 return "texture1d_array";
3904 }
3905 case ImageType::FLOAT_2D_ARRAY: {
3906 return "texture2d_array";
3907 }
3908 case ImageType::FLOAT_CUBE_ARRAY: {
3909 return "texturecube_array";
3910 }
3911 case ImageType::FLOAT_BUFFER: {
3912 return "texture_buffer";
3913 }
3914 case ImageType::DEPTH_2D: {
3915 return "depth2d";
3916 }
3917 case ImageType::SHADOW_2D: {
3918 return "depth2d";
3919 }
3920 case ImageType::DEPTH_2D_ARRAY: {
3921 return "depth2d_array";
3922 }
3923 case ImageType::SHADOW_2D_ARRAY: {
3924 return "depth2d_array";
3925 }
3926 case ImageType::DEPTH_CUBE: {
3927 return "depthcube";
3928 }
3929 case ImageType::SHADOW_CUBE: {
3930 return "depthcube";
3931 }
3932 case ImageType::DEPTH_CUBE_ARRAY: {
3933 return "depthcube_array";
3934 }
3935 case ImageType::SHADOW_CUBE_ARRAY: {
3936 return "depthcube_array";
3937 }
3938 case ImageType::INT_1D: {
3939 return "texture1d";
3940 }
3941 case ImageType::INT_2D: {
3942 return "texture2d";
3943 }
3944 case ImageType::INT_3D: {
3945 return "texture3d";
3946 }
3947 case ImageType::INT_CUBE: {
3948 return "texturecube";
3949 }
3950 case ImageType::INT_1D_ARRAY: {
3951 return "texture1d_array";
3952 }
3953 case ImageType::INT_2D_ARRAY: {
3954 return "texture2d_array";
3955 }
3956 case ImageType::INT_CUBE_ARRAY: {
3957 return "texturecube_array";
3958 }
3959 case ImageType::INT_BUFFER: {
3960 return "texture_buffer";
3961 }
3962 case ImageType::UINT_1D: {
3963 return "texture1d";
3964 }
3965 case ImageType::UINT_2D: {
3966 return "texture2d";
3967 }
3968 case ImageType::UINT_3D: {
3969 return "texture3d";
3970 }
3971 case ImageType::UINT_CUBE: {
3972 return "texturecube";
3973 }
3974 case ImageType::UINT_1D_ARRAY: {
3975 return "texture1d_array";
3976 }
3977 case ImageType::UINT_2D_ARRAY: {
3978 return "texture2d_array";
3979 }
3980 case ImageType::UINT_CUBE_ARRAY: {
3981 return "texturecube_array";
3982 }
3983 case ImageType::UINT_BUFFER: {
3984 return "texture_buffer";
3985 }
3986 /* If texture atomics are natively supported, we use the native texture type, otherwise all
3987 * other formats are implemented via texture2d. */
3988 case ImageType::INT_2D_ATOMIC:
3989 case ImageType::UINT_2D_ATOMIC: {
3990 return "texture2d";
3991 }
3992 case ImageType::INT_2D_ARRAY_ATOMIC:
3993 case ImageType::UINT_2D_ARRAY_ATOMIC: {
3994 if (supports_native_atomics) {
3995 return "texture2d_array";
3996 }
3997 else {
3998 return "texture2d";
3999 }
4000 }
4001 case ImageType::INT_3D_ATOMIC:
4002 case ImageType::UINT_3D_ATOMIC: {
4003 if (supports_native_atomics) {
4004 return "texture3d";
4005 }
4006 else {
4007 return "texture2d";
4008 }
4009 }
4010
4011 default: {
4012 /* Unrecognized type. */
4014 return "ERROR";
4015 }
4016 };
4017}
4018
4020{
4021 bool supports_native_atomics = MTLBackend::get_capabilities().supports_texture_atomics;
4022 /* Add Types as needed. */
4023 switch (this->type) {
4024 case ImageType::FLOAT_1D: {
4025 return "_mtl_combined_image_sampler_1d";
4026 }
4027 case ImageType::FLOAT_2D: {
4028 return "_mtl_combined_image_sampler_2d";
4029 }
4030 case ImageType::FLOAT_3D: {
4031 return "_mtl_combined_image_sampler_3d";
4032 }
4033 case ImageType::FLOAT_CUBE: {
4034 return "_mtl_combined_image_sampler_cube";
4035 }
4036 case ImageType::FLOAT_1D_ARRAY: {
4037 return "_mtl_combined_image_sampler_1d_array";
4038 }
4039 case ImageType::FLOAT_2D_ARRAY: {
4040 return "_mtl_combined_image_sampler_2d_array";
4041 }
4042 case ImageType::FLOAT_CUBE_ARRAY: {
4043 return "_mtl_combined_image_sampler_cube_array";
4044 }
4045 case ImageType::FLOAT_BUFFER: {
4046 return "_mtl_combined_image_sampler_buffer";
4047 }
4048 case ImageType::DEPTH_2D: {
4049 return "_mtl_combined_image_sampler_depth_2d";
4050 }
4051 case ImageType::SHADOW_2D: {
4052 return "_mtl_combined_image_sampler_depth_2d";
4053 }
4054 case ImageType::DEPTH_2D_ARRAY: {
4055 return "_mtl_combined_image_sampler_depth_2d_array";
4056 }
4057 case ImageType::SHADOW_2D_ARRAY: {
4058 return "_mtl_combined_image_sampler_depth_2d_array";
4059 }
4060 case ImageType::DEPTH_CUBE: {
4061 return "_mtl_combined_image_sampler_depth_cube";
4062 }
4063 case ImageType::SHADOW_CUBE: {
4064 return "_mtl_combined_image_sampler_depth_cube";
4065 }
4066 case ImageType::DEPTH_CUBE_ARRAY: {
4067 return "_mtl_combined_image_sampler_depth_cube_array";
4068 }
4069 case ImageType::SHADOW_CUBE_ARRAY: {
4070 return "_mtl_combined_image_sampler_depth_cube_array";
4071 }
4072 case ImageType::INT_1D: {
4073 return "_mtl_combined_image_sampler_1d";
4074 }
4075 case ImageType::INT_2D: {
4076 return "_mtl_combined_image_sampler_2d";
4077 }
4078 case ImageType::INT_3D: {
4079 return "_mtl_combined_image_sampler_3d";
4080 }
4081 case ImageType::INT_CUBE: {
4082 return "_mtl_combined_image_sampler_cube";
4083 }
4084 case ImageType::INT_1D_ARRAY: {
4085 return "_mtl_combined_image_sampler_1d_array";
4086 }
4087 case ImageType::INT_2D_ARRAY: {
4088 return "_mtl_combined_image_sampler_2d_array";
4089 }
4090 case ImageType::INT_CUBE_ARRAY: {
4091 return "_mtl_combined_image_sampler_cube_array";
4092 }
4093 case ImageType::INT_BUFFER: {
4094 return "_mtl_combined_image_sampler_buffer";
4095 }
4096 case ImageType::UINT_1D: {
4097 return "_mtl_combined_image_sampler_1d";
4098 }
4099 case ImageType::UINT_2D: {
4100 return "_mtl_combined_image_sampler_2d";
4101 }
4102 case ImageType::UINT_3D: {
4103 return "_mtl_combined_image_sampler_3d";
4104 }
4105 case ImageType::UINT_CUBE: {
4106 return "_mtl_combined_image_sampler_cube";
4107 }
4108 case ImageType::UINT_1D_ARRAY: {
4109 return "_mtl_combined_image_sampler_1d_array";
4110 }
4111 case ImageType::UINT_2D_ARRAY: {
4112 return "_mtl_combined_image_sampler_2d_array";
4113 }
4114 case ImageType::UINT_CUBE_ARRAY: {
4115 return "_mtl_combined_image_sampler_cube_array";
4116 }
4117 case ImageType::UINT_BUFFER: {
4118 return "_mtl_combined_image_sampler_buffer";
4119 }
4120 /* If native texture atomics are unsupported, map types to fallback atomic structures which
4121 * contain a buffer pointer and metadata members for size and alignment. */
4122 case ImageType::INT_2D_ATOMIC:
4123 case ImageType::UINT_2D_ATOMIC: {
4124 if (supports_native_atomics) {
4125 return "_mtl_combined_image_sampler_2d";
4126 }
4127 else {
4128 return "_mtl_combined_image_sampler_2d_atomic_fallback";
4129 }
4130 }
4131 case ImageType::INT_3D_ATOMIC:
4132 case ImageType::UINT_3D_ATOMIC: {
4133 if (supports_native_atomics) {
4134 return "_mtl_combined_image_sampler_3d";
4135 }
4136 else {
4137 return "_mtl_combined_image_sampler_3d_atomic_fallback";
4138 }
4139 }
4140 case ImageType::INT_2D_ARRAY_ATOMIC:
4141 case ImageType::UINT_2D_ARRAY_ATOMIC: {
4142 if (supports_native_atomics) {
4143 return "_mtl_combined_image_sampler_2d_array";
4144 }
4145 else {
4146 return "_mtl_combined_image_sampler_2d_array_atomic_fallback";
4147 }
4148 }
4149 default: {
4150 /* Unrecognized type. */
4152 return "ERROR";
4153 }
4154 };
4155}
4156
4158{
4159 /* Add Types as needed */
4160 switch (this->type) {
4161 /* Floating point return. */
4162 case ImageType::FLOAT_1D:
4163 case ImageType::FLOAT_2D:
4164 case ImageType::FLOAT_3D:
4165 case ImageType::FLOAT_CUBE:
4166 case ImageType::FLOAT_1D_ARRAY:
4167 case ImageType::FLOAT_2D_ARRAY:
4168 case ImageType::FLOAT_CUBE_ARRAY:
4169 case ImageType::FLOAT_BUFFER:
4170 case ImageType::DEPTH_2D:
4171 case ImageType::SHADOW_2D:
4172 case ImageType::DEPTH_2D_ARRAY:
4173 case ImageType::SHADOW_2D_ARRAY:
4174 case ImageType::DEPTH_CUBE:
4175 case ImageType::SHADOW_CUBE:
4176 case ImageType::DEPTH_CUBE_ARRAY:
4177 case ImageType::SHADOW_CUBE_ARRAY: {
4178 return "float";
4179 }
4180 /* Integer return. */
4181 case ImageType::INT_1D:
4182 case ImageType::INT_2D:
4183 case ImageType::INT_3D:
4184 case ImageType::INT_CUBE:
4185 case ImageType::INT_1D_ARRAY:
4186 case ImageType::INT_2D_ARRAY:
4187 case ImageType::INT_CUBE_ARRAY:
4188 case ImageType::INT_BUFFER:
4189 case ImageType::INT_2D_ATOMIC:
4190 case ImageType::INT_2D_ARRAY_ATOMIC:
4191 case ImageType::INT_3D_ATOMIC: {
4192 return "int";
4193 }
4194
4195 /* Unsigned Integer return. */
4196 case ImageType::UINT_1D:
4197 case ImageType::UINT_2D:
4198 case ImageType::UINT_3D:
4199 case ImageType::UINT_CUBE:
4200 case ImageType::UINT_1D_ARRAY:
4201 case ImageType::UINT_2D_ARRAY:
4202 case ImageType::UINT_CUBE_ARRAY:
4203 case ImageType::UINT_BUFFER:
4204 case ImageType::UINT_2D_ATOMIC:
4205 case ImageType::UINT_2D_ARRAY_ATOMIC:
4206 case ImageType::UINT_3D_ATOMIC: {
4207 return "uint32_t";
4208 }
4209
4210 default: {
4211 /* Unrecognized type. */
4213 return "ERROR";
4214 }
4215 };
4216}
4217
4219{
4220 /* Add Types as needed */
4221 switch (this->type) {
4222 case ImageType::FLOAT_1D: {
4223 return GPU_TEXTURE_1D;
4224 }
4225 case ImageType::FLOAT_2D: {
4226 return GPU_TEXTURE_2D;
4227 }
4228 case ImageType::FLOAT_3D: {
4229 return GPU_TEXTURE_3D;
4230 }
4231 case ImageType::FLOAT_CUBE: {
4232 return GPU_TEXTURE_CUBE;
4233 }
4234 case ImageType::FLOAT_1D_ARRAY: {
4235 return GPU_TEXTURE_1D_ARRAY;
4236 }
4237 case ImageType::FLOAT_2D_ARRAY: {
4238 return GPU_TEXTURE_2D_ARRAY;
4239 }
4240 case ImageType::FLOAT_CUBE_ARRAY: {
4242 }
4243 case ImageType::FLOAT_BUFFER: {
4244 return GPU_TEXTURE_BUFFER;
4245 }
4246 case ImageType::DEPTH_2D: {
4247 return GPU_TEXTURE_2D;
4248 }
4249 case ImageType::SHADOW_2D: {
4250 return GPU_TEXTURE_2D;
4251 }
4252 case ImageType::DEPTH_2D_ARRAY: {
4253 return GPU_TEXTURE_2D_ARRAY;
4254 }
4255 case ImageType::SHADOW_2D_ARRAY: {
4256 return GPU_TEXTURE_2D_ARRAY;
4257 }
4258 case ImageType::DEPTH_CUBE: {
4259 return GPU_TEXTURE_CUBE;
4260 }
4261 case ImageType::SHADOW_CUBE: {
4262 return GPU_TEXTURE_CUBE;
4263 }
4264 case ImageType::DEPTH_CUBE_ARRAY: {
4266 }
4267 case ImageType::SHADOW_CUBE_ARRAY: {
4269 }
4270 case ImageType::INT_1D: {
4271 return GPU_TEXTURE_1D;
4272 }
4273 case ImageType::INT_2D: {
4274 return GPU_TEXTURE_2D;
4275 }
4276 case ImageType::INT_3D: {
4277 return GPU_TEXTURE_3D;
4278 }
4279 case ImageType::INT_CUBE: {
4280 return GPU_TEXTURE_CUBE;
4281 }
4282 case ImageType::INT_1D_ARRAY: {
4283 return GPU_TEXTURE_1D_ARRAY;
4284 }
4285 case ImageType::INT_2D_ARRAY: {
4286 return GPU_TEXTURE_2D_ARRAY;
4287 }
4288 case ImageType::INT_CUBE_ARRAY: {
4290 }
4291 case ImageType::INT_BUFFER: {
4292 return GPU_TEXTURE_BUFFER;
4293 }
4294 case ImageType::UINT_1D: {
4295 return GPU_TEXTURE_1D;
4296 }
4297 case ImageType::UINT_2D:
4298 case ImageType::UINT_2D_ATOMIC:
4299 case ImageType::INT_2D_ATOMIC: {
4300 return GPU_TEXTURE_2D;
4301 }
4302 case ImageType::UINT_3D:
4303 case ImageType::UINT_3D_ATOMIC:
4304 case ImageType::INT_3D_ATOMIC: {
4305 return GPU_TEXTURE_3D;
4306 }
4307 case ImageType::UINT_CUBE: {
4308 return GPU_TEXTURE_CUBE;
4309 }
4310 case ImageType::UINT_1D_ARRAY: {
4311 return GPU_TEXTURE_1D_ARRAY;
4312 }
4313 case ImageType::UINT_2D_ARRAY:
4314 case ImageType::UINT_2D_ARRAY_ATOMIC:
4315 case ImageType::INT_2D_ARRAY_ATOMIC: {
4316 return GPU_TEXTURE_2D_ARRAY;
4317 }
4318 case ImageType::UINT_CUBE_ARRAY: {
4320 }
4321 case ImageType::UINT_BUFFER: {
4322 return GPU_TEXTURE_BUFFER;
4323 }
4324 default: {
4326 return GPU_TEXTURE_2D;
4327 }
4328 };
4329}
4330
4332{
4333 switch (this->type) {
4334 case ImageType::FLOAT_BUFFER:
4335 case ImageType::FLOAT_1D:
4336 case ImageType::FLOAT_1D_ARRAY:
4337 case ImageType::FLOAT_2D:
4338 case ImageType::FLOAT_2D_ARRAY:
4339 case ImageType::FLOAT_3D:
4340 case ImageType::FLOAT_CUBE:
4341 case ImageType::FLOAT_CUBE_ARRAY:
4343 case ImageType::INT_BUFFER:
4344 case ImageType::INT_1D:
4345 case ImageType::INT_1D_ARRAY:
4346 case ImageType::INT_2D:
4347 case ImageType::INT_2D_ARRAY:
4348 case ImageType::INT_3D:
4349 case ImageType::INT_CUBE:
4350 case ImageType::INT_CUBE_ARRAY:
4351 case ImageType::INT_2D_ATOMIC:
4352 case ImageType::INT_3D_ATOMIC:
4353 case ImageType::INT_2D_ARRAY_ATOMIC:
4354 return GPU_SAMPLER_TYPE_INT;
4355 case ImageType::UINT_BUFFER:
4356 case ImageType::UINT_1D:
4357 case ImageType::UINT_1D_ARRAY:
4358 case ImageType::UINT_2D:
4359 case ImageType::UINT_2D_ARRAY:
4360 case ImageType::UINT_3D:
4361 case ImageType::UINT_CUBE:
4362 case ImageType::UINT_CUBE_ARRAY:
4363 case ImageType::UINT_2D_ATOMIC:
4364 case ImageType::UINT_3D_ATOMIC:
4365 case ImageType::UINT_2D_ARRAY_ATOMIC:
4366 return GPU_SAMPLER_TYPE_UINT;
4367 case ImageType::SHADOW_2D:
4368 case ImageType::SHADOW_2D_ARRAY:
4369 case ImageType::SHADOW_CUBE:
4370 case ImageType::SHADOW_CUBE_ARRAY:
4371 case ImageType::DEPTH_2D:
4372 case ImageType::DEPTH_2D_ARRAY:
4373 case ImageType::DEPTH_CUBE:
4374 case ImageType::DEPTH_CUBE_ARRAY:
4376 default:
4378 }
4380}
4381
4384} // namespace blender::gpu
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
MINLINE uint max_uu(uint a, uint b)
MINLINE int max_ii(int a, int b)
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
unsigned int uint
#define ARRAY_SET_ITEMS(...)
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
int GPU_max_samplers()
int GPU_max_textures_vert()
@ GPU_ARCHITECTURE_TBDR
GPUArchitectureType GPU_platform_architecture()
@ GPU_SHADER_TFB_NONE
#define GPU_VERT_ATTR_MAX_LEN
#define MEM_reallocN(vmemh, len)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
vertex_source("basic_depth_pointcloud_vert.glsl") .additional_info("draw_pointcloud")
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int64_t size() const
void append(const T &value)
std::string generate_msl_texture_vars(ShaderStage shader_stage)
std::string generate_msl_uniform_structs(ShaderStage shader_stage)
std::string generate_msl_global_uniform_population(ShaderStage stage)
blender::Vector< MSLBufferBlock > uniform_blocks
blender::Vector< MSLBufferBlock > storage_blocks
std::string generate_msl_uniform_block_population(ShaderStage stage)
blender::Vector< MSLFragmentTileInputAttribute > fragment_tile_inputs
std::string generate_msl_vertex_transform_feedback_out_struct(ShaderStage shader_stage)
blender::Vector< MSLVertexOutputAttribute > fragment_input_varyings
blender::Vector< MSLVertexOutputAttribute > vertex_output_varyings
uint32_t get_sampler_argument_buffer_bind_index(ShaderStage stage)
blender::Vector< MSLTextureResource > texture_samplers
blender::Vector< MSLVertexInputAttribute > vertex_input_attributes
std::string generate_msl_fragment_struct(bool is_input)
std::string generate_msl_uniform_undefs(ShaderStage stage)
void prepare_from_createinfo(const shader::ShaderCreateInfo *info)
uint32_t max_sampler_index_for_stage(ShaderStage stage) const
blender::Vector< MSLConstant > constants
std::string generate_msl_vertex_out_struct(ShaderStage shader_stage)
uint32_t num_samplers_for_stage(ShaderStage stage) const
void generate_msl_uniforms_input_string(std::stringstream &out, ShaderStage stage, bool &is_first_parameter)
blender::Vector< MSLFragmentOutputAttribute > fragment_outputs
blender::Vector< MSLVertexOutputAttribute > vertex_output_varyings_tf
MTLShaderInterface * bake_shader_interface(const char *name, const shader::ShaderCreateInfo *info=nullptr)
void generate_msl_textures_input_string(std::stringstream &out, ShaderStage stage, bool &is_first_parameter)
blender::Vector< MSLSharedMemoryBlock > shared_memory_blocks
blender::Vector< MSLUniform > uniforms
static MTLCapabilities & get_capabilities()
const char * get_name_at_offset(uint32_t offset) const
const MTLShaderInputAttribute & get_attribute(uint index) const
void set_fragment_function_name(NSString *fragment_function_name)
std::string vertex_interface_declare(const shader::ShaderCreateInfo &info) const override
void shader_compute_source_from_msl(NSString *input_compute_source)
std::string compute_layout_declare(const shader::ShaderCreateInfo &info) const override
std::string fragment_interface_declare(const shader::ShaderCreateInfo &info) const override
bool has_transform_feedback_varying(std::string str)
void set_vertex_function_name(NSString *vetex_function_name)
void shader_source_from_msl(NSString *input_vertex_source, NSString *input_fragment_source)
MTLShaderInterface * get_interface()
std::string geometry_layout_declare(const shader::ShaderCreateInfo &info) const override
std::string resources_declare(const shader::ShaderCreateInfo &info) const override
void set_interface(MTLShaderInterface *interface)
void set_compute_function_name(NSString *compute_function_name)
const ShaderInput * uniform_get(const char *name) const
ShaderInterface * interface
const char *const name_get() const
output_img push_constant(Type::FLOAT, "subtrahend") .define("TYPE"
EvaluationStage stage
Definition deg_eval.cc:83
int len
#define str(s)
int count
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
static void error(const char *str)
static void clear(Message &msg)
Definition msgfmt.cc:218
#define MTL_MAX_BUFFER_BINDINGS
#define MTL_MAX_TEXTURE_SLOTS
#define MTL_MAX_DEFAULT_SAMPLERS
#define MTL_LOG_INFO(info,...)
Definition mtl_debug.hh:51
#define MTL_LOG_WARNING(info,...)
Definition mtl_debug.hh:44
#define MTL_LOG_ERROR(info,...)
Definition mtl_debug.hh:36
#define shader_debug_printf(...)
Definition mtl_shader.hh:49
#define MTL_SHADER_SPECIALIZATION_CONSTANT_BASE_ID
Definition mtl_shader.hh:53
#define UNIFORM_SSBO_VBO_ID_STR
#define UNIFORM_SSBO_OFFSET_STR
#define UNIFORM_SSBO_INPUT_VERT_COUNT_STR
#define UNIFORM_SSBO_INPUT_PRIM_TYPE_STR
#define UNIFORM_SSBO_STRIDE_STR
#define UNIFORM_SSBO_USES_INDEXED_RENDERING_STR
#define UNIFORM_SSBO_INDEX_MODE_U16_STR
#define UNIFORM_SSBO_FETCHMODE_STR
#define UNIFORM_SSBO_TYPE_STR
#define UNIFORM_SSBO_INDEX_BASE_STR
#define FRAGMENT_TILE_IN_STRUCT_NAME
char datatoc_mtl_shader_defines_msl[]
#define ATOMIC_DEFINE_STR
#define FRAGMENT_OUT_STRUCT_NAME
char datatoc_mtl_shader_shared_h[]
@ MTL_DATATYPE_INT1010102_NORM
uint mtl_get_data_type_size(eMTLDataType type)
#define MTL_SSBO_VERTEX_FETCH_MAX_VBOS
BLI_INLINE int to_component_count(const Type &type)
static void replace_array_initializers_func(std::string &str)
uint get_shader_stage_index(ShaderStage stage)
char * next_word_in_range(char *begin, char *end)
const char * to_string(ShaderStage stage)
Definition mtl_shader.mm:52
bool is_matrix_type(const std::string &type)
static void replace_matrix_constructors(std::string &str)
static uint32_t name_buffer_copystr(char **name_buffer_ptr, const char *str_to_copy, uint32_t &name_buffer_size, uint32_t &name_buffer_offset)
constexpr size_t const_strlen(const char *str)
static bool is_program_word(const char *chr, int *len)
static void extract_and_replace_clipping_distances(std::string &vertex_source, MSLGeneratorInterface &msl_iface)
static void remove_multiline_comments_func(std::string &str)
static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &res)
const char * get_shader_stage_instance_name(ShaderStage stage)
static bool extract_ssbo_pragma_info(const MTLShader *shader, const MSLGeneratorInterface &, const std::string &in_vertex_src, MTLPrimitiveType &out_prim_tye, uint32_t &out_num_output_verts)
static void replace_outvars(std::string &str)
const char * to_string_msl(const shader::Interpolation &interp)
static char parameter_delimiter(bool &is_first_parameter)
const char * get_stage_class_name(ShaderStage stage)
static std::regex remove_non_numeric_characters("[^0-9]")
int get_matrix_location_count(const std::string &type)
static MTLSamplerAddressMode to_mtl_type(GPUSamplerExtendMode wrap_mode)
static void extract_global_scope_constants(std::string &str, std::stringstream &)
std::string get_matrix_subtype(const std::string &type)
static int backwards_program_word_scan(const char *array_loc, const char *min)
static void remove_singleline_comments_func(std::string &str)
std::string get_attribute_conversion_function(bool *uses_conversion, const shader::Type &type)
bool is_builtin_type(std::string type)
char * next_symbol_in_range(char *begin, char *end, char symbol)
static bool balanced_braces(char *current_str_begin, char *current_str_end)
static void generate_specialization_constant_declarations(const shader::ShaderCreateInfo *info, std::stringstream &ss)
void extract_shared_memory_blocks(MSLGeneratorInterface &msl_iface, std::string &glsl_compute_source)
MTLVertexFormat mtl_datatype_to_vertex_type(eMTLDataType type)
#define min(a, b)
Definition sort.c:32
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
unsigned char uint8_t
Definition stdint.h:78
eGPUSamplerFormat get_sampler_format() const
eGPUTextureType get_texture_binding_type() const
void set_compute_workgroup_size(int workgroup_size_x, int workgroup_size_y, int workgroup_size_z)
Describe inputs & outputs, stage interfaces, resources and sources of a shader. If all data is correc...
Vector< SpecializationConstant > specialization_constants_