Blender V5.0
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
8
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
39
40using namespace blender;
41using namespace blender::gpu;
42using namespace blender::gpu::shader;
43
44namespace blender::gpu {
45
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/* -------------------------------------------------------------------- */
56
57static void split_array(StringRefNull input, std::string &r_name, std::string &r_array)
58{
59 size_t array_start = input.find('[');
60 if (array_start != std::string::npos) {
61 r_name = input.substr(0, array_start);
62 r_array = input.substr(array_start);
63 }
64 else {
65 r_name = input;
66 r_array = "";
67 }
68}
69
71{
72 switch (type) {
73 case Type::float_t:
74 return MTL_DATATYPE_FLOAT;
75 case Type::float2_t:
77 case Type::float3_t:
79 case Type::float4_t:
85 case Type::uint_t:
86 return MTL_DATATYPE_UINT;
87 case Type::uint2_t:
88 return MTL_DATATYPE_UINT2;
89 case Type::uint3_t:
90 return MTL_DATATYPE_UINT3;
91 case Type::uint4_t:
92 return MTL_DATATYPE_UINT4;
93 case Type::int_t:
94 return MTL_DATATYPE_INT;
95 case Type::int2_t:
96 return MTL_DATATYPE_INT2;
97 case Type::int3_t:
98 return MTL_DATATYPE_INT3;
99 case Type::int4_t:
100 return MTL_DATATYPE_INT4;
103 case Type::bool_t:
104 return MTL_DATATYPE_BOOL;
105 case Type::uchar_t:
106 return MTL_DATATYPE_UCHAR;
107 case Type::uchar2_t:
108 return MTL_DATATYPE_UCHAR2;
109 case Type::uchar3_t:
110 return MTL_DATATYPE_UCHAR3;
111 case Type::uchar4_t:
112 return MTL_DATATYPE_UCHAR4;
113 case Type::char_t:
114 return MTL_DATATYPE_CHAR;
115 case Type::char2_t:
116 return MTL_DATATYPE_CHAR2;
117 case Type::char3_t:
118 return MTL_DATATYPE_CHAR3;
119 case Type::char4_t:
120 return MTL_DATATYPE_CHAR4;
121 case Type::ushort_t:
122 return MTL_DATATYPE_USHORT;
123 case Type::ushort2_t:
125 case Type::ushort3_t:
127 case Type::ushort4_t:
129 case Type::short_t:
130 return MTL_DATATYPE_SHORT;
131 case Type::short2_t:
132 return MTL_DATATYPE_SHORT2;
133 case Type::short3_t:
134 return MTL_DATATYPE_SHORT3;
135 case Type::short4_t:
136 return MTL_DATATYPE_SHORT4;
137 default: {
138 BLI_assert_msg(false, "Unexpected data type");
139 }
140 }
141 return MTL_DATATYPE_FLOAT;
142}
143
144static std::regex remove_non_numeric_characters("[^0-9]");
145
146/* Extract clipping distance usage indices, and replace syntax with metal-compatible.
147 * We need to replace syntax gl_ClipDistance[N] with gl_ClipDistance_N such that it is compatible
148 * with the Metal shaders Vertex shader output struct. */
149static void extract_and_replace_clipping_distances(std::string &vertex_source,
150 MSLGeneratorInterface &msl_iface)
151{
152 char *current_str_begin = &*vertex_source.begin();
153 char *current_str_end = &*vertex_source.end();
154
155 for (char *c = current_str_begin + 2; c < current_str_end - 18; c++) {
156 char *base_search = strstr(c, "gl_ClipDistance[");
157 if (base_search == nullptr) {
158 /* No clip distances found. */
159 return;
160 }
161 c = base_search + 16;
162
163 /* Ensure closing brace. */
164 if (*(c + 1) != ']') {
165 continue;
166 }
167
168 /* Extract ID between zero and 9. */
169 if ((*c >= '0') && (*c <= '9')) {
170 char clip_distance_id = ((*c) - '0');
171 auto found = std::find(
172 msl_iface.clip_distances.begin(), msl_iface.clip_distances.end(), clip_distance_id);
173 if (found == msl_iface.clip_distances.end()) {
174 msl_iface.clip_distances.append(clip_distance_id);
175 }
176
177 /* Replace syntax (array brace removal, and replacement with underscore). */
178 *(base_search + 15) = '_';
179 *(base_search + 17) = ' ';
180 }
181 }
182}
183
185
186/* -------------------------------------------------------------------- */
189
190static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &res)
191{
192 switch (res.bind_type) {
194 break;
196 break;
198 int64_t array_offset = res.uniformbuf.name.find_first_of("[");
199 if (array_offset == -1) {
200 /* Create local class member as constant pointer reference to bound UBO buffer.
201 * Given usage within a shader follows ubo_name.ubo_element syntax, we can
202 * dereference the pointer as the compiler will optimize this data fetch.
203 * To do this, we also give the UBO name a post-fix of `_local` to avoid
204 * macro accessor collisions. */
205 os << "constant " << res.uniformbuf.type_name << " *" << res.uniformbuf.name
206 << "_local;\n";
207 os << "#define " << res.uniformbuf.name << " (*" << res.uniformbuf.name << "_local)\n";
208 }
209 else {
210 /* For arrays, we can directly provide the constant access pointer, as the array
211 * syntax will de-reference this at the correct fetch index. */
212 StringRef name_no_array = StringRef(res.uniformbuf.name.c_str(), array_offset);
213 os << "constant " << res.uniformbuf.type_name << " *" << name_no_array << ";\n";
214 }
215 break;
216 }
218 int64_t array_offset = res.storagebuf.name.find_first_of("[");
219 bool writeable = (res.storagebuf.qualifiers & shader::Qualifier::write) ==
221 const char *memory_scope = ((writeable) ? "device " : "constant ");
222 if (array_offset == -1) {
223 /* Create local class member as device pointer reference to bound SSBO.
224 * Given usage within a shader follows ssbo_name.ssbo_element syntax, we can
225 * dereference the pointer as the compiler will optimize this data fetch.
226 * To do this, we also give the UBO name a post-fix of `_local` to avoid
227 * macro accessor collisions. */
228
229 os << memory_scope << res.storagebuf.type_name << " *" << res.storagebuf.name
230 << "_local;\n";
231 os << "#define " << res.storagebuf.name << " (*" << res.storagebuf.name << "_local)\n";
232 }
233 else {
234 /* For arrays, we can directly provide the constant access pointer, as the array
235 * syntax will de-reference this at the correct fetch index. */
236 StringRef name_no_array = StringRef(res.storagebuf.name.c_str(), array_offset);
237 os << memory_scope << res.storagebuf.type_name << " *" << name_no_array << ";\n";
238 }
239 break;
240 }
241 }
242}
243
244std::string MTLShader::resources_declare(const ShaderCreateInfo &info) const
245{
246 /* NOTE(Metal): We only use the upfront preparation functions to populate members which
247 * would exist in the original non-create-info variant.
248 *
249 * This function is only used to generate resource structs.
250 * Global-scope handles for Uniforms, UBOs, textures and samplers
251 * are generated during class-wrapper construction in `generate_msl_from_glsl`. */
252 std::stringstream ss;
253
254 ss << "\n/* Shared Variables. */\n";
256 std::string array, name;
258 ss << "threadgroup " << to_string(sv.type) << " (&" << name << ")" << array << ";\n";
259 }
260 /* Generate resource stubs for UBOs and textures. */
261 ss << "\n/* Pass Resources. */\n";
262 for (const ShaderCreateInfo::Resource &res : info.pass_resources_) {
263 print_resource(ss, res);
264 }
265 ss << "\n/* Batch Resources. */\n";
266 for (const ShaderCreateInfo::Resource &res : info.batch_resources_) {
267 print_resource(ss, res);
268 }
269 ss << "\n/* Geometry Resources. */\n";
270 for (const ShaderCreateInfo::Resource &res : info.geometry_resources_) {
271 print_resource(ss, res);
272 }
273 /* NOTE: Push constant uniform data is generated during `generate_msl_from_glsl`
274 * as the generated output is needed for all paths. This includes generation
275 * of the push constant data structure (struct PushConstantBlock).
276 * As all shader generation paths require creation of this. */
277 return ss.str();
278}
279
281{
282 /* NOTE(Metal): We only use the upfront preparation functions to populate members which
283 * would exist in the original non-create-info variant.
284 *
285 * Here we generate the variables within class wrapper scope to allow reading of
286 * input attributes by the main code. */
287 std::stringstream ss;
288 ss << "\n/* Vertex Inputs. */\n";
289 for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) {
290 ss << to_string(attr.type) << " " << attr.name << ";\n";
291 }
292 return ss.str();
293}
294
296{
297 /* For shaders generated from MSL, the fragment-output struct is generated as part of the entry
298 * stub during glsl->MSL conversion in `generate_msl_from_glsl`.
299 * Here, we can instead generate the global-scope variables which will be populated during
300 * execution.
301 *
302 * NOTE: The output declaration for location and blend index are generated in the entry-point
303 * struct. This is simply a mirror class member which stores the value during main shader body
304 * execution. */
305 std::stringstream ss;
306 ss << "\n/* Fragment Outputs. */\n";
308 ss << to_string(output.type) << " " << output.name << ";\n";
309 }
310 ss << "\n";
311
312 ss << "\n/* Fragment Tile inputs. */\n";
314 ss << to_string(input.type) << " " << input.name << ";\n";
315 }
316 ss << "\n";
317
318 return ss.str();
319}
320
321std::string MTLShader::MTLShader::geometry_interface_declare(
322 const shader::ShaderCreateInfo & /*info*/) const
323{
324 BLI_assert_msg(false, "Geometry shading unsupported by Metal");
325 return "";
326}
327
329{
330 BLI_assert_msg(false, "Geometry shading unsupported by Metal");
331 return "";
332}
333
334std::string MTLShader::compute_layout_declare(const ShaderCreateInfo & /*info*/) const
335{
336 /* Metal supports compute shaders. THis function is a pass-through.
337 * Compute shader interface population happens during mtl_shader_generator, as part of GLSL
338 * conversion. */
339 return "";
340}
341
343
344/* -------------------------------------------------------------------- */
347
349{
351 if (msl_patch_default != nullptr) {
352 msl_patch_default_lock.unlock();
353 return msl_patch_default;
354 }
355
356 std::stringstream ss_patch;
357 ss_patch << datatoc_mtl_shader_shared_hh << std::endl;
358 size_t len = strlen(ss_patch.str().c_str()) + 1;
359
360 msl_patch_default = (char *)malloc(len * sizeof(char));
361 memcpy(msl_patch_default, ss_patch.str().c_str(), len * sizeof(char));
362 msl_patch_default_lock.unlock();
363 return msl_patch_default;
364}
365
366static void shared_variable_args(const shader::ShaderCreateInfo &info, std::stringstream &ss)
367{
368 bool first = true;
370 std::string array, name;
371 split_array(var.name, name, array);
372 ss << (first ? ' ' : ',') << "threadgroup " << to_string(var.type) << "(&_" << name << ")"
373 << array;
374 first = false;
375 }
376}
377
378static void shared_variable_assign(const shader::ShaderCreateInfo &info, std::stringstream &ss)
379{
380 bool first = true;
382 std::string array, name;
383 split_array(var.name, name, array);
384 ss << (first ? ':' : ',') << name << "(_" << name << ")";
385 first = false;
386 }
387}
388
389static void shared_variable_declare(const shader::ShaderCreateInfo &info, std::stringstream &ss)
390{
392 std::string array, name;
393 split_array(var.name, name, array);
394 ss << "threadgroup " << to_string(var.type) << ' ' << name << array << ";\n";
395 }
396}
397
398static void shared_variable_pass(const shader::ShaderCreateInfo &info, std::stringstream &ss)
399{
400 bool first = true;
401 if (info.shared_variables_.is_empty()) {
402 return;
403 }
404 ss << "(";
406 std::string array, name;
407 split_array(var.name, name, array);
408 ss << (first ? ' ' : ',') << name;
409 first = false;
410 }
411 ss << ")";
412}
413
414/* Specialization constants will evaluate using a dynamic value if provided at PSO compile time. */
416 std::stringstream &ss)
417{
419 for (const SpecializationConstant &sc : info->specialization_constants_) {
420 /* TODO(Metal): Output specialization constant chain. */
421 ss << "constant " << sc.type << " " << sc.name << " [[function_constant(" << index << ")]];\n";
422 index++;
423 }
424}
425
427 std::stringstream &ss)
428{
429 for (const CompilationConstant &cc : info->compilation_constants_) {
430 std::string value;
431 std::string value_define;
432 switch (cc.type) {
433 case Type::uint_t:
434 value = std::to_string(cc.value.u);
435 break;
436 case Type::int_t:
437 value = std::to_string(cc.value.i);
438 break;
439 case Type::bool_t:
440 value = cc.value.u ? "true" : "false";
441 value_define = std::to_string(cc.value.u);
442 break;
443 default:
445 }
446 ss << "constant " << cc.type << " " << cc.name << " = " << value << ";\n";
447 }
448}
449
450bool MTLShader::generate_msl_from_glsl(const shader::ShaderCreateInfo *info)
451{
452 /* Verify if create-info is available.
453 * NOTE(Metal): For now, only support creation from CreateInfo.
454 * If needed, we can perform source translation without this using
455 * manual reflection. */
456 bool uses_create_info = info != nullptr;
457 if (!uses_create_info) {
458 MTL_LOG_WARNING("Unable to compile shader %p '%s' as no create-info was provided!",
459 this,
460 this->name_get().c_str());
461 valid_ = false;
462 return false;
463 }
464
465 /* Compute shaders use differing compilation path. */
466 if (shd_builder_->glsl_compute_source_.empty() == false) {
467 return this->generate_msl_from_glsl_compute(info);
468 }
469
470 /* #MSLGeneratorInterface is a class populated to describe all parameters, resources, bindings
471 * and features used by the source GLSL shader. This information is then used to generate the
472 * appropriate Metal entry points and perform any required source translation. */
473 MSLGeneratorInterface msl_iface(*this);
474 BLI_assert(shd_builder_ != nullptr);
475
476 /* Populate #MSLGeneratorInterface from Create-Info.
477 * NOTE: this is a separate path as #MSLGeneratorInterface can also be manually populated
478 * from parsing, if support for shaders without create-info is required. */
479 msl_iface.prepare_from_createinfo(info);
480
481 /* Verify Source sizes are greater than zero. */
482 BLI_assert(shd_builder_->glsl_vertex_source_.empty() == false);
483 BLI_assert(shd_builder_->glsl_fragment_source_.empty() == false);
484
485 /* Concatenate msl_shader_defines to provide functionality mapping
486 * from GLSL to MSL. Also include additional GPU defines for
487 * optional high-level feature support. */
488 std::string msl_defines_string = "#define GPU_ARB_shader_draw_parameters 1\n";
489 msl_defines_string += "#define GPU_ARB_clip_control 1\n";
490
491 /* NOTE(Metal): textureGather appears to not function correctly on non-Apple-silicon GPUs.
492 * Manifests as selection outlines not showing up (#103412). Disable texture gather if
493 * not suitable for use. */
494 if (MTLBackend::get_capabilities().supports_texture_gather) {
495 msl_defines_string += "#define GPU_ARB_texture_gather 1\n";
496 }
497
498 shd_builder_->glsl_vertex_source_ = msl_defines_string + shd_builder_->glsl_vertex_source_;
499 shd_builder_->glsl_fragment_source_ = msl_defines_string + shd_builder_->glsl_fragment_source_;
500
501 /**** Extract usage of GL globals. ****/
502 /* NOTE(METAL): Currently still performing fallback string scan, as info->builtins_ does
503 * not always contain the usage flag. This can be removed once all appropriate create-info's
504 * have been updated. In some cases, this may incur a false positive if access is guarded
505 * behind a macro. Though in these cases, unused code paths and parameters will be
506 * optimized out by the Metal shader compiler. */
507
509 msl_iface.uses_gl_VertexID = bool(info->builtins_ & BuiltinBits::VERTEX_ID) ||
510 shd_builder_->glsl_vertex_source_.find("gl_VertexID") !=
511 std::string::npos;
512 msl_iface.uses_gl_InstanceID = bool(info->builtins_ & BuiltinBits::INSTANCE_ID) ||
513 shd_builder_->glsl_vertex_source_.find("gl_InstanceID") !=
514 std::string::npos ||
515 shd_builder_->glsl_vertex_source_.find("gpu_InstanceIndex") !=
516 std::string::npos;
517
518 /* instance ID in GL is `[0, instance_count]` in metal it is
519 * `[base_instance, base_instance + instance_count]`,
520 * so we need to offset instance_ID by base instance in Metal --
521 * Thus we expose the `[[base_instance]]` attribute if instance ID is used at all. */
522 msl_iface.uses_gl_BaseInstanceARB = msl_iface.uses_gl_InstanceID ||
523 shd_builder_->glsl_vertex_source_.find(
524 "gl_BaseInstanceARB") != std::string::npos ||
525 shd_builder_->glsl_vertex_source_.find("gpu_BaseInstance") !=
526 std::string::npos;
527 msl_iface.uses_gl_Position = shd_builder_->glsl_vertex_source_.find("gl_Position") !=
528 std::string::npos;
529 msl_iface.uses_gl_PointSize = shd_builder_->glsl_vertex_source_.find("gl_PointSize") !=
530 std::string::npos;
531 msl_iface.uses_gpu_layer = bool(info->builtins_ & BuiltinBits::LAYER);
532 msl_iface.uses_gpu_viewport_index = bool(info->builtins_ & BuiltinBits::VIEWPORT_INDEX);
533
535 {
536 std::smatch gl_special_cases;
537 msl_iface.uses_gl_PointCoord = bool(info->builtins_ & BuiltinBits::POINT_COORD) ||
538 shd_builder_->glsl_fragment_source_.find("gl_PointCoord") !=
539 std::string::npos;
540 msl_iface.uses_barycentrics = bool(info->builtins_ & BuiltinBits::BARYCENTRIC_COORD);
541 msl_iface.uses_gl_FrontFacing = bool(info->builtins_ & BuiltinBits::FRONT_FACING) ||
542 shd_builder_->glsl_fragment_source_.find("gl_FrontFacing") !=
543 std::string::npos;
544 msl_iface.uses_gl_PrimitiveID = bool(info->builtins_ & BuiltinBits::PRIMITIVE_ID) ||
545 shd_builder_->glsl_fragment_source_.find("gl_PrimitiveID") !=
546 std::string::npos;
547
548 /* NOTE(Metal): If FragColor is not used, then we treat the first fragment output attachment
549 * as the primary output. */
550 msl_iface.uses_gl_FragColor = shd_builder_->glsl_fragment_source_.find("gl_FragColor") !=
551 std::string::npos;
552
553 /* NOTE(Metal): FragDepth output mode specified in create-info 'DepthWrite depth_write_'.
554 * If parsing without create-info, manual extraction will be required. */
555 msl_iface.uses_gl_FragDepth = (info->depth_write_ != DepthWrite::UNCHANGED) &&
556 shd_builder_->glsl_fragment_source_.find("gl_FragDepth") !=
557 std::string::npos;
558
559 msl_iface.uses_gl_FragStencilRefARB = bool(info->builtins_ & BuiltinBits::STENCIL_REF);
560
561 msl_iface.depth_write = info->depth_write_;
562
563 /* Early fragment tests. */
564 msl_iface.uses_early_fragment_test = info->early_fragment_test_;
565 }
566
567 /* Extract gl_ClipDistances. */
568 extract_and_replace_clipping_distances(shd_builder_->glsl_vertex_source_, msl_iface);
569
570 /**** METAL Shader source generation. ****/
571 /* Setup `stringstream` for populating generated MSL shader vertex/frag shaders. */
572 std::stringstream ss_vertex;
573 std::stringstream ss_fragment;
574 ss_vertex << "#line " STRINGIFY(__LINE__) " \"" __FILE__ "\"" << std::endl;
575 ss_fragment << "#line " STRINGIFY(__LINE__) " \"" __FILE__ "\"" << std::endl;
576
577 /* Generate specialization constants. */
580
581 /* Generate compilation constants. */
584
585 /*** Generate VERTEX Stage ***/
586 /* Conditional defines. */
587 arg_buf_samplers_vert_ = msl_iface.use_argument_buffer_for_samplers() ?
588 msl_iface.max_sampler_index_for_stage(ShaderStage::VERTEX) + 1 :
589 0;
590
591 /* Inject common Metal header. */
592 ss_vertex << msl_iface.msl_patch_default_get() << std::endl << std::endl;
593
594 /* Generate additional shader interface struct members from create-info. */
595 for (const StageInterfaceInfo *iface : info->vertex_out_interfaces_) {
596
597 /* Only generate struct for ones with instance names */
598 if (!iface->instance_name.is_empty()) {
599 ss_vertex << "struct " << iface->name << " {" << std::endl;
600 for (const StageInterfaceInfo::InOut &inout : iface->inouts) {
601 ss_vertex << to_string(inout.type) << " " << inout.name << " "
602 << to_string_msl(inout.interp) << ";" << std::endl;
603 }
604 ss_vertex << "};" << std::endl;
605 }
606 }
607
608 /* Wrap entire GLSL source inside class to create
609 * a scope within the class to enable use of global variables.
610 * e.g. global access to attributes, uniforms, UBOs, textures etc; */
611 ss_vertex << "class " << get_stage_class_name(ShaderStage::VERTEX) << " {" << std::endl;
612 ss_vertex << "public:" << std::endl;
613
614 /* Generate additional shader interface struct members from create-info. */
615 for (const StageInterfaceInfo *iface : info->vertex_out_interfaces_) {
616
617 bool is_inside_struct = false;
618 if (!iface->instance_name.is_empty()) {
619 /* If shader stage interface has an instance name, then it
620 * is using a struct format and as such we only need a local
621 * class member for the struct, not each element. */
622 ss_vertex << iface->name << " " << iface->instance_name << ";" << std::endl;
623 is_inside_struct = true;
624 }
625
626 /* Generate local variables, populate elems for vertex out struct gen. */
627 for (const StageInterfaceInfo::InOut &inout : iface->inouts) {
628
629 /* Only output individual elements if they are not part of an interface struct instance. */
630 if (!is_inside_struct) {
631 ss_vertex << to_string(inout.type) << " " << inout.name << ";" << std::endl;
632 }
633
634 const char *arraystart = strchr(inout.name.c_str(), '[');
635 bool is_array = (arraystart != nullptr);
636 int array_len = (is_array) ? std::stoi(std::regex_replace(
637 arraystart, remove_non_numeric_characters, "")) :
638 0;
639
640 /* Remove array from string name. */
641 std::string out_name = inout.name.c_str();
642 std::size_t pos = out_name.find('[');
643 if (is_array && pos != std::string::npos) {
644 out_name.resize(pos);
645 }
646
647 /* Add to vertex-output interface. */
648 msl_iface.vertex_output_varyings.append(
649 {to_string(inout.type),
650 out_name.c_str(),
651 ((is_inside_struct) ? iface->instance_name.c_str() : ""),
652 to_string(inout.interp),
653 is_array,
654 array_len});
655
656 /* Add to fragment-input interface. */
657 msl_iface.fragment_input_varyings.append(
658 {to_string(inout.type),
659 out_name.c_str(),
660 ((is_inside_struct) ? iface->instance_name.c_str() : ""),
661 to_string(inout.interp),
662 is_array,
663 array_len});
664 }
665 }
666
668 /* Generate VertexIn struct. */
669 ss_vertex << msl_iface.generate_msl_vertex_in_struct();
670 /* Generate Uniform data structs. */
671 ss_vertex << msl_iface.generate_msl_uniform_structs(ShaderStage::VERTEX);
672
673 /* Conditionally use global GL variables. */
674 if (msl_iface.uses_gl_Position) {
675 ss_vertex << "float4 gl_Position;" << std::endl;
676 }
677 if (msl_iface.uses_gl_PointSize) {
678 ss_vertex << "float gl_PointSize = 1.0;" << std::endl;
679 }
680 if (msl_iface.uses_gl_VertexID) {
681 ss_vertex << "int gl_VertexID;" << std::endl;
682 }
683 if (msl_iface.uses_gl_InstanceID) {
684 ss_vertex << "int gl_InstanceID;" << std::endl;
685 }
686 if (msl_iface.uses_gl_BaseInstanceARB) {
687 ss_vertex << "int gl_BaseInstanceARB;" << std::endl;
688 }
689 for (const int cd : IndexRange(msl_iface.clip_distances.size())) {
690 ss_vertex << "float gl_ClipDistance_" << cd << ";" << std::endl;
691 }
692
693 /* Render target array index if using multilayered rendering. */
694 if (msl_iface.uses_gpu_layer) {
695 ss_vertex << "int gpu_Layer = 0;" << std::endl;
696 }
697 if (msl_iface.uses_gpu_viewport_index) {
698 ss_vertex << "int gpu_ViewportIndex = 0;" << std::endl;
699 }
700
701 /* Add Texture members.
702 * These members pack both a texture and a sampler into a single
703 * struct, as both are needed within texture functions.
704 * e.g. `_mtl_sampler_2d<float, access::read>`
705 * The exact typename is generated inside `get_msl_typestring_wrapper()`. */
706 for (const MSLTextureResource &tex : msl_iface.texture_samplers) {
707 if (bool(tex.stage & ShaderStage::VERTEX)) {
708 ss_vertex << "\tthread " << tex.get_msl_typestring_wrapper(false) << ";" << std::endl;
709 }
710 }
711 ss_vertex << std::endl;
712
713 /* Inject main GLSL source into output stream. */
714 ss_vertex << shd_builder_->glsl_vertex_source_ << std::endl;
715 ss_vertex << "#line " STRINGIFY(__LINE__) " \"" __FILE__ "\"" << std::endl;
716
717 /* Generate VertexOut and TransformFeedbackOutput structs. */
718 ss_vertex << msl_iface.generate_msl_vertex_out_struct(ShaderStage::VERTEX);
719
720 /* Class Closing Bracket to end shader global scope. */
721 ss_vertex << "};" << std::endl;
722
723 /* Generate Vertex shader entry-point function containing resource bindings. */
724 ss_vertex << msl_iface.generate_msl_vertex_entry_stub();
725
726 /*** Generate FRAGMENT Stage. ***/
727 {
728
729 /* Conditional defines. */
730 arg_buf_samplers_frag_ = msl_iface.use_argument_buffer_for_samplers() ?
731 msl_iface.max_sampler_index_for_stage(ShaderStage::FRAGMENT) + 1 :
732 0;
733
734 /* Inject common Metal header. */
735 ss_fragment << msl_iface.msl_patch_default_get() << std::endl << std::endl;
736
737 /* Generate additional shader interface struct members from create-info. */
738 for (const StageInterfaceInfo *iface : info->vertex_out_interfaces_) {
739
740 /* Only generate struct for ones with instance names. */
741 if (!iface->instance_name.is_empty()) {
742 ss_fragment << "struct " << iface->name << " {" << std::endl;
743 for (const StageInterfaceInfo::InOut &inout : iface->inouts) {
744 ss_fragment << to_string(inout.type) << " " << inout.name << ""
745 << to_string_msl(inout.interp) << ";" << std::endl;
746 }
747 ss_fragment << "};" << std::endl;
748 }
749 }
750
751 /* Wrap entire GLSL source inside class to create
752 * a scope within the class to enable use of global variables. */
753 ss_fragment << "class " << get_stage_class_name(ShaderStage::FRAGMENT) << " {" << std::endl;
754 ss_fragment << "public:" << std::endl;
755
756 /* In/out interface values */
757 /* Generate additional shader interface struct members from create-info. */
758 for (const StageInterfaceInfo *iface : info->vertex_out_interfaces_) {
759 bool is_inside_struct = false;
760 if (!iface->instance_name.is_empty()) {
761 /* Struct local variable. */
762 ss_fragment << iface->name << " " << iface->instance_name << ";" << std::endl;
763 is_inside_struct = true;
764 }
765
766 /* Generate local variables, populate elems for vertex out struct gen. */
767 for (const StageInterfaceInfo::InOut &inout : iface->inouts) {
768 /* Only output individual elements if they are not part of an interface struct instance.
769 */
770 if (!is_inside_struct) {
771 ss_fragment << to_string(inout.type) << " " << inout.name << ";" << std::endl;
772 }
773 }
774 }
775
776 /* Generate global structs */
777 ss_fragment << msl_iface.generate_msl_vertex_out_struct(ShaderStage::FRAGMENT);
778 if (msl_iface.fragment_tile_inputs.is_empty() == false) {
779 ss_fragment << msl_iface.generate_msl_fragment_struct(true);
780 }
781 ss_fragment << msl_iface.generate_msl_fragment_struct(false);
782 ss_fragment << msl_iface.generate_msl_uniform_structs(ShaderStage::FRAGMENT);
783
785 /* gl_FragCoord will always be assigned to the output position from vertex shading. */
786 ss_fragment << "float4 gl_FragCoord;" << std::endl;
787 if (msl_iface.uses_gl_FragColor) {
788 ss_fragment << "float4 gl_FragColor;" << std::endl;
789 }
790 if (msl_iface.uses_gl_FragDepth) {
791 ss_fragment << "float gl_FragDepth;" << std::endl;
792 }
793 if (msl_iface.uses_gl_FragStencilRefARB) {
794 ss_fragment << "int gl_FragStencilRefARB;" << std::endl;
795 }
796 if (msl_iface.uses_gl_PointCoord) {
797 ss_fragment << "float2 gl_PointCoord;" << std::endl;
798 }
799 if (msl_iface.uses_gl_FrontFacing) {
800 ss_fragment << "bool gl_FrontFacing;" << std::endl;
801 }
802 if (msl_iface.uses_gl_PrimitiveID) {
803 ss_fragment << "uint gl_PrimitiveID;" << std::endl;
804 }
805
806 /* Global barycentrics. */
807 if (msl_iface.uses_barycentrics) {
808 ss_fragment << "vec3 gpu_BaryCoord;\n";
809 }
810
811 /* Render target array index and viewport array index passed from vertex shader. */
812 if (msl_iface.uses_gpu_layer) {
813 ss_fragment << "int gpu_Layer = 0;" << std::endl;
814 }
815 if (msl_iface.uses_gpu_viewport_index) {
816 ss_fragment << "int gpu_ViewportIndex = 0;" << std::endl;
817 }
818
819 /* Add Texture members. */
820 for (const MSLTextureResource &tex : msl_iface.texture_samplers) {
821 if (bool(tex.stage & ShaderStage::FRAGMENT)) {
822 ss_fragment << "\tthread " << tex.get_msl_typestring_wrapper(false) << ";" << std::endl;
823 }
824 }
825
826 /* Inject Main GLSL Fragment Source into output stream. */
827 ss_fragment << shd_builder_->glsl_fragment_source_ << std::endl;
828 ss_fragment << "#line " STRINGIFY(__LINE__) " \"" __FILE__ "\"" << std::endl;
829
830 /* Class Closing Bracket to end shader global scope. */
831 ss_fragment << "};" << std::endl;
832
833 /* Generate Fragment entry-point function. */
834 ss_fragment << msl_iface.generate_msl_fragment_entry_stub();
835 }
836
837 /* DEBUG: Export source to file for manual verification. */
838#if MTL_SHADER_DEBUG_EXPORT_SOURCE
839 NSFileManager *sharedFM = [NSFileManager defaultManager];
840 NSURL *app_bundle_url = [[NSBundle mainBundle] bundleURL];
841 NSURL *shader_dir = [[app_bundle_url URLByDeletingLastPathComponent]
842 URLByAppendingPathComponent:@"Shaders/"
843 isDirectory:YES];
844 [sharedFM createDirectoryAtURL:shader_dir
845 withIntermediateDirectories:YES
846 attributes:nil
847 error:nil];
848 const char *path_cstr = [shader_dir fileSystemRepresentation];
849
850 std::ofstream vertex_fs;
851 vertex_fs.open(
852 (std::string(path_cstr) + "/" + std::string(this->name) + "_GeneratedVertexShader.msl")
853 .c_str());
854 vertex_fs << ss_vertex.str();
855 vertex_fs.close();
856
857 std::ofstream fragment_fs;
858 fragment_fs.open(
859 (std::string(path_cstr) + "/" + std::string(this->name) + "_GeneratedFragmentShader.msl")
860 .c_str());
861 fragment_fs << ss_fragment.str();
862 fragment_fs.close();
863
865 "Vertex Shader Saved to: %s\n",
866 (std::string(path_cstr) + std::string(this->name) + "_GeneratedFragmentShader.msl").c_str());
867#endif
868
869 /* Set MSL source NSString's. Required by Metal API. */
870 NSString *msl_final_vert = [NSString stringWithUTF8String:ss_vertex.str().c_str()];
871 NSString *msl_final_frag = [NSString stringWithUTF8String:ss_fragment.str().c_str()];
872
873 this->shader_source_from_msl(msl_final_vert, msl_final_frag);
874
875#ifndef NDEBUG
876 /* In debug mode, we inject the name of the shader into the entry-point function
877 * name, as these are what show up in the Xcode GPU debugger. */
879 [[NSString stringWithFormat:@"vertex_function_entry_%s", this->name] retain]);
881 [[NSString stringWithFormat:@"fragment_function_entry_%s", this->name] retain]);
882#else
883 this->set_vertex_function_name(@"vertex_function_entry");
884 this->set_fragment_function_name(@"fragment_function_entry");
885#endif
886
887 /* Bake shader interface. */
888 this->set_interface(msl_iface.bake_shader_interface(this->name, info));
889
890 /* Update other shader properties. */
891 uses_gpu_layer = msl_iface.uses_gpu_layer;
892 uses_gpu_viewport_index = msl_iface.uses_gpu_viewport_index;
893
894 /* Successfully completed GLSL to MSL translation. */
895 return true;
896}
897
898bool MTLShader::generate_msl_from_glsl_compute(const shader::ShaderCreateInfo *info)
899{
900 /* #MSLGeneratorInterface is a class populated to describe all parameters, resources, bindings
901 * and features used by the source GLSL shader. This information is then used to generate the
902 * appropriate Metal entry points and perform any required source translation. */
903 MSLGeneratorInterface msl_iface(*this);
904 BLI_assert(shd_builder_ != nullptr);
905
906 /* Populate #MSLGeneratorInterface from Create-Info.
907 * NOTE: this is a separate path as #MSLGeneratorInterface can also be manually populated
908 * from parsing, if support for shaders without create-info is required. */
909 msl_iface.prepare_from_createinfo(info);
910
911 /* Verify Source sizes are greater than zero. */
912 BLI_assert(shd_builder_->glsl_compute_source_.empty() == false);
913
914 /**** Extract usage of GL globals. ****/
915 /* NOTE(METAL): Currently still performing fallback string scan, as info->builtins_ does
916 * not always contain the usage flag. This can be removed once all appropriate create-info's
917 * have been updated. In some cases, this may incur a false positive if access is guarded
918 * behind a macro. Though in these cases, unused code paths and parameters will be
919 * optimized out by the Metal shader compiler. */
920
921 /* gl_GlobalInvocationID. */
922 msl_iface.uses_gl_GlobalInvocationID =
923 bool(info->builtins_ & BuiltinBits::GLOBAL_INVOCATION_ID) ||
924 shd_builder_->glsl_compute_source_.find("gl_GlobalInvocationID") != std::string::npos;
925 /* gl_WorkGroupSize. */
926 msl_iface.uses_gl_WorkGroupSize = bool(info->builtins_ & BuiltinBits::WORK_GROUP_SIZE) ||
927 shd_builder_->glsl_compute_source_.find("gl_WorkGroupSize") !=
928 std::string::npos;
929 /* gl_WorkGroupID. */
930 msl_iface.uses_gl_WorkGroupID = bool(info->builtins_ & BuiltinBits::WORK_GROUP_ID) ||
931 shd_builder_->glsl_compute_source_.find("gl_WorkGroupID") !=
932 std::string::npos;
933 /* gl_NumWorkGroups. */
934 msl_iface.uses_gl_NumWorkGroups = bool(info->builtins_ & BuiltinBits::NUM_WORK_GROUP) ||
935 shd_builder_->glsl_compute_source_.find("gl_NumWorkGroups") !=
936 std::string::npos;
937 /* gl_LocalInvocationIndex. */
938 msl_iface.uses_gl_LocalInvocationIndex =
939 bool(info->builtins_ & BuiltinBits::LOCAL_INVOCATION_INDEX) ||
940 shd_builder_->glsl_compute_source_.find("gl_LocalInvocationIndex") != std::string::npos;
941 /* gl_LocalInvocationID. */
942 msl_iface.uses_gl_LocalInvocationID = bool(info->builtins_ & BuiltinBits::LOCAL_INVOCATION_ID) ||
943 shd_builder_->glsl_compute_source_.find(
944 "gl_LocalInvocationID") != std::string::npos;
945
947 std::stringstream ss_compute;
948 ss_compute << "#line " STRINGIFY(__LINE__) " \"" __FILE__ "\"" << std::endl;
949
950 ss_compute << "#define GPU_ARB_shader_draw_parameters 1\n";
951 ss_compute << "#define GPU_ARB_clip_control 1\n";
952
955
956 /* Conditional defines. */
957 arg_buf_samplers_comp_ = msl_iface.use_argument_buffer_for_samplers() ?
958 msl_iface.max_sampler_index_for_stage(ShaderStage::COMPUTE) + 1 :
959 0;
960
961 /* Inject common Metal header. */
962 ss_compute << msl_iface.msl_patch_default_get() << std::endl << std::endl;
963
964 /* Wrap entire GLSL source inside class to create
965 * a scope within the class to enable use of global variables.
966 * e.g. global access to attributes, uniforms, UBOs, textures etc; */
967 ss_compute << "class " << get_stage_class_name(ShaderStage::COMPUTE) << " {" << std::endl;
968 ss_compute << "public:" << std::endl;
969
970 /* Generate Uniform data structs. */
971 ss_compute << msl_iface.generate_msl_uniform_structs(ShaderStage::VERTEX);
972
973 /* Add Texture members.
974 * These members pack both a texture and a sampler into a single
975 * struct, as both are needed within texture functions.
976 * e.g. `_mtl_sampler_2d<float, access::read>`
977 * The exact typename is generated inside `get_msl_typestring_wrapper()`. */
978 for (const MSLTextureResource &tex : msl_iface.texture_samplers) {
979 if (bool(tex.stage & ShaderStage::COMPUTE)) {
980 ss_compute << "\tthread " << tex.get_msl_typestring_wrapper(false) << ";" << std::endl;
981 }
982 }
983 ss_compute << std::endl;
984
985 /* Conditionally use global GL variables. */
986 if (msl_iface.uses_gl_GlobalInvocationID) {
987 ss_compute << "uint3 gl_GlobalInvocationID;" << std::endl;
988 }
989 if (msl_iface.uses_gl_WorkGroupID) {
990 ss_compute << "uint3 gl_WorkGroupID;" << std::endl;
991 }
992 if (msl_iface.uses_gl_NumWorkGroups) {
993 ss_compute << "uint3 gl_NumWorkGroups;" << std::endl;
994 }
995 if (msl_iface.uses_gl_LocalInvocationIndex) {
996 ss_compute << "uint gl_LocalInvocationIndex;" << std::endl;
997 }
998 if (msl_iface.uses_gl_LocalInvocationID) {
999 ss_compute << "uint3 gl_LocalInvocationID;" << std::endl;
1000 }
1001
1002 /* Inject main GLSL source into output stream. */
1003 ss_compute << shd_builder_->glsl_compute_source_ << std::endl;
1004 ss_compute << "#line " STRINGIFY(__LINE__) " \"" __FILE__ "\"" << std::endl;
1005
1006 /* Compute constructor for Shared memory blocks, as we must pass
1007 * local references from entry-point function scope into the class
1008 * instantiation. */
1009 ss_compute << get_stage_class_name(ShaderStage::COMPUTE) << "( ";
1010 if (!info->shared_variables_.is_empty()) {
1011 shared_variable_args(*info, ss_compute);
1012 }
1013 else {
1014 ss_compute << "MSL_SHARED_VARS_ARGS";
1015 }
1016 ss_compute << ")";
1017 if (!info->shared_variables_.is_empty()) {
1018 shared_variable_assign(*info, ss_compute);
1019 }
1020 else {
1021 ss_compute << " MSL_SHARED_VARS_ASSIGN ";
1022 }
1023 ss_compute << "{}\n";
1024
1025 /* Class Closing Bracket to end shader global scope. */
1026 ss_compute << "};" << std::endl;
1027
1028 /* Generate Vertex shader entry-point function containing resource bindings. */
1029 ss_compute << msl_iface.generate_msl_compute_entry_stub(*info);
1030
1031#ifndef NDEBUG
1032 /* In debug mode, we inject the name of the shader into the entry-point function
1033 * name, as these are what show up in the Xcode GPU debugger. */
1035 [[NSString stringWithFormat:@"compute_function_entry_%s", this->name] retain]);
1036#else
1037 this->set_compute_function_name(@"compute_function_entry");
1038#endif
1039
1040 /* DEBUG: Export source to file for manual verification. */
1041#if MTL_SHADER_DEBUG_EXPORT_SOURCE
1042 NSFileManager *sharedFM = [NSFileManager defaultManager];
1043 NSURL *app_bundle_url = [[NSBundle mainBundle] bundleURL];
1044 NSURL *shader_dir = [[app_bundle_url URLByDeletingLastPathComponent]
1045 URLByAppendingPathComponent:@"Shaders/"
1046 isDirectory:YES];
1047 [sharedFM createDirectoryAtURL:shader_dir
1048 withIntermediateDirectories:YES
1049 attributes:nil
1050 error:nil];
1051 const char *path_cstr = [shader_dir fileSystemRepresentation];
1052
1053 std::ofstream compute_fs;
1054 compute_fs.open(
1055 (std::string(path_cstr) + "/" + std::string(this->name) + "_GeneratedComputeShader.msl")
1056 .c_str());
1057 compute_fs << ss_compute.str();
1058 compute_fs.close();
1059
1061 "Compute Shader Saved to: %s\n",
1062 (std::string(path_cstr) + std::string(this->name) + "_GeneratedComputeShader.msl").c_str());
1063#endif
1064
1065 NSString *msl_final_compute = [NSString stringWithUTF8String:ss_compute.str().c_str()];
1066 this->shader_compute_source_from_msl(msl_final_compute);
1067
1068 /* Bake shader interface. */
1069 this->set_interface(msl_iface.bake_shader_interface(this->name, info));
1070
1071 /* Compute dims. */
1072 this->compute_pso_common_state_.set_compute_workgroup_size(
1076
1077 /* Successfully completed GLSL to MSL translation. */
1078 return true;
1079}
1080
1081constexpr size_t const_strlen(const char *str)
1082{
1083 return (*str == '\0') ? 0 : const_strlen(str + 1) + 1;
1084}
1085
1087{
1089 create_info_ = info;
1090
1092 for (const shader::ShaderCreateInfo::PushConst &push_constant : create_info_->push_constants_) {
1093 MSLUniform uniform(push_constant.type,
1094 push_constant.name,
1095 bool(push_constant.array_size > 1),
1096 push_constant.array_size);
1097 uniforms.append(uniform);
1098 }
1099
1101 for (const auto &constant : create_info_->specialization_constants_) {
1102 constants.append(MSLConstant(constant.type, constant.name));
1103 }
1104
1105 /* Prepare textures and uniform blocks.
1106 * Perform across both resource categories and extract both
1107 * texture samplers and image types. */
1108
1109 /* NOTE: Metal requires Samplers and images to share slots. We will re-map these.
1110 * If `auto_resource_location_` is not used, then slot collision could occur and
1111 * this should be resolved in the original create-info.
1112 * UBOs and SSBOs also share the same bind table. */
1113 int texture_slot_id = 0;
1114 int ubo_buffer_slot_id_ = 0;
1115 int storage_buffer_slot_id_ = 0;
1116
1117 uint max_storage_buffer_location = 0;
1118
1120
1121 /* Determine max sampler slot for image resource offset, when not using auto resource location,
1122 * as image resources cannot overlap sampler ranges. */
1123 int max_sampler_slot = 0;
1124 if (!create_info_->auto_resource_location_) {
1125 for (const ShaderCreateInfo::Resource &res : all_resources) {
1127 max_sampler_slot = max_ii(res.slot, max_sampler_slot);
1128 }
1129 }
1130 }
1131
1132 for (const ShaderCreateInfo::Resource &res : all_resources) {
1133 /* TODO(Metal): Consider adding stage flags to textures in create info. */
1134 /* Handle sampler types. */
1135 switch (res.bind_type) {
1137
1138 /* Samplers to have access::sample by default. */
1140 /* TextureBuffers must have read/write/read-write access pattern. */
1141 if (res.sampler.type == ImageType::FloatBuffer ||
1142 res.sampler.type == ImageType::IntBuffer || res.sampler.type == ImageType::UintBuffer)
1143 {
1145 }
1146
1147 MSLTextureResource msl_tex;
1148 msl_tex.stage = ShaderStage::ANY;
1149 msl_tex.type = res.sampler.type;
1150 msl_tex.name = res.sampler.name;
1151 msl_tex.access = access;
1152 msl_tex.slot = texture_slot_id++;
1153 msl_tex.location = (create_info_->auto_resource_location_) ? msl_tex.slot : res.slot;
1154 msl_tex.is_texture_sampler = true;
1156
1157 texture_samplers.append(msl_tex);
1159 } break;
1160
1162 /* Flatten qualifier flags into final access state. */
1164 if ((res.image.qualifiers & Qualifier::read_write) == Qualifier::read_write) {
1166 }
1167 else if (bool(res.image.qualifiers & Qualifier::write)) {
1169 }
1170 else {
1172 }
1173
1174 /* Writeable image targets only assigned to Fragment and compute shaders. */
1175 MSLTextureResource msl_image;
1177 msl_image.type = res.image.type;
1178 msl_image.name = res.image.name;
1179 msl_image.access = access;
1180 msl_image.slot = texture_slot_id++;
1181 msl_image.location = (create_info_->auto_resource_location_) ? msl_image.slot : res.slot;
1182 msl_image.is_texture_sampler = false;
1184
1185 texture_samplers.append(msl_image);
1187 } break;
1188
1190 MSLBufferBlock ubo;
1191 BLI_assert(res.uniformbuf.type_name.is_empty() == false);
1192 BLI_assert(res.uniformbuf.name.is_empty() == false);
1193 int64_t array_offset = res.uniformbuf.name.find_first_of("[");
1194
1195 /* We maintain two bind indices. "Slot" refers to the storage index buffer(N) in which
1196 * we will bind the resource. "Location" refers to the explicit bind index specified
1197 * in ShaderCreateInfo.
1198 * NOTE: ubo.slot is offset by one, as first UBO slot is reserved for push constant data.
1199 */
1200 ubo.slot = 1 + (ubo_buffer_slot_id_++);
1201 ubo.location = (create_info_->auto_resource_location_) ? ubo.slot : res.slot;
1202
1204
1206 ubo.type_name = res.uniformbuf.type_name;
1207 ubo.is_texture_buffer = false;
1208 ubo.is_array = (array_offset > -1);
1209 if (ubo.is_array) {
1210 /* If is array UBO, strip out array tag from name. */
1211 StringRef name_no_array = StringRef(res.uniformbuf.name.c_str(), array_offset);
1212 ubo.name = name_no_array;
1213 }
1214 else {
1215 ubo.name = res.uniformbuf.name;
1216 }
1217 ubo.stage = ShaderStage::ANY;
1218 uniform_blocks.append(ubo);
1219 } break;
1220
1222 MSLBufferBlock ssbo;
1223 BLI_assert(res.storagebuf.type_name.is_empty() == false);
1224 BLI_assert(res.storagebuf.name.is_empty() == false);
1225 int64_t array_offset = res.storagebuf.name.find_first_of("[");
1226
1227 /* We maintain two bind indices. "Slot" refers to the storage index buffer(N) in which
1228 * we will bind the resource. "Location" refers to the explicit bind index specified
1229 * in ShaderCreateInfo. */
1230 ssbo.slot = storage_buffer_slot_id_++;
1231 ssbo.location = (create_info_->auto_resource_location_) ? ssbo.slot : res.slot;
1232
1233 max_storage_buffer_location = max_uu(max_storage_buffer_location, ssbo.location);
1234
1236
1237 ssbo.qualifiers = res.storagebuf.qualifiers;
1238 ssbo.type_name = res.storagebuf.type_name;
1239 ssbo.is_texture_buffer = false;
1240 ssbo.is_array = (array_offset > -1);
1241 if (ssbo.is_array) {
1242 /* If is array UBO, strip out array tag from name. */
1243 StringRef name_no_array = StringRef(res.storagebuf.name.c_str(), array_offset);
1244 ssbo.name = name_no_array;
1245 }
1246 else {
1247 ssbo.name = res.storagebuf.name;
1248 }
1249 ssbo.stage = ShaderStage::ANY;
1250 storage_blocks.append(ssbo);
1251 } break;
1252 }
1253 }
1254
1255 /* For texture atomic fallback support, bind texture source buffers and data buffer as storage
1256 * blocks. */
1257 if (!MTLBackend::get_capabilities().supports_texture_atomics) {
1258 uint atomic_fallback_buffer_count = 0;
1260 if (ELEM(tex.type,
1261 ImageType::AtomicUint2D,
1262 ImageType::AtomicUint2DArray,
1263 ImageType::AtomicUint3D,
1264 ImageType::AtomicInt2D,
1265 ImageType::AtomicInt2DArray,
1266 ImageType::AtomicInt3D))
1267 {
1268 /* Add storage-buffer bind-point. */
1269 MSLBufferBlock ssbo;
1270
1271 /* We maintain two bind indices. "Slot" refers to the storage index buffer(N) in which
1272 * we will bind the resource. "Location" refers to the explicit bind index specified
1273 * in ShaderCreateInfo.
1274 * NOTE: For texture buffers, we will accumulate these after all other storage buffers.
1275 */
1276 ssbo.slot = storage_buffer_slot_id_++;
1277 ssbo.location = max_storage_buffer_location + 1 + atomic_fallback_buffer_count;
1278
1279 /* Flag atomic fallback buffer id and location.
1280 * ID is used to determine order for accessing parameters, while
1281 * location is used to extract the explicit bind point for the buffer. */
1283
1285
1286 /* Qualifier should be read write and type is either uint or int. */
1289 ssbo.is_array = false;
1290 ssbo.name = tex.name + "_storagebuf";
1291 ssbo.stage = ShaderStage::ANY;
1292 ssbo.is_texture_buffer = true;
1293 storage_blocks.append(ssbo);
1294
1295 /* Add uniform for metadata. */
1296 MSLUniform uniform(shader::Type::int4_t, tex.name + "_metadata", false, 1);
1297 uniforms.append(uniform);
1298
1299 atomic_fallback_buffer_count++;
1300 }
1301 }
1302 }
1303
1304 /* Assign maximum buffer. */
1305 max_buffer_slot = storage_buffer_slot_id_ + ubo_buffer_slot_id_ + 1;
1306
1308 bool all_attr_location_assigned = true;
1309 for (const ShaderCreateInfo::VertIn &attr : info->vertex_inputs_) {
1310
1311 /* Validate input. */
1312 BLI_assert(attr.name.is_empty() == false);
1313
1314 /* NOTE(Metal): Input attributes may not have a location specified.
1315 * unset locations are resolved during: `resolve_input_attribute_locations`. */
1316 MSLVertexInputAttribute msl_attr;
1317 bool attr_location_assigned = (attr.index >= 0);
1318 all_attr_location_assigned = all_attr_location_assigned && attr_location_assigned;
1319 msl_attr.layout_location = attr_location_assigned ? attr.index : -1;
1320 msl_attr.type = attr.type;
1321 msl_attr.name = attr.name;
1322 vertex_input_attributes.append(msl_attr);
1323 }
1324
1325 /* Ensure all attributes are assigned a location. */
1326 if (!all_attr_location_assigned) {
1328 }
1329
1331 for (const shader::ShaderCreateInfo::FragOut &frag_out : create_info_->fragment_outputs_) {
1332 /* Validate input. */
1333 BLI_assert(frag_out.name.is_empty() == false);
1334 BLI_assert(frag_out.index >= 0);
1335
1336 /* Populate MSLGenerator attribute. */
1337 MSLFragmentOutputAttribute mtl_frag_out;
1338 mtl_frag_out.layout_location = frag_out.index;
1339 mtl_frag_out.layout_index = (frag_out.blend != DualBlend::NONE) ?
1340 ((frag_out.blend == DualBlend::SRC_0) ? 0 : 1) :
1341 -1;
1342 mtl_frag_out.type = frag_out.type;
1343 mtl_frag_out.name = frag_out.name;
1344 mtl_frag_out.raster_order_group = frag_out.raster_order_group;
1345
1346 fragment_outputs.append(mtl_frag_out);
1347 }
1348
1349 /* Fragment tile inputs. */
1350 for (const shader::ShaderCreateInfo::SubpassIn &frag_tile_in : create_info_->subpass_inputs_) {
1351
1352 /* Validate input. */
1353 BLI_assert(frag_tile_in.name.is_empty() == false);
1354 BLI_assert(frag_tile_in.index >= 0);
1355
1356 /* Populate MSLGenerator attribute. */
1358 mtl_frag_in.layout_location = frag_tile_in.index;
1359 mtl_frag_in.layout_index = -1;
1360 mtl_frag_in.type = frag_tile_in.type;
1361 mtl_frag_in.name = frag_tile_in.name;
1362 mtl_frag_in.raster_order_group = frag_tile_in.raster_order_group;
1363 mtl_frag_in.is_layered_input = ELEM(frag_tile_in.img_type,
1364 ImageType::Uint2DArray,
1365 ImageType::Int2DArray,
1366 ImageType::Float2DArray);
1367
1368 fragment_tile_inputs.append(mtl_frag_in);
1369
1370 /* If we do not support native tile inputs, generate an image-binding per input. */
1371 if (!MTLBackend::capabilities.supports_native_tile_inputs) {
1372 /* Generate texture binding resource. */
1373 MSLTextureResource msl_image;
1374 msl_image.stage = ShaderStage::FRAGMENT;
1375 msl_image.type = frag_tile_in.img_type;
1376 msl_image.name = frag_tile_in.name + "_subpass_img";
1378 msl_image.slot = texture_slot_id++;
1379 /* WATCH: We don't have a great place to generate the image bindings.
1380 * So we will use the subpass binding index and check if it collides with an existing
1381 * binding. */
1382 msl_image.location = frag_tile_in.index;
1383 msl_image.is_texture_sampler = false;
1386
1387 /* Check existing samplers. */
1388 for (const auto &tex : texture_samplers) {
1389 UNUSED_VARS_NDEBUG(tex);
1390 BLI_assert(tex.location != msl_image.location);
1391 }
1392
1393 texture_samplers.append(msl_image);
1395 }
1396 }
1397}
1398
1400{
1401 /* We can only use argument buffers IF highest sampler index exceeds static limit of 16,
1402 * AND we can support more samplers with an argument buffer. */
1403 bool use_argument_buffer = (texture_samplers.size() >= 15 || max_tex_bind_index >= 14) &&
1404 GPU_max_samplers() > 15;
1405
1406#ifndef NDEBUG
1407 /* Due to explicit bind location support, we may be below the sampler limit, but forced to offset
1408 * bindings due to the range being high. Introduce debug check here to issue warning. In these
1409 * cases, if explicit bind location support is not required, best to use auto_resource_location
1410 * to optimize bind point packing. */
1411 if (use_argument_buffer && texture_samplers.size() < 15) {
1413 "Compiled Shader '%s' is falling back to bindless via argument buffers due to having a "
1414 "texture sampler of Index: %u Which exceeds the limit of 15+1. However shader only uses "
1415 "%d textures. Consider optimising bind points with .auto_resource_location(true).",
1416 parent_shader_.name_get().c_str(),
1418 (int)texture_samplers.size());
1419 }
1420#endif
1421
1422 return use_argument_buffer;
1423}
1424
1426{
1427 /* NOTE: Sampler bindings and argument buffer shared across stages,
1428 * in case stages share texture/sampler bindings. */
1429 return texture_samplers.size();
1430}
1431
1433{
1434 /* NOTE: Sampler bindings and argument buffer shared across stages,
1435 * in case stages share texture/sampler bindings. */
1436 return max_tex_bind_index;
1437}
1438
1440{
1441 /* NOTE: Shader stage must be a singular index. Compound shader masks are not valid for this
1442 * function. */
1444 stage == ShaderStage::COMPUTE);
1447 }
1448
1449 /* Sampler argument buffer to follow UBOs and PushConstantBlock. */
1452}
1453
1455{
1456 static const char *shader_stage_inst_name = get_shader_stage_instance_name(ShaderStage::VERTEX);
1457
1458 std::stringstream out;
1459 out << std::endl << "/*** AUTO-GENERATED MSL VERETX SHADER STUB. ***/" << std::endl;
1460
1461 /* Un-define texture defines from main source - avoid conflict with MSL texture. */
1462 out << "#undef texture" << std::endl;
1463 out << "#undef textureLod" << std::endl;
1464
1465 /* Disable special case for booleans being treated as ints in GLSL. */
1466 out << "#undef bool" << std::endl;
1467
1468 /* Un-define uniform mappings to avoid name collisions. */
1470
1471 /* Generate function entry point signature w/ resource bindings and inputs. */
1472 out << "vertex ";
1473 out << get_stage_class_name(ShaderStage::VERTEX) << "::VertexOut ";
1474#ifndef NDEBUG
1475 out << "vertex_function_entry_" << parent_shader_.name_get() << "(\n\t";
1476#else
1477 out << "vertex_function_entry(\n\t";
1478#endif
1479
1481 out << ") {" << std::endl << std::endl;
1482 out << "\t" << get_stage_class_name(ShaderStage::VERTEX) << "::VertexOut output;" << std::endl
1483 << "\t" << get_stage_class_name(ShaderStage::VERTEX) << " " << shader_stage_inst_name << ";"
1484 << std::endl;
1485
1486 /* Copy Vertex Globals. */
1487 if (this->uses_gl_VertexID) {
1488 out << shader_stage_inst_name << ".gl_VertexID = gl_VertexID;" << std::endl;
1489 }
1490 if (this->uses_gl_InstanceID) {
1491 out << shader_stage_inst_name << ".gl_InstanceID = gl_InstanceID-gl_BaseInstanceARB;"
1492 << std::endl;
1493 }
1494 if (this->uses_gl_BaseInstanceARB) {
1495 out << shader_stage_inst_name << ".gl_BaseInstanceARB = gl_BaseInstanceARB;" << std::endl;
1496 }
1497
1498 /* Copy vertex attributes into local variables. */
1500
1501 /* Populate Uniforms and uniform blocks. */
1505
1506 /* Execute original 'main' function within class scope. */
1507 out << "\t/* Execute Vertex main function */\t" << std::endl
1508 << "\t" << shader_stage_inst_name << ".main();" << std::endl
1509 << std::endl;
1510
1511 /* Populate Output values. */
1513
1514 /* Final point size,
1515 * This is only compiled if the `MTL_global_pointsize` is specified
1516 * as a function specialization in the PSO. This is restricted to
1517 * point primitive types. */
1518 out << "if(is_function_constant_defined(MTL_global_pointsize)){ output.pointsize = "
1519 "(MTL_global_pointsize > 0.0)?MTL_global_pointsize:output.pointsize; }"
1520 << std::endl;
1521 out << "\treturn output;" << std::endl;
1522 out << "}";
1523 return out.str();
1524}
1525
1527{
1528 static const char *shader_stage_inst_name = get_shader_stage_instance_name(
1530 std::stringstream out;
1531 out << std::endl << "/*** AUTO-GENERATED MSL FRAGMENT SHADER STUB. ***/" << std::endl;
1532
1533 /* Undefine texture defines from main source - avoid conflict with MSL texture. */
1534 out << "#undef texture" << std::endl;
1535 out << "#undef textureLod" << std::endl;
1536
1537 /* Disable special case for booleans being treated as integers in GLSL. */
1538 out << "#undef bool" << std::endl;
1539
1540 /* Undefine uniform mappings to avoid name collisions. */
1542
1543 /* Early fragment tests. */
1545 out << "[[early_fragment_tests]]" << std::endl;
1546 }
1547
1548 /* Generate function entry point signature w/ resource bindings and inputs. */
1549#ifndef NDEBUG
1551 << "::" FRAGMENT_OUT_STRUCT_NAME " fragment_function_entry_" << parent_shader_.name_get()
1552 << "(\n\t";
1553#else
1555 << "::" FRAGMENT_OUT_STRUCT_NAME " fragment_function_entry(\n\t";
1556#endif
1558 out << ") {" << std::endl << std::endl;
1560 << "::" FRAGMENT_OUT_STRUCT_NAME " output;" << std::endl
1561 << "\t" << get_stage_class_name(ShaderStage::FRAGMENT) << " " << shader_stage_inst_name
1562 << ";" << std::endl;
1563
1564 /* Copy Fragment Globals. */
1565 if (this->uses_gl_PointCoord) {
1566 out << shader_stage_inst_name << ".gl_PointCoord = gl_PointCoord;" << std::endl;
1567 }
1568 if (this->uses_gl_FrontFacing) {
1569 out << shader_stage_inst_name << ".gl_FrontFacing = gl_FrontFacing;" << std::endl;
1570 }
1571 if (this->uses_gl_PrimitiveID) {
1572 out << "fragment_shader_instance.gl_PrimitiveID = gl_PrimitiveID;" << std::endl;
1573 }
1574
1575 /* Copy vertex attributes into local variable.s */
1577
1578 /* Barycentrics. */
1579 if (this->uses_barycentrics) {
1580 out << shader_stage_inst_name << ".gpu_BaryCoord = mtl_barycentric_coord.xyz;" << std::endl;
1581 }
1582
1583 /* Populate Uniforms and uniform blocks. */
1587
1588 /* Populate fragment tile-in members. */
1589 if (this->fragment_tile_inputs.is_empty() == false) {
1591 }
1592
1593 /* Execute original 'main' function within class scope. */
1594 out << "\t/* Execute Fragment main function */\t" << std::endl
1595 << "\t" << shader_stage_inst_name << ".main();" << std::endl
1596 << std::endl;
1597
1598 /* Populate Output values. */
1600 out << " return output;" << std::endl << "}";
1601
1602 return out.str();
1603}
1604
1606 const shader::ShaderCreateInfo &info)
1607{
1608 static const char *shader_stage_inst_name = get_shader_stage_instance_name(ShaderStage::COMPUTE);
1609 std::stringstream out;
1610 out << std::endl << "/*** AUTO-GENERATED MSL COMPUTE SHADER STUB. ***/" << std::endl;
1611
1612 /* Un-define texture defines from main source - avoid conflict with MSL texture. */
1613 out << "#undef texture" << std::endl;
1614 out << "#undef textureLod" << std::endl;
1615
1616 /* Disable special case for booleans being treated as ints in GLSL. */
1617 out << "#undef bool" << std::endl;
1618
1619 /* Un-define uniform mappings to avoid name collisions. */
1621
1622 /* Generate function entry point signature w/ resource bindings and inputs. */
1623 out << "kernel void ";
1624#ifndef NDEBUG
1625 out << "compute_function_entry_" << parent_shader_.name_get() << "(\n\t";
1626#else
1627 out << "compute_function_entry(\n\t";
1628#endif
1629
1631 out << ") {" << std::endl << std::endl;
1632 if (!info.shared_variables_.is_empty()) {
1634 }
1635 else {
1636 out << "MSL_SHARED_VARS_DECLARE\n";
1637 }
1638
1639 out << "\t" << get_stage_class_name(ShaderStage::COMPUTE) << " " << shader_stage_inst_name;
1640 /* Shared vars should be either all be declared in shader (MSL_SHARED_VARS_* path) or all in
1641 * create infos (shared_variable_* path). */
1642 if (!info.shared_variables_.is_empty()) {
1644 }
1645 else {
1646 out << " MSL_SHARED_VARS_PASS ";
1647 }
1648 out << ";\n";
1649
1650 /* Copy global variables. */
1651 /* Entry point parameters for gl Globals. */
1652 if (this->uses_gl_GlobalInvocationID) {
1653 out << shader_stage_inst_name << ".gl_GlobalInvocationID = gl_GlobalInvocationID;"
1654 << std::endl;
1655 }
1656 if (this->uses_gl_WorkGroupID) {
1657 out << shader_stage_inst_name << ".gl_WorkGroupID = gl_WorkGroupID;" << std::endl;
1658 }
1659 if (this->uses_gl_NumWorkGroups) {
1660 out << shader_stage_inst_name << ".gl_NumWorkGroups = gl_NumWorkGroups;" << std::endl;
1661 }
1662 if (this->uses_gl_LocalInvocationIndex) {
1663 out << shader_stage_inst_name << ".gl_LocalInvocationIndex = gl_LocalInvocationIndex;"
1664 << std::endl;
1665 }
1666 if (this->uses_gl_LocalInvocationID) {
1667 out << shader_stage_inst_name << ".gl_LocalInvocationID = gl_LocalInvocationID;" << std::endl;
1668 }
1669
1670 /* Populate Uniforms and uniform blocks. */
1674
1675 /* Execute original 'main' function within class scope. */
1676 out << "\t/* Execute Compute main function */\t" << std::endl
1677 << "\t" << shader_stage_inst_name << ".main();" << std::endl
1678 << std::endl;
1679
1680 out << "}";
1681 return out.str();
1682}
1683
1684/* If first parameter in function signature, do not print out a comma.
1685 * Update first parameter flag to false for future invocations. */
1686static char parameter_delimiter(bool &is_first_parameter)
1687{
1688 if (is_first_parameter) {
1689 is_first_parameter = false;
1690 return ' ';
1691 }
1692 return ',';
1693}
1694
1696 ShaderStage stage,
1697 bool &is_first_parameter)
1698{
1699 /* NOTE: Shader stage must be specified as the singular stage index for which the input
1700 * is generating. Compound stages are not valid inputs. */
1702 stage == ShaderStage::COMPUTE);
1703 /* Generate texture signatures for textures used by this stage. */
1705 for (const MSLTextureResource &tex : this->texture_samplers) {
1706 if (bool(tex.stage & stage)) {
1707 out << parameter_delimiter(is_first_parameter) << "\n\t" << tex.get_msl_typestring(false)
1708 << " [[texture(" << tex.slot << ")]]";
1709 }
1710 }
1711
1712 /* Generate sampler signatures. */
1713 /* NOTE: Currently textures and samplers share indices across shading stages, so the limit is
1714 * shared.
1715 * If we exceed the hardware-supported limit, then follow a bind-less model using argument
1716 * buffers. */
1718 out << parameter_delimiter(is_first_parameter)
1719 << "\n\tconstant SStruct& samplers [[buffer(MTL_uniform_buffer_base_index+"
1720 << (this->get_sampler_argument_buffer_bind_index(stage)) << ")]]";
1721 }
1722 else {
1723 /* Maximum Limit of samplers defined in the function argument table is
1724 * `MTL_MAX_DEFAULT_SAMPLERS=16`. */
1725 BLI_assert(this->texture_samplers.size() <= MTL_MAX_DEFAULT_SAMPLERS);
1726 for (const MSLTextureResource &tex : this->texture_samplers) {
1727 if (bool(tex.stage & stage)) {
1728 out << parameter_delimiter(is_first_parameter) << "\n\tsampler " << tex.name
1729 << "_sampler [[sampler(" << tex.slot << ")]]";
1730 }
1731 }
1732
1733 /* Fallback. */
1734 if (this->texture_samplers.size() > 16) {
1736 "[Metal] Warning: Shader exceeds limit of %u samplers on current hardware\n",
1738 }
1739 }
1740}
1741
1743 ShaderStage stage,
1744 bool &is_first_parameter)
1745{
1746 for (const MSLBufferBlock &ubo : this->uniform_blocks) {
1747 if (bool(ubo.stage & stage)) {
1748 /* For literal/existing global types, we do not need the class name-space accessor. */
1749 out << parameter_delimiter(is_first_parameter) << "\n\tconstant ";
1750 if (!is_builtin_type(ubo.type_name)) {
1751 out << get_stage_class_name(stage) << "::";
1752 }
1753 /* #UniformBuffer bind indices start at `MTL_uniform_buffer_base_index + 1`, as
1754 * MTL_uniform_buffer_base_index is reserved for the #PushConstantBlock (push constants).
1755 * MTL_uniform_buffer_base_index is an offset depending on the number of unique VBOs
1756 * bound for the current PSO specialization. */
1757 out << ubo.type_name << "* " << ubo.name << "[[buffer(MTL_uniform_buffer_base_index+"
1758 << ubo.slot << ")]]";
1759 }
1760 }
1761
1762 /* Storage buffers. */
1763 for (const MSLBufferBlock &ssbo : this->storage_blocks) {
1764 if (bool(ssbo.stage & stage)) {
1765 out << parameter_delimiter(is_first_parameter) << "\n\t";
1766 if (bool(stage & ShaderStage::VERTEX)) {
1767 out << "const ";
1768 }
1769 /* For literal/existing global types, we do not need the class name-space accessor. */
1770 bool writeable = (ssbo.qualifiers & shader::Qualifier::write) == shader::Qualifier::write;
1771 const char *memory_scope = ((writeable) ? "device " : "constant ");
1772 out << memory_scope;
1773 if (!is_builtin_type(ssbo.type_name)) {
1774 out << get_stage_class_name(stage) << "::";
1775 }
1776 /* #StorageBuffer bind indices start at `MTL_storage_buffer_base_index`.
1777 * MTL_storage_buffer_base_index follows immediately after all uniform blocks.
1778 * such that MTL_storage_buffer_base_index = MTL_uniform_buffer_base_index +
1779 * uniform_blocks.size() + 1. Where the additional buffer is reserved for the
1780 * #PushConstantBlock (push constants). */
1781 out << ssbo.type_name << "* " << ssbo.name << "[[buffer(MTL_storage_buffer_base_index+"
1782 << (ssbo.slot) << ")]]";
1783 }
1784 }
1785}
1786
1788{
1789 std::stringstream out;
1790 bool is_first_parameter = true;
1791
1792 if (this->vertex_input_attributes.is_empty() == false) {
1793 /* Vertex Buffers use input assembly. */
1794 out << get_stage_class_name(ShaderStage::VERTEX) << "::VertexIn v_in [[stage_in]]";
1795 is_first_parameter = false;
1796 }
1797
1798 if (this->uniforms.is_empty() == false) {
1799 out << parameter_delimiter(is_first_parameter) << "\n\tconstant "
1801 << "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]";
1802 is_first_parameter = false;
1803 }
1804
1805 this->generate_msl_uniforms_input_string(out, ShaderStage::VERTEX, is_first_parameter);
1806
1807 /* Generate texture signatures. */
1808 this->generate_msl_textures_input_string(out, ShaderStage::VERTEX, is_first_parameter);
1809
1810 /* Entry point parameters for gl Globals. */
1811 if (this->uses_gl_VertexID) {
1812 out << parameter_delimiter(is_first_parameter)
1813 << "\n\tconst uint32_t gl_VertexID [[vertex_id]]";
1814 }
1815 if (this->uses_gl_InstanceID) {
1816 out << parameter_delimiter(is_first_parameter)
1817 << "\n\tconst uint32_t gl_InstanceID [[instance_id]]";
1818 }
1819 if (this->uses_gl_BaseInstanceARB) {
1820 out << parameter_delimiter(is_first_parameter)
1821 << "\n\tconst uint32_t gl_BaseInstanceARB [[base_instance]]";
1822 }
1823 return out.str();
1824}
1825
1827{
1828 bool is_first_parameter = true;
1829 std::stringstream out;
1831 << "::VertexOut v_in [[stage_in]]";
1832
1833 if (this->uniforms.is_empty() == false) {
1834 out << parameter_delimiter(is_first_parameter) << "\n\tconstant "
1836 << "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]";
1837 }
1838
1839 this->generate_msl_uniforms_input_string(out, ShaderStage::FRAGMENT, is_first_parameter);
1840
1841 /* Generate texture signatures. */
1842 this->generate_msl_textures_input_string(out, ShaderStage::FRAGMENT, is_first_parameter);
1843
1844 if (this->uses_gl_PointCoord) {
1845 out << parameter_delimiter(is_first_parameter)
1846 << "\n\tconst float2 gl_PointCoord [[point_coord]]";
1847 }
1848 if (this->uses_gl_FrontFacing) {
1849 out << parameter_delimiter(is_first_parameter)
1850 << "\n\tconst bool gl_FrontFacing [[front_facing]]";
1851 }
1852 if (this->uses_gl_PrimitiveID) {
1853 out << parameter_delimiter(is_first_parameter)
1854 << "\n\tconst uint gl_PrimitiveID [[primitive_id]]";
1855 }
1856
1857 /* Barycentrics. */
1858 if (this->uses_barycentrics) {
1859 out << parameter_delimiter(is_first_parameter)
1860 << "\n\tconst float3 mtl_barycentric_coord [[barycentric_coord]]";
1861 }
1862
1863 /* Fragment tile-inputs. */
1864 if (this->fragment_tile_inputs.is_empty() == false) {
1865 out << parameter_delimiter(is_first_parameter) << "\n\t"
1867 << "::" FRAGMENT_TILE_IN_STRUCT_NAME " fragment_tile_in";
1868 }
1869 return out.str();
1870}
1871
1873{
1874 bool is_first_parameter = true;
1875 std::stringstream out;
1876 if (this->uniforms.is_empty() == false) {
1877 out << parameter_delimiter(is_first_parameter) << "constant "
1879 << "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]";
1880 }
1881
1882 this->generate_msl_uniforms_input_string(out, ShaderStage::COMPUTE, is_first_parameter);
1883
1884 /* Generate texture signatures. */
1885 this->generate_msl_textures_input_string(out, ShaderStage::COMPUTE, is_first_parameter);
1886
1887 /* Entry point parameters for gl Globals. */
1888 if (this->uses_gl_GlobalInvocationID) {
1889 out << parameter_delimiter(is_first_parameter)
1890 << "\n\tconst uint3 gl_GlobalInvocationID [[thread_position_in_grid]]";
1891 }
1892 if (this->uses_gl_WorkGroupID) {
1893 out << parameter_delimiter(is_first_parameter)
1894 << "\n\tconst uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]";
1895 }
1896 if (this->uses_gl_NumWorkGroups) {
1897 out << parameter_delimiter(is_first_parameter)
1898 << "\n\tconst uint3 gl_NumWorkGroups [[threadgroups_per_grid]]";
1899 }
1900 if (this->uses_gl_LocalInvocationIndex) {
1901 out << parameter_delimiter(is_first_parameter)
1902 << "\n\tconst uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]";
1903 }
1904 if (this->uses_gl_LocalInvocationID) {
1905 out << parameter_delimiter(is_first_parameter)
1906 << "\n\tconst uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]";
1907 }
1908
1909 return out.str();
1910}
1911
1913{
1914 /* Only generate PushConstantBlock if we have uniforms. */
1915 if (this->uniforms.size() == 0) {
1916 return "";
1917 }
1918 BLI_assert(shader_stage == ShaderStage::VERTEX || shader_stage == ShaderStage::FRAGMENT);
1919 UNUSED_VARS_NDEBUG(shader_stage);
1920 std::stringstream out;
1921
1922 /* Common Uniforms. */
1923 out << "typedef struct {" << std::endl;
1924
1925 for (const MSLUniform &uniform : this->uniforms) {
1926 if (uniform.is_array) {
1927 out << "\t" << to_string(uniform.type) << " " << uniform.name << "[" << uniform.array_elems
1928 << "];" << std::endl;
1929 }
1930 else {
1931 out << "\t" << to_string(uniform.type) << " " << uniform.name << ";" << std::endl;
1932 }
1933 }
1934 out << "} PushConstantBlock;\n\n";
1935
1936 /* Member UBO block reference. */
1937 out << std::endl << "const constant PushConstantBlock *global_uniforms;" << std::endl;
1938
1939 /* Macro define chain.
1940 * To access uniforms, we generate a macro such that the uniform name can
1941 * be used directly without using the struct's handle. */
1942 for (const MSLUniform &uniform : this->uniforms) {
1943 out << "#define " << uniform.name << " global_uniforms->" << uniform.name << std::endl;
1944 }
1945 out << std::endl;
1946 return out.str();
1947}
1948
1949/* NOTE: Uniform macro definition vars can conflict with other parameters. */
1951{
1952 std::stringstream out;
1953
1954 /* Macro undef chain. */
1955 for (const MSLUniform &uniform : this->uniforms) {
1956 out << "#undef " << uniform.name << std::endl;
1957 }
1958 /* UBO block undef. */
1959 for (const MSLBufferBlock &ubo : this->uniform_blocks) {
1960 out << "#undef " << ubo.name << std::endl;
1961 }
1962 /* SSBO block undef. */
1963 for (const MSLBufferBlock &ssbo : this->storage_blocks) {
1964 out << "#undef " << ssbo.name << std::endl;
1965 }
1966 return out.str();
1967}
1968
1970{
1971 std::stringstream out;
1972
1973 /* Skip struct if no vert attributes. */
1974 if (this->vertex_input_attributes.size() == 0) {
1975 return "";
1976 }
1977
1978 /* Output */
1979 out << "typedef struct {" << std::endl;
1980 for (const MSLVertexInputAttribute &in_attr : this->vertex_input_attributes) {
1981 /* Matrix and array attributes are not trivially supported and thus
1982 * require each element to be passed as an individual attribute.
1983 * This requires shader source generation of sequential elements.
1984 * The matrix type is then re-packed into a Mat4 inside the entry function.
1985 *
1986 * e.g.
1987 * float4 __internal_modelmatrix_0 [[attribute(0)]];
1988 * float4 __internal_modelmatrix_1 [[attribute(1)]];
1989 * float4 __internal_modelmatrix_2 [[attribute(2)]];
1990 * float4 __internal_modelmatrix_3 [[attribute(3)]];
1991 */
1992 if (is_matrix_type(in_attr.type)) {
1993 for (int elem = 0; elem < get_matrix_location_count(in_attr.type); elem++) {
1994 out << "\t" << get_matrix_subtype(in_attr.type) << " __internal_" << in_attr.name << elem
1995 << " [[attribute(" << (in_attr.layout_location + elem) << ")]];" << std::endl;
1996 }
1997 }
1998 else {
1999 out << "\t" << in_attr.type << " " << in_attr.name << " [[attribute("
2000 << in_attr.layout_location << ")]];" << std::endl;
2001 }
2002 }
2003
2004 out << "} VertexIn;" << std::endl << std::endl;
2005
2006 return out.str();
2007}
2008
2010{
2011 BLI_assert(shader_stage == ShaderStage::VERTEX || shader_stage == ShaderStage::FRAGMENT);
2012 std::stringstream out;
2013
2014 /* Vertex output struct. */
2015 out << "typedef struct {" << std::endl;
2016
2017 /* If we use GL position, our standard output variable will be mapped to '_default_position_'.
2018 * Otherwise, we use the FIRST element in the output array. */
2019 bool first_attr_is_position = false;
2020 if (this->uses_gl_Position) {
2021
2022 /* If invariance is available, utilize this to consistently mitigate depth fighting artifacts
2023 * by ensuring that vertex position is consistently calculated between subsequent passes
2024 * with maximum precision. */
2025 out << "\tfloat4 _default_position_ [[position]]";
2026 out << " [[invariant]]";
2027 out << ";" << std::endl;
2028 }
2029 else {
2030 /* Use first output element for position. */
2031 BLI_assert(this->vertex_output_varyings.is_empty() == false);
2032 BLI_assert(this->vertex_output_varyings[0].type == "vec4");
2033
2034 /* Use invariance if available. See above for detail. */
2035 out << "\tfloat4 " << this->vertex_output_varyings[0].name << " [[position]];";
2036 out << " [[invariant]]";
2037 out << ";" << std::endl;
2038 first_attr_is_position = true;
2039 }
2040
2041 /* Generate other vertex output members. */
2042 bool skip_first_index = first_attr_is_position;
2043 for (const MSLVertexOutputAttribute &v_out : this->vertex_output_varyings) {
2044
2045 /* Skip first index if used for position. */
2046 if (skip_first_index) {
2047 skip_first_index = false;
2048 continue;
2049 }
2050
2051 if (v_out.is_array) {
2052 /* Array types cannot be trivially passed between shading stages.
2053 * Instead we pass each component individually. E.g. vec4 pos[2]
2054 * will be converted to: `vec4 pos_0; vec4 pos_1;`
2055 * The specified interpolation qualifier will be applied per element. */
2056 /* TODO(Metal): Support array of matrix in-out types if required
2057 * e.g. Mat4 out_matrices[3]. */
2058 for (int i = 0; i < v_out.array_elems; i++) {
2059 out << "\t" << v_out.type << " " << v_out.instance_name << "_" << v_out.name << i
2060 << v_out.get_mtl_interpolation_qualifier() << ";" << std::endl;
2061 }
2062 }
2063 else {
2064 /* Matrix types need to be expressed as their vector sub-components. */
2065 if (is_matrix_type(v_out.type)) {
2066 BLI_assert(v_out.get_mtl_interpolation_qualifier() == " [[flat]]" &&
2067 "Matrix varying types must have [[flat]] interpolation");
2068 std::string subtype = get_matrix_subtype(v_out.type);
2069 for (int elem = 0; elem < get_matrix_location_count(v_out.type); elem++) {
2070 out << "\t" << subtype << v_out.instance_name << " __matrix_" << v_out.name << elem
2071 << v_out.get_mtl_interpolation_qualifier() << ";" << std::endl;
2072 }
2073 }
2074 else {
2075 out << "\t" << v_out.type << " " << v_out.instance_name << "_" << v_out.name
2076 << v_out.get_mtl_interpolation_qualifier() << ";" << std::endl;
2077 }
2078 }
2079 }
2080
2081 /* Add gl_PointSize if written to. */
2082 if (shader_stage == ShaderStage::VERTEX) {
2083 if (this->uses_gl_PointSize) {
2084 /* If `gl_PointSize` is explicitly written to,
2085 * we will output the written value directly.
2086 * This value can still be overridden by the
2087 * global point-size value. */
2088 out << "\tfloat pointsize [[point_size]];" << std::endl;
2089 }
2090 else {
2091 /* Otherwise, if point-size is not written to inside the shader,
2092 * then its usage is controlled by whether the `MTL_global_pointsize`
2093 * function constant has been specified.
2094 * This function constant is enabled for all point primitives being rendered. */
2095 out << "\tfloat pointsize [[point_size, function_constant(MTL_global_pointsize)]];"
2096 << std::endl;
2097 }
2098 }
2099
2100 /* Add gl_ClipDistance[n]. */
2101 if (shader_stage == ShaderStage::VERTEX) {
2102 out << "#if defined(USE_CLIP_PLANES) || defined(USE_WORLD_CLIP_PLANES)" << std::endl;
2103 if (this->clip_distances.size() > 1) {
2104 /* Output array of clip distances if specified. */
2105 out << "\tfloat clipdistance [[clip_distance, "
2106 "function_constant(MTL_clip_distances_enabled)]] ["
2107 << this->clip_distances.size() << "];" << std::endl;
2108 }
2109 else if (this->clip_distances.is_empty() == false) {
2110 out << "\tfloat clipdistance [[clip_distance, "
2111 "function_constant(MTL_clip_distances_enabled)]];"
2112 << std::endl;
2113 }
2114 out << "#endif" << std::endl;
2115 }
2116
2117 /* Add MTL render target array index for multilayered rendering support. */
2118 if (uses_gpu_layer) {
2119 out << "\tuint gpu_Layer [[render_target_array_index]];" << std::endl;
2120 }
2121
2122 /* Add Viewport Index output */
2124 out << "\tuint gpu_ViewportIndex [[viewport_array_index]];" << std::endl;
2125 }
2126
2127 out << "} VertexOut;" << std::endl << std::endl;
2128
2129 return out.str();
2130}
2131
2133{
2134 std::stringstream out;
2135
2136 auto &fragment_interface_src = (is_input) ? this->fragment_tile_inputs : this->fragment_outputs;
2137
2138 /* Output. */
2139 out << "typedef struct {" << std::endl;
2140 for (int f_output = 0; f_output < fragment_interface_src.size(); f_output++) {
2141 out << "\t" << to_string(fragment_interface_src[f_output].type) << " "
2142 << fragment_interface_src[f_output].name << " [[color("
2143 << fragment_interface_src[f_output].layout_location << ")";
2144 if (fragment_interface_src[f_output].layout_index >= 0) {
2145 out << ", index(" << fragment_interface_src[f_output].layout_index << ")";
2146 }
2147 if (fragment_interface_src[f_output].raster_order_group >= 0) {
2148 out << ", raster_order_group(" << fragment_interface_src[f_output].raster_order_group << ")";
2149 }
2150 out << "]]"
2151 << ";" << std::endl;
2152 }
2153 /* Add gl_FragDepth output if used. */
2154 if (this->uses_gl_FragDepth) {
2155 std::string out_depth_argument = ((this->depth_write == DepthWrite::GREATER) ?
2156 "greater" :
2157 ((this->depth_write == DepthWrite::LESS) ? "less" :
2158 "any"));
2159 out << "\tfloat fragdepth [[depth(" << out_depth_argument << ")]];" << std::endl;
2160 }
2161 /* Add gl_FragStencilRefARB output if used. */
2162 if (!is_input && this->uses_gl_FragStencilRefARB) {
2163 out << "\tuint fragstencil [[stencil]];" << std::endl;
2164 }
2165 if (is_input) {
2166 out << "} " FRAGMENT_TILE_IN_STRUCT_NAME ";" << std::endl;
2167 }
2168 else {
2169 out << "} " FRAGMENT_OUT_STRUCT_NAME ";" << std::endl;
2170 }
2171 out << std::endl;
2172 return out.str();
2173}
2174
2176{
2177 if (this->uniforms.size() == 0) {
2178 return "";
2179 }
2180 /* Populate Global Uniforms. */
2181 std::stringstream out;
2182
2183 /* Copy UBO block ref. */
2184 out << "\t/* Copy Uniform block member reference */" << std::endl;
2185 out << "\t" << get_shader_stage_instance_name(stage) << "."
2186 << "global_uniforms = uniforms;" << std::endl;
2187
2188 return out.str();
2189}
2190
2192{
2193 std::stringstream out;
2194
2195 /* Native tile read is supported on tile-based architectures (Apple Silicon). */
2196 if (MTLBackend::capabilities.supports_native_tile_inputs) {
2197 for (const MSLFragmentTileInputAttribute &tile_input : this->fragment_tile_inputs) {
2199 << tile_input.name << " = "
2200 << "fragment_tile_in." << tile_input.name << ";" << std::endl;
2201 }
2202 }
2203 else {
2204 for (const MSLFragmentTileInputAttribute &tile_input : this->fragment_tile_inputs) {
2205 /* Get read swizzle mask. */
2206 char swizzle[] = "xyzw";
2207 swizzle[to_component_count(tile_input.type)] = '\0';
2208
2209 bool is_layered_fb = bool(create_info_->builtins_ & BuiltinBits::LAYER);
2210 std::string texel_co =
2211 (tile_input.is_layered_input) ?
2212 ((is_layered_fb) ? "ivec3(ivec2(v_in._default_position_.xy), int(v_in.gpu_Layer))" :
2213 /* This should fetch the attached layer.
2214 * But this is not simple to set. For now
2215 * assume it is always the first layer. */
2216 "ivec3(ivec2(v_in._default_position_.xy), 0)") :
2217 "ivec2(v_in._default_position_.xy)";
2218
2220 << tile_input.name << " = imageLoad("
2221 << get_shader_stage_instance_name(ShaderStage::FRAGMENT) << "." << tile_input.name
2222 << "_subpass_img, " << texel_co << ")." << swizzle << ";\n";
2223 }
2224 }
2225 return out.str();
2226}
2227
2229{
2230 /* Populate Global Uniforms. */
2231 std::stringstream out;
2232 out << "\t/* Copy UBO block references into local class variables */" << std::endl;
2233 for (const MSLBufferBlock &ubo : this->uniform_blocks) {
2234
2235 /* Only include blocks which are used within this stage. */
2236 if (bool(ubo.stage & stage)) {
2237 /* Generate UBO reference assignment.
2238 * NOTE(Metal): We append `_local` post-fix onto the class member name
2239 * for the ubo to avoid name collision with the UBO accessor macro.
2240 * We only need to add this post-fix for the non-array access variant,
2241 * as the array is indexed directly, rather than requiring a dereference. */
2242 out << "\t" << get_shader_stage_instance_name(stage) << "." << ubo.name;
2243 if (!ubo.is_array) {
2244 out << "_local";
2245 }
2246 out << " = " << ubo.name << ";" << std::endl;
2247 }
2248 }
2249
2250 /* Populate storage buffer references. */
2251 out << "\t/* Copy SSBO block references into local class variables */" << std::endl;
2252 for (const MSLBufferBlock &ssbo : this->storage_blocks) {
2253
2254 /* Only include blocks which are used within this stage. */
2255 if (bool(ssbo.stage & stage) && !ssbo.is_texture_buffer) {
2256 /* Generate UBO reference assignment.
2257 * NOTE(Metal): We append `_local` post-fix onto the class member name
2258 * for the ubo to avoid name collision with the UBO accessor macro.
2259 * We only need to add this post-fix for the non-array access variant,
2260 * as the array is indexed directly, rather than requiring a dereference. */
2261 out << "\t" << get_shader_stage_instance_name(stage) << "." << ssbo.name;
2262 if (!ssbo.is_array) {
2263 out << "_local";
2264 }
2265 out << " = ";
2266
2267 if (bool(stage & ShaderStage::VERTEX)) {
2268 bool writeable = bool(ssbo.qualifiers & shader::Qualifier::write);
2269 const char *memory_scope = ((writeable) ? "device " : "constant ");
2270
2271 out << "const_cast<" << memory_scope;
2272
2273 if (!is_builtin_type(ssbo.type_name)) {
2274 out << get_stage_class_name(stage) << "::";
2275 }
2276 out << ssbo.type_name << "*>(";
2277 }
2278 out << ssbo.name;
2279 if (bool(stage & ShaderStage::VERTEX)) {
2280 out << ")";
2281 }
2282 out << ";" << std::endl;
2283 }
2284 }
2285
2286 out << std::endl;
2287 return out.str();
2288}
2289
2290/* Copy input attributes from stage_in into class local variables. */
2292{
2293 static const char *shader_stage_inst_name = get_shader_stage_instance_name(ShaderStage::VERTEX);
2294
2295 /* Populate local attribute variables. */
2296 std::stringstream out;
2297 out << "\t/* Copy Vertex Stage-in attributes into local variables */" << std::endl;
2298 for (int attribute = 0; attribute < this->vertex_input_attributes.size(); attribute++) {
2299
2300 if (is_matrix_type(this->vertex_input_attributes[attribute].type)) {
2301 /* Reading into an internal matrix from split attributes: Should generate the following:
2302 * vertex_shader_instance.mat_attribute_type =
2303 * mat4(v_in.__internal_mat_attribute_type0,
2304 * v_in.__internal_mat_attribute_type1,
2305 * v_in.__internal_mat_attribute_type2,
2306 * v_in.__internal_mat_attribute_type3). */
2307 out << "\t" << shader_stage_inst_name << "." << this->vertex_input_attributes[attribute].name
2308 << " = " << this->vertex_input_attributes[attribute].type << "(v_in.__internal_"
2309 << this->vertex_input_attributes[attribute].name << 0;
2310 for (int elem = 1;
2311 elem < get_matrix_location_count(this->vertex_input_attributes[attribute].type);
2312 elem++)
2313 {
2314 out << ",\n"
2315 << "v_in.__internal_" << this->vertex_input_attributes[attribute].name << elem;
2316 }
2317 out << ");";
2318 }
2319 else {
2320 /* OpenGL uses the `GPU_FETCH_*` functions which can alter how an attribute value is
2321 * interpreted. In Metal, we cannot support all implicit conversions within the vertex
2322 * descriptor/vertex stage-in, so we need to perform value transformation on-read.
2323 *
2324 * This is handled by wrapping attribute reads to local shader registers in a
2325 * suitable conversion function `attribute_conversion_func_name`.
2326 * This conversion function performs a specific transformation on the source
2327 * vertex data, depending on the specified GPU_FETCH_* mode for the current
2328 * vertex format.
2329 *
2330 * The fetch_mode is specified per-attribute using specialization constants
2331 * on the PSO, wherein a unique set of constants is passed in per vertex
2332 * buffer/format configuration. Efficiently enabling pass-through reads
2333 * if no special fetch is required. */
2334 bool do_attribute_conversion_on_read = false;
2335 std::string attribute_conversion_func_name = get_attribute_conversion_function(
2336 &do_attribute_conversion_on_read, this->vertex_input_attributes[attribute].type);
2337
2338 if (do_attribute_conversion_on_read) {
2339 BLI_assert(this->vertex_input_attributes[attribute].layout_location >= 0);
2340 out << "\t" << attribute_conversion_func_name << "(MTL_AttributeConvert"
2341 << this->vertex_input_attributes[attribute].layout_location << ", v_in."
2342 << this->vertex_input_attributes[attribute].name << ", " << shader_stage_inst_name
2343 << "." << this->vertex_input_attributes[attribute].name << ");" << std::endl;
2344 }
2345 else {
2346 out << "\t" << shader_stage_inst_name << "."
2347 << this->vertex_input_attributes[attribute].name << " = v_in."
2348 << this->vertex_input_attributes[attribute].name << ";" << std::endl;
2349 }
2350 }
2351 }
2352 out << std::endl;
2353 return out.str();
2354}
2355
2356/* Copy post-main, modified, local class variables into vertex-output struct. */
2358{
2359 static const char *shader_stage_inst_name = get_shader_stage_instance_name(ShaderStage::VERTEX);
2360 std::stringstream out;
2361 out << "\t/* Copy Vertex Outputs into output struct */" << std::endl;
2362
2363 /* Output gl_Position with conversion to Metal coordinate-space. */
2364 if (this->uses_gl_Position) {
2365 out << "\toutput._default_position_ = " << shader_stage_inst_name << ".gl_Position;"
2366 << std::endl;
2367
2368 /* Invert Y and rescale depth range.
2369 * This is an alternative method to modifying all projection matrices. */
2370 out << "\toutput._default_position_.y = -output._default_position_.y;" << std::endl;
2371 out << "\toutput._default_position_.z = "
2372 "(output._default_position_.z+output._default_position_.w)/2.0;"
2373 << std::endl;
2374 }
2375
2376 /* Output Point-size. */
2377 if (this->uses_gl_PointSize) {
2378 out << "\toutput.pointsize = " << shader_stage_inst_name << ".gl_PointSize;" << std::endl;
2379 }
2380
2381 /* Output render target array Index. */
2382 if (uses_gpu_layer) {
2383 out << "\toutput.gpu_Layer = " << shader_stage_inst_name << ".gpu_Layer;" << std::endl;
2384 }
2385
2386 /* Output Viewport Index. */
2388 out << "\toutput.gpu_ViewportIndex = " << shader_stage_inst_name << ".gpu_ViewportIndex;"
2389 << std::endl;
2390 }
2391
2392 /* Output clip-distances.
2393 * Clip distances are only written to if both clipping planes are turned on for the shader,
2394 * and the clipping planes are enabled. Enablement is controlled on a per-plane basis
2395 * via function constants in the shader pipeline state object (PSO). */
2396 out << "#if defined(USE_CLIP_PLANES) || defined(USE_WORLD_CLIP_PLANES)" << std::endl
2397 << "if(MTL_clip_distances_enabled) {" << std::endl;
2398 if (this->clip_distances.size() > 1) {
2399 for (int cd = 0; cd < this->clip_distances.size(); cd++) {
2400 /* Default value when clipping is disabled >= 0.0 to ensure primitive is not clipped. */
2401 out << "\toutput.clipdistance[" << cd
2402 << "] = (is_function_constant_defined(MTL_clip_distance_enabled" << cd << "))?"
2403 << shader_stage_inst_name << ".gl_ClipDistance_" << cd << ":1.0;" << std::endl;
2404 }
2405 }
2406 else if (this->clip_distances.is_empty() == false) {
2407 out << "\toutput.clipdistance = " << shader_stage_inst_name << ".gl_ClipDistance_0;"
2408 << std::endl;
2409 }
2410 out << "}" << std::endl << "#endif" << std::endl;
2411
2412 /* Populate output vertex variables. */
2413 int output_id = 0;
2414 for (const MSLVertexOutputAttribute &v_out : this->vertex_output_varyings) {
2415 if (v_out.is_array) {
2416
2417 for (int i = 0; i < v_out.array_elems; i++) {
2418 out << "\toutput." << v_out.instance_name << "_" << v_out.name << i << " = "
2419 << shader_stage_inst_name << ".";
2420
2421 if (v_out.instance_name.empty() == false) {
2422 out << v_out.instance_name << ".";
2423 }
2424
2425 out << v_out.name << "[" << i << "]"
2426 << ";" << std::endl;
2427 }
2428 }
2429 else {
2430 /* Matrix types are split into vectors and need to be reconstructed. */
2431 if (is_matrix_type(v_out.type)) {
2432 for (int elem = 0; elem < get_matrix_location_count(v_out.type); elem++) {
2433 out << "\toutput." << v_out.instance_name << "__matrix_" << v_out.name << elem << " = "
2434 << shader_stage_inst_name << ".";
2435
2436 if (v_out.instance_name.empty() == false) {
2437 out << v_out.instance_name << ".";
2438 }
2439
2440 out << v_out.name << "[" << elem << "];" << std::endl;
2441 }
2442 }
2443 else {
2444 /* If we are not using gl_Position, first vertex output is used for position.
2445 * Ensure it is vec4. */
2446 if (!this->uses_gl_Position && output_id == 0) {
2447 out << "\toutput." << v_out.instance_name << "_" << v_out.name << " = to_vec4("
2448 << shader_stage_inst_name << "." << v_out.name << ");" << std::endl;
2449
2450 /* Invert Y */
2451 out << "\toutput." << v_out.instance_name << "_" << v_out.name << ".y = -output."
2452 << v_out.name << ".y;" << std::endl;
2453 }
2454 else {
2455 /* Assign vertex output. */
2456 out << "\toutput." << v_out.instance_name << "_" << v_out.name << " = "
2457 << shader_stage_inst_name << ".";
2458
2459 if (v_out.instance_name.empty() == false) {
2460 out << v_out.instance_name << ".";
2461 }
2462
2463 out << v_out.name << ";" << std::endl;
2464 }
2465 }
2466 }
2467 output_id++;
2468 }
2469 out << std::endl;
2470 return out.str();
2471}
2472
2473/* Copy fragment stage inputs (Vertex Outputs) into local class variables. */
2475{
2476 static const char *shader_stage_inst_name = get_shader_stage_instance_name(
2478 /* Populate local attribute variables. */
2479 std::stringstream out;
2480 out << "\t/* Copy Fragment input into local variables. */" << std::endl;
2481
2482 /* Special common case for gl_FragCoord, assigning to input position. */
2483 if (this->uses_gl_Position) {
2484 out << "\t" << shader_stage_inst_name << ".gl_FragCoord = v_in._default_position_;"
2485 << std::endl;
2486 }
2487 else {
2488 /* When gl_Position is not set, first VertexIn element is used for position. */
2489 out << "\t" << shader_stage_inst_name << ".gl_FragCoord = v_in."
2490 << this->vertex_output_varyings[0].name << ";" << std::endl;
2491 }
2492
2493 /* Assign default gl_FragDepth.
2494 * If gl_FragDepth is used, it should default to the original depth value. Resolves #107159 where
2495 * overlay_wireframe_frag may not write to gl_FragDepth. */
2496 if (this->uses_gl_FragDepth) {
2497 out << "\t" << shader_stage_inst_name << ".gl_FragDepth = " << shader_stage_inst_name
2498 << ".gl_FragCoord.z;" << std::endl;
2499 }
2500
2501 /* Input render target array index received from vertex shader. */
2502 if (uses_gpu_layer) {
2503 out << "\t" << shader_stage_inst_name << ".gpu_Layer = v_in.gpu_Layer;" << std::endl;
2504 }
2505
2506 /* Input viewport array index received from vertex shader. */
2508 out << "\t" << shader_stage_inst_name << ".gpu_ViewportIndex = v_in.gpu_ViewportIndex;"
2509 << std::endl;
2510 }
2511
2512 /* NOTE: We will only assign to the intersection of the vertex output and fragment input.
2513 * Fragment input represents varying variables which are declared (but are not necessarily
2514 * used). The Vertex out defines the set which is passed into the fragment shader, which
2515 * contains out variables declared in the vertex shader, though these are not necessarily
2516 * consumed by the fragment shader.
2517 *
2518 * In the cases where the fragment shader expects a variable, but it does not exist in the
2519 * vertex shader, a warning will be provided. */
2520 for (int f_input = (this->uses_gl_Position) ? 0 : 1;
2521 f_input < this->fragment_input_varyings.size();
2522 f_input++)
2523 {
2524 bool exists_in_vertex_output = false;
2525 for (int v_o = 0; v_o < this->vertex_output_varyings.size() && !exists_in_vertex_output; v_o++)
2526 {
2527 if (this->fragment_input_varyings[f_input].name == this->vertex_output_varyings[v_o].name) {
2528 exists_in_vertex_output = true;
2529 }
2530 }
2531 if (!exists_in_vertex_output) {
2533 "[Warning] Fragment shader expects varying input '%s', but this is not passed from "
2534 "the "
2535 "vertex shader\n",
2536 this->fragment_input_varyings[f_input].name.c_str());
2537 continue;
2538 }
2539 if (this->fragment_input_varyings[f_input].is_array) {
2540 for (int i = 0; i < this->fragment_input_varyings[f_input].array_elems; i++) {
2541 out << "\t" << shader_stage_inst_name << ".";
2542
2543 if (this->fragment_input_varyings[f_input].instance_name.empty() == false) {
2544 out << this->fragment_input_varyings[f_input].instance_name << ".";
2545 }
2546
2547 out << this->fragment_input_varyings[f_input].name << "[" << i << "] = v_in."
2548 << this->fragment_input_varyings[f_input].instance_name << "_"
2549 << this->fragment_input_varyings[f_input].name << i << ";" << std::endl;
2550 }
2551 }
2552 else {
2553 /* Matrix types are split into components and need to be regrouped into a matrix. */
2554 if (is_matrix_type(this->fragment_input_varyings[f_input].type)) {
2555 out << "\t" << shader_stage_inst_name << ".";
2556
2557 if (this->fragment_input_varyings[f_input].instance_name.empty() == false) {
2558 out << this->fragment_input_varyings[f_input].instance_name << ".";
2559 }
2560
2561 out << this->fragment_input_varyings[f_input].name << " = "
2562 << this->fragment_input_varyings[f_input].type;
2563 int count = get_matrix_location_count(this->fragment_input_varyings[f_input].type);
2564 for (int elem = 0; elem < count; elem++) {
2565 out << ((elem == 0) ? "(" : "") << "v_in."
2566 << this->fragment_input_varyings[f_input].instance_name << "__matrix_"
2567 << this->fragment_input_varyings[f_input].name << elem
2568 << ((elem < count - 1) ? ",\n" : "");
2569 }
2570 out << ");" << std::endl;
2571 }
2572 else {
2573 out << "\t" << shader_stage_inst_name << ".";
2574
2575 if (this->fragment_input_varyings[f_input].instance_name.empty() == false) {
2576 out << this->fragment_input_varyings[f_input].instance_name << ".";
2577 }
2578
2579 out << this->fragment_input_varyings[f_input].name << " = v_in."
2580 << this->fragment_input_varyings[f_input].instance_name << "_"
2581 << this->fragment_input_varyings[f_input].name << ";" << std::endl;
2582 }
2583 }
2584 }
2585 out << std::endl;
2586 return out.str();
2587}
2588
2589/* Copy post-main, modified, local class variables into fragment-output struct. */
2591{
2592 static const char *shader_stage_inst_name = get_shader_stage_instance_name(
2594 /* Populate output fragment variables. */
2595 std::stringstream out;
2596 out << "\t/* Copy Fragment Outputs into output struct. */" << std::endl;
2597
2598 /* Output gl_FragDepth. */
2599 if (this->uses_gl_FragDepth) {
2600 out << "\toutput.fragdepth = " << shader_stage_inst_name << ".gl_FragDepth;" << std::endl;
2601 }
2602
2603 /* Output gl_FragStencilRefARB. */
2604 if (this->uses_gl_FragStencilRefARB) {
2605 out << "\toutput.fragstencil = uint(" << shader_stage_inst_name << ".gl_FragStencilRefARB);"
2606 << std::endl;
2607 }
2608
2609 /* Output attributes. */
2610 for (int f_output = 0; f_output < this->fragment_outputs.size(); f_output++) {
2611
2612 out << "\toutput." << this->fragment_outputs[f_output].name << " = " << shader_stage_inst_name
2613 << "." << this->fragment_outputs[f_output].name << ";" << std::endl;
2614 }
2615 out << std::endl;
2616 return out.str();
2617}
2618
2620{
2621 /* NOTE: Shader stage must be a singular stage index. Compound stage is not valid for this
2622 * function. */
2623 BLI_assert(shader_stage == ShaderStage::VERTEX || shader_stage == ShaderStage::FRAGMENT ||
2624 shader_stage == ShaderStage::COMPUTE);
2625
2626 std::stringstream out;
2627 out << "\t/* Populate local texture and sampler members */" << std::endl;
2628 for (int i = 0; i < this->texture_samplers.size(); i++) {
2629 if (bool(this->texture_samplers[i].stage & shader_stage)) {
2630
2631 /* Assign texture reference. */
2632 out << "\t" << get_shader_stage_instance_name(shader_stage) << "."
2633 << this->texture_samplers[i].name << ".texture = &" << this->texture_samplers[i].name
2634 << ";" << std::endl;
2635
2636 /* Assign sampler reference. */
2638 out << "\t" << get_shader_stage_instance_name(shader_stage) << "."
2639 << this->texture_samplers[i].name << ".samp = &samplers.sampler_args["
2640 << this->texture_samplers[i].slot << "];" << std::endl;
2641 }
2642 else {
2643 out << "\t" << get_shader_stage_instance_name(shader_stage) << "."
2644 << this->texture_samplers[i].name << ".samp = &" << this->texture_samplers[i].name
2645 << "_sampler;" << std::endl;
2646 }
2647
2648 /* Assign texture buffer reference and uniform metadata (if used). */
2649 int tex_buf_id = this->texture_samplers[i].atomic_fallback_buffer_ssbo_id;
2650 if (tex_buf_id != -1) {
2651 MSLBufferBlock &ssbo = this->storage_blocks[tex_buf_id];
2652 out << "\t" << get_shader_stage_instance_name(shader_stage) << "."
2653 << this->texture_samplers[i].name << ".atomic.buffer = ";
2654
2655 if (bool(shader_stage & ShaderStage::VERTEX)) {
2656 bool writeable = bool(ssbo.qualifiers & shader::Qualifier::write);
2657 const char *memory_scope = ((writeable) ? "device " : "constant ");
2658
2659 out << "const_cast<" << memory_scope;
2660
2661 if (!is_builtin_type(ssbo.type_name)) {
2662 out << get_stage_class_name(shader_stage) << "::";
2663 }
2664 out << ssbo.type_name << "*>(";
2665 }
2666 out << ssbo.name;
2667 if (bool(shader_stage & ShaderStage::VERTEX)) {
2668 out << ")";
2669 }
2670 out << ";" << std::endl;
2671
2672 out << "\t" << get_shader_stage_instance_name(shader_stage) << "."
2673 << this->texture_samplers[i].name << ".atomic.aligned_width = uniforms->"
2674 << this->texture_samplers[i].name << "_metadata.w;" << std::endl;
2675
2676 /* Buffer-backed 2D Array and 3D texture types are not natively supported so texture size
2677 * is passed in as uniform metadata for 3D to 2D coordinate remapping. */
2678 if (ELEM(this->texture_samplers[i].type,
2679 ImageType::AtomicUint2DArray,
2680 ImageType::AtomicUint3D,
2681 ImageType::AtomicInt2DArray,
2682 ImageType::AtomicInt3D))
2683 {
2684 out << "\t" << get_shader_stage_instance_name(shader_stage) << "."
2685 << this->texture_samplers[i].name << ".atomic.texture_size = ushort3(uniforms->"
2686 << this->texture_samplers[i].name << "_metadata.xyz);" << std::endl;
2687 }
2688 }
2689 }
2690 }
2691 out << std::endl;
2692 return out.str();
2693}
2694
2696{
2697 /* Determine used-attribute-location mask. */
2698 uint32_t used_locations = 0;
2700 if (attr.layout_location >= 0) {
2701 /* Matrix and array types span multiple location slots. */
2702 uint32_t location_element_count = get_matrix_location_count(attr.type);
2703 for (uint32_t i = 1; i <= location_element_count; i++) {
2704 /* Ensure our location hasn't already been used. */
2705 uint32_t location_mask = (i << attr.layout_location);
2706 BLI_assert((used_locations & location_mask) == 0);
2707 used_locations = used_locations | location_mask;
2708 }
2709 }
2710 }
2711
2712 /* Assign unused location slots to other attributes. */
2714 if (attr.layout_location == -1) {
2715 /* Determine number of locations required. */
2716 uint32_t required_attr_slot_count = get_matrix_location_count(attr.type);
2717
2718 /* Determine free location.
2719 * Starting from 1 is slightly less efficient, however,
2720 * given multi-sized attributes, an earlier slot may remain free.
2721 * given GPU_VERT_ATTR_MAX_LEN is small, this wont matter. */
2722 for (int loc = 0; loc < GPU_VERT_ATTR_MAX_LEN - (required_attr_slot_count - 1); loc++) {
2723
2724 uint32_t location_mask = (1 << loc);
2725 /* Generate sliding mask using location and required number of slots,
2726 * to ensure contiguous slots are free.
2727 * slot mask will be a number containing N binary 1's, where N is the
2728 * number of attributes needed.
2729 * e.g. N=4 -> 1111. */
2730 uint32_t location_slot_mask = (1 << required_attr_slot_count) - 1;
2731 uint32_t sliding_location_slot_mask = location_slot_mask << location_mask;
2732 if ((used_locations & sliding_location_slot_mask) == 0) {
2733 /* Assign location and update mask. */
2734 attr.layout_location = loc;
2735 used_locations = used_locations | location_slot_mask;
2736 continue;
2737 }
2738 }
2739
2740 /* Error if could not assign attribute. */
2741 MTL_LOG_ERROR("Could not assign attribute location to attribute %s for shader %s",
2742 attr.name.c_str(),
2743 this->parent_shader_.name_get().c_str());
2744 }
2745 }
2746}
2747
2749{
2750 int running_location_ind = 0;
2751
2752 /* This code works under the assumption that either all layout_locations are set,
2753 * or none are. */
2754 for (int i = 0; i < this->fragment_outputs.size(); i++) {
2756 ((running_location_ind > 0) ? (this->fragment_outputs[i].layout_location == -1) : true),
2757 "Error: Mismatched input attributes, some with location specified, some without");
2758 if (this->fragment_outputs[i].layout_location == -1) {
2759 this->fragment_outputs[i].layout_location = running_location_ind;
2760 running_location_ind++;
2761 }
2762 }
2763}
2764
2769static uint32_t name_buffer_copystr(char **name_buffer_ptr,
2770 const char *str_to_copy,
2771 uint32_t &name_buffer_size,
2772 uint32_t &name_buffer_offset)
2773{
2774 /* Verify input is valid. */
2775 BLI_assert(str_to_copy != nullptr);
2776
2777 /* Determine length of new string, and ensure name buffer is large enough. */
2778 uint32_t ret_len = strlen(str_to_copy);
2779 BLI_assert(ret_len > 0);
2780
2781 /* If required name buffer size is larger, increase by at least 128 bytes. */
2782 if (name_buffer_offset + ret_len + 1 > name_buffer_size) {
2783 name_buffer_size = name_buffer_offset + max_ii(128, ret_len + 1);
2784 *name_buffer_ptr = (char *)MEM_reallocN(*name_buffer_ptr, name_buffer_size);
2785 }
2786
2787 /* Copy string into name buffer. */
2788 uint32_t insert_offset = name_buffer_offset;
2789 char *current_offset = (*name_buffer_ptr) + insert_offset;
2790 memcpy(current_offset, str_to_copy, (ret_len + 1) * sizeof(char));
2791
2792 /* Adjust offset including null terminator. */
2793 name_buffer_offset += ret_len + 1;
2794
2795 /* Return offset into name buffer for inserted string. */
2796 return insert_offset;
2797}
2798
2800 const char *name, const shader::ShaderCreateInfo *info)
2801{
2803 interface->init();
2804
2805 /* Name buffer. */
2806 /* Initialize name buffer. */
2807 uint32_t name_buffer_size = 256;
2808 uint32_t name_buffer_offset = 0;
2809 interface->name_buffer_ = (char *)MEM_mallocN(name_buffer_size, "name_buffer");
2810
2811 /* Prepare Interface Input Attributes. */
2812 int c_offset = 0;
2813 for (int attribute = 0; attribute < this->vertex_input_attributes.size(); attribute++) {
2814
2815 /* We need a special case for handling matrix types, which splits the matrix into its vector
2816 * components. */
2817 if (is_matrix_type(this->vertex_input_attributes[attribute].type)) {
2818
2820 get_matrix_subtype(this->vertex_input_attributes[attribute].type));
2821 int size = mtl_get_data_type_size(mtl_type);
2822 for (int elem = 0;
2823 elem < get_matrix_location_count(this->vertex_input_attributes[attribute].type);
2824 elem++)
2825 {
2826 /* First attribute matches the core name -- subsequent attributes tagged with
2827 * `__internal_<name><index>`. */
2828 std::string _internal_name = (elem == 0) ?
2829 this->vertex_input_attributes[attribute].name :
2830 "__internal_" +
2831 this->vertex_input_attributes[attribute].name +
2832 std::to_string(elem);
2833
2834 interface->add_input_attribute(
2835 name_buffer_copystr(&interface->name_buffer_,
2836 _internal_name.c_str(),
2837 name_buffer_size,
2838 name_buffer_offset),
2839 this->vertex_input_attributes[attribute].layout_location + elem,
2841 0,
2842 size,
2843 c_offset,
2844 (elem == 0) ?
2845 get_matrix_location_count(this->vertex_input_attributes[attribute].type) :
2846 0);
2847 c_offset += size;
2848 }
2850 "[Note] Matrix Type '%s' added to shader interface as vertex attribute. (Elem Count: "
2851 "%d)\n",
2852 this->vertex_input_attributes[attribute].name.c_str(),
2853 get_matrix_location_count(this->vertex_input_attributes[attribute].type));
2854 }
2855 else {
2856
2857 /* Normal attribute types. */
2858 MTLInterfaceDataType mtl_type = to_mtl_type(this->vertex_input_attributes[attribute].type);
2859 int size = mtl_get_data_type_size(mtl_type);
2860 interface->add_input_attribute(
2861 name_buffer_copystr(&interface->name_buffer_,
2862 this->vertex_input_attributes[attribute].name.c_str(),
2863 name_buffer_size,
2864 name_buffer_offset),
2865 this->vertex_input_attributes[attribute].layout_location,
2867 0,
2868 size,
2869 c_offset);
2870 c_offset += size;
2871 }
2872
2873 /* Used in `GPU_shader_get_attribute_info`. */
2874 interface->attr_types_[this->vertex_input_attributes[attribute].layout_location] = uint8_t(
2875 this->vertex_input_attributes[attribute].type);
2876 }
2877
2878 /* Prepare Interface Default Uniform Block. */
2879 interface->add_push_constant_block(name_buffer_copystr(
2880 &interface->name_buffer_, "PushConstantBlock", name_buffer_size, name_buffer_offset));
2881
2882 for (int uniform = 0; uniform < this->uniforms.size(); uniform++) {
2883 interface->add_uniform(
2884 name_buffer_copystr(&interface->name_buffer_,
2885 this->uniforms[uniform].name.c_str(),
2886 name_buffer_size,
2887 name_buffer_offset),
2888 to_mtl_type(this->uniforms[uniform].type),
2889 (this->uniforms[uniform].is_array) ? this->uniforms[uniform].array_elems : 1);
2890 }
2891
2892 /* Prepare Interface Uniform Blocks. */
2893 for (int uniform_block = 0; uniform_block < this->uniform_blocks.size(); uniform_block++) {
2894 interface->add_uniform_block(
2895 name_buffer_copystr(&interface->name_buffer_,
2896 this->uniform_blocks[uniform_block].name.c_str(),
2897 name_buffer_size,
2898 name_buffer_offset),
2899 this->uniform_blocks[uniform_block].slot,
2900 this->uniform_blocks[uniform_block].location,
2901 0,
2902 this->uniform_blocks[uniform_block].stage);
2903 }
2904
2905 /* Prepare Interface Storage Blocks. */
2906 for (int storage_block = 0; storage_block < this->storage_blocks.size(); storage_block++) {
2907 interface->add_storage_block(
2908 name_buffer_copystr(&interface->name_buffer_,
2909 this->storage_blocks[storage_block].name.c_str(),
2910 name_buffer_size,
2911 name_buffer_offset),
2912 this->storage_blocks[storage_block].slot,
2913 this->storage_blocks[storage_block].location,
2914 0,
2915 this->storage_blocks[storage_block].stage);
2916 }
2917
2918 /* Texture/sampler bindings to interface. */
2919 for (const MSLTextureResource &input_texture : this->texture_samplers) {
2920 /* Determine SSBO bind location for buffer-baked texture's data. */
2921 uint tex_buf_ssbo_location = -1;
2922 uint tex_buf_ssbo_id = input_texture.atomic_fallback_buffer_ssbo_id;
2923 if (tex_buf_ssbo_id != -1) {
2924 tex_buf_ssbo_location = this->storage_blocks[tex_buf_ssbo_id].location;
2925 }
2926
2927 interface->add_texture(name_buffer_copystr(&interface->name_buffer_,
2928 input_texture.name.c_str(),
2929 name_buffer_size,
2930 name_buffer_offset),
2931 input_texture.slot,
2932 input_texture.location,
2933 input_texture.get_texture_binding_type(),
2934 input_texture.get_sampler_format(),
2935 input_texture.is_texture_sampler,
2936 input_texture.stage,
2937 tex_buf_ssbo_location);
2938 }
2939
2940 /* Specialization Constants. */
2941 for (const MSLConstant &constant : this->constants) {
2942 interface->add_constant(name_buffer_copystr(
2943 &interface->name_buffer_, constant.name.c_str(), name_buffer_size, name_buffer_offset));
2944 }
2945
2946 /* Sampler Parameters. */
2947 interface->set_sampler_properties(
2952
2953 /* Map Metal bindings to standardized ShaderInput struct name/binding index. */
2954 interface->prepare_common_shader_inputs(info);
2955
2956 /* Resize name buffer to save some memory. */
2957 if (name_buffer_offset < name_buffer_size) {
2958 interface->name_buffer_ = (char *)MEM_reallocN(interface->name_buffer_, name_buffer_offset);
2959 }
2960
2961 return interface;
2962}
2963
2965{
2966 bool supports_native_atomics = MTLBackend::get_capabilities().supports_texture_atomics;
2967 /* Add Types as needed. */
2968 switch (this->type) {
2969 case ImageType::Float1D: {
2970 return "texture1d";
2971 }
2972 case ImageType::Float2D: {
2973 return "texture2d";
2974 }
2975 case ImageType::Float3D: {
2976 return "texture3d";
2977 }
2978 case ImageType::FloatCube: {
2979 return "texturecube";
2980 }
2981 case ImageType::Float1DArray: {
2982 return "texture1d_array";
2983 }
2984 case ImageType::Float2DArray: {
2985 return "texture2d_array";
2986 }
2987 case ImageType::FloatCubeArray: {
2988 return "texturecube_array";
2989 }
2990 case ImageType::FloatBuffer: {
2991 return "texture_buffer";
2992 }
2993 case ImageType::Depth2D: {
2994 return "depth2d";
2995 }
2996 case ImageType::Shadow2D: {
2997 return "depth2d";
2998 }
2999 case ImageType::Depth2DArray: {
3000 return "depth2d_array";
3001 }
3002 case ImageType::Shadow2DArray: {
3003 return "depth2d_array";
3004 }
3005 case ImageType::DepthCube: {
3006 return "depthcube";
3007 }
3008 case ImageType::ShadowCube: {
3009 return "depthcube";
3010 }
3011 case ImageType::DepthCubeArray: {
3012 return "depthcube_array";
3013 }
3014 case ImageType::ShadowCubeArray: {
3015 return "depthcube_array";
3016 }
3017 case ImageType::Int1D: {
3018 return "texture1d";
3019 }
3020 case ImageType::Int2D: {
3021 return "texture2d";
3022 }
3023 case ImageType::Int3D: {
3024 return "texture3d";
3025 }
3026 case ImageType::IntCube: {
3027 return "texturecube";
3028 }
3029 case ImageType::Int1DArray: {
3030 return "texture1d_array";
3031 }
3032 case ImageType::Int2DArray: {
3033 return "texture2d_array";
3034 }
3035 case ImageType::IntCubeArray: {
3036 return "texturecube_array";
3037 }
3038 case ImageType::IntBuffer: {
3039 return "texture_buffer";
3040 }
3041 case ImageType::Uint1D: {
3042 return "texture1d";
3043 }
3044 case ImageType::Uint2D: {
3045 return "texture2d";
3046 }
3047 case ImageType::Uint3D: {
3048 return "texture3d";
3049 }
3050 case ImageType::UintCube: {
3051 return "texturecube";
3052 }
3053 case ImageType::Uint1DArray: {
3054 return "texture1d_array";
3055 }
3056 case ImageType::Uint2DArray: {
3057 return "texture2d_array";
3058 }
3059 case ImageType::UintCubeArray: {
3060 return "texturecube_array";
3061 }
3062 case ImageType::UintBuffer: {
3063 return "texture_buffer";
3064 }
3065 /* If texture atomics are natively supported, we use the native texture type, otherwise all
3066 * other formats are implemented via texture2d. */
3067 case ImageType::AtomicInt2D:
3068 case ImageType::AtomicUint2D: {
3069 return "texture2d";
3070 }
3071 case ImageType::AtomicInt2DArray:
3072 case ImageType::AtomicUint2DArray: {
3073 if (supports_native_atomics) {
3074 return "texture2d_array";
3075 }
3076 else {
3077 return "texture2d";
3078 }
3079 }
3080 case ImageType::AtomicInt3D:
3081 case ImageType::AtomicUint3D: {
3082 if (supports_native_atomics) {
3083 return "texture3d";
3084 }
3085 else {
3086 return "texture2d";
3087 }
3088 }
3089
3090 default: {
3091 /* Unrecognized type. */
3093 return "ERROR";
3094 }
3095 };
3096}
3097
3099{
3100 bool supports_native_atomics = MTLBackend::get_capabilities().supports_texture_atomics;
3101 /* Add Types as needed. */
3102 switch (this->type) {
3103 case ImageType::Float1D: {
3104 return "_mtl_sampler_1d";
3105 }
3106 case ImageType::Float2D: {
3107 return "_mtl_sampler_2d";
3108 }
3109 case ImageType::Float3D: {
3110 return "_mtl_sampler_3d";
3111 }
3112 case ImageType::FloatCube: {
3113 return "_mtl_sampler_cube";
3114 }
3115 case ImageType::Float1DArray: {
3116 return "_mtl_sampler_1d_array";
3117 }
3118 case ImageType::Float2DArray: {
3119 return "_mtl_sampler_2d_array";
3120 }
3121 case ImageType::FloatCubeArray: {
3122 return "_mtl_sampler_cube_array";
3123 }
3124 case ImageType::FloatBuffer: {
3125 return "_mtl_sampler_buffer";
3126 }
3127 case ImageType::Depth2D: {
3128 return "_mtl_sampler_depth_2d";
3129 }
3130 case ImageType::Shadow2D: {
3131 return "_mtl_sampler_depth_2d";
3132 }
3133 case ImageType::Depth2DArray: {
3134 return "_mtl_sampler_depth_2d_array";
3135 }
3136 case ImageType::Shadow2DArray: {
3137 return "_mtl_sampler_depth_2d_array";
3138 }
3139 case ImageType::DepthCube: {
3140 return "_mtl_sampler_depth_cube";
3141 }
3142 case ImageType::ShadowCube: {
3143 return "_mtl_sampler_depth_cube";
3144 }
3145 case ImageType::DepthCubeArray: {
3146 return "_mtl_sampler_depth_cube_array";
3147 }
3148 case ImageType::ShadowCubeArray: {
3149 return "_mtl_sampler_depth_cube_array";
3150 }
3151 case ImageType::Int1D: {
3152 return "_mtl_sampler_1d";
3153 }
3154 case ImageType::Int2D: {
3155 return "_mtl_sampler_2d";
3156 }
3157 case ImageType::Int3D: {
3158 return "_mtl_sampler_3d";
3159 }
3160 case ImageType::IntCube: {
3161 return "_mtl_sampler_cube";
3162 }
3163 case ImageType::Int1DArray: {
3164 return "_mtl_sampler_1d_array";
3165 }
3166 case ImageType::Int2DArray: {
3167 return "_mtl_sampler_2d_array";
3168 }
3169 case ImageType::IntCubeArray: {
3170 return "_mtl_sampler_cube_array";
3171 }
3172 case ImageType::IntBuffer: {
3173 return "_mtl_sampler_buffer";
3174 }
3175 case ImageType::Uint1D: {
3176 return "_mtl_sampler_1d";
3177 }
3178 case ImageType::Uint2D: {
3179 return "_mtl_sampler_2d";
3180 }
3181 case ImageType::Uint3D: {
3182 return "_mtl_sampler_3d";
3183 }
3184 case ImageType::UintCube: {
3185 return "_mtl_sampler_cube";
3186 }
3187 case ImageType::Uint1DArray: {
3188 return "_mtl_sampler_1d_array";
3189 }
3190 case ImageType::Uint2DArray: {
3191 return "_mtl_sampler_2d_array";
3192 }
3193 case ImageType::UintCubeArray: {
3194 return "_mtl_sampler_cube_array";
3195 }
3196 case ImageType::UintBuffer: {
3197 return "_mtl_sampler_buffer";
3198 }
3199 /* If native texture atomics are unsupported, map types to fallback atomic structures which
3200 * contain a buffer pointer and metadata members for size and alignment. */
3201 case ImageType::AtomicInt2D:
3202 case ImageType::AtomicUint2D: {
3203 if (supports_native_atomics) {
3204 return "_mtl_sampler_2d";
3205 }
3206 else {
3207 return "_mtl_sampler_2d_atomic";
3208 }
3209 }
3210 case ImageType::AtomicInt3D:
3211 case ImageType::AtomicUint3D: {
3212 if (supports_native_atomics) {
3213 return "_mtl_sampler_3d";
3214 }
3215 else {
3216 return "_mtl_sampler_3d_atomic";
3217 }
3218 }
3219 case ImageType::AtomicInt2DArray:
3220 case ImageType::AtomicUint2DArray: {
3221 if (supports_native_atomics) {
3222 return "_mtl_sampler_2d_array";
3223 }
3224 else {
3225 return "_mtl_sampler_2d_array_atomic";
3226 }
3227 }
3228 default: {
3229 /* Unrecognized type. */
3231 return "ERROR";
3232 }
3233 };
3234}
3235
3237{
3238 /* Add Types as needed */
3239 switch (this->type) {
3240 /* Floating point return. */
3241 case ImageType::Float1D:
3242 case ImageType::Float2D:
3243 case ImageType::Float3D:
3244 case ImageType::FloatCube:
3245 case ImageType::Float1DArray:
3246 case ImageType::Float2DArray:
3247 case ImageType::FloatCubeArray:
3248 case ImageType::FloatBuffer:
3249 case ImageType::Depth2D:
3250 case ImageType::Shadow2D:
3251 case ImageType::Depth2DArray:
3252 case ImageType::Shadow2DArray:
3253 case ImageType::DepthCube:
3254 case ImageType::ShadowCube:
3255 case ImageType::DepthCubeArray:
3256 case ImageType::ShadowCubeArray: {
3257 return "float";
3258 }
3259 /* Integer return. */
3260 case ImageType::Int1D:
3261 case ImageType::Int2D:
3262 case ImageType::Int3D:
3263 case ImageType::IntCube:
3264 case ImageType::Int1DArray:
3265 case ImageType::Int2DArray:
3266 case ImageType::IntCubeArray:
3267 case ImageType::IntBuffer:
3268 case ImageType::AtomicInt2D:
3269 case ImageType::AtomicInt2DArray:
3270 case ImageType::AtomicInt3D: {
3271 return "int";
3272 }
3273
3274 /* Unsigned Integer return. */
3275 case ImageType::Uint1D:
3276 case ImageType::Uint2D:
3277 case ImageType::Uint3D:
3278 case ImageType::UintCube:
3279 case ImageType::Uint1DArray:
3280 case ImageType::Uint2DArray:
3281 case ImageType::UintCubeArray:
3282 case ImageType::UintBuffer:
3283 case ImageType::AtomicUint2D:
3284 case ImageType::AtomicUint2DArray:
3285 case ImageType::AtomicUint3D: {
3286 return "uint32_t";
3287 }
3288
3289 default: {
3290 /* Unrecognized type. */
3292 return "ERROR";
3293 }
3294 };
3295}
3296
3298{
3299 /* Add Types as needed */
3300 switch (this->type) {
3301 case ImageType::Float1D: {
3302 return GPU_TEXTURE_1D;
3303 }
3304 case ImageType::Float2D: {
3305 return GPU_TEXTURE_2D;
3306 }
3307 case ImageType::Float3D: {
3308 return GPU_TEXTURE_3D;
3309 }
3310 case ImageType::FloatCube: {
3311 return GPU_TEXTURE_CUBE;
3312 }
3313 case ImageType::Float1DArray: {
3314 return GPU_TEXTURE_1D_ARRAY;
3315 }
3316 case ImageType::Float2DArray: {
3317 return GPU_TEXTURE_2D_ARRAY;
3318 }
3319 case ImageType::FloatCubeArray: {
3321 }
3322 case ImageType::FloatBuffer: {
3323 return GPU_TEXTURE_BUFFER;
3324 }
3325 case ImageType::Depth2D: {
3326 return GPU_TEXTURE_2D;
3327 }
3328 case ImageType::Shadow2D: {
3329 return GPU_TEXTURE_2D;
3330 }
3331 case ImageType::Depth2DArray: {
3332 return GPU_TEXTURE_2D_ARRAY;
3333 }
3334 case ImageType::Shadow2DArray: {
3335 return GPU_TEXTURE_2D_ARRAY;
3336 }
3337 case ImageType::DepthCube: {
3338 return GPU_TEXTURE_CUBE;
3339 }
3340 case ImageType::ShadowCube: {
3341 return GPU_TEXTURE_CUBE;
3342 }
3343 case ImageType::DepthCubeArray: {
3345 }
3346 case ImageType::ShadowCubeArray: {
3348 }
3349 case ImageType::Int1D: {
3350 return GPU_TEXTURE_1D;
3351 }
3352 case ImageType::Int2D: {
3353 return GPU_TEXTURE_2D;
3354 }
3355 case ImageType::Int3D: {
3356 return GPU_TEXTURE_3D;
3357 }
3358 case ImageType::IntCube: {
3359 return GPU_TEXTURE_CUBE;
3360 }
3361 case ImageType::Int1DArray: {
3362 return GPU_TEXTURE_1D_ARRAY;
3363 }
3364 case ImageType::Int2DArray: {
3365 return GPU_TEXTURE_2D_ARRAY;
3366 }
3367 case ImageType::IntCubeArray: {
3369 }
3370 case ImageType::IntBuffer: {
3371 return GPU_TEXTURE_BUFFER;
3372 }
3373 case ImageType::Uint1D: {
3374 return GPU_TEXTURE_1D;
3375 }
3376 case ImageType::Uint2D:
3377 case ImageType::AtomicUint2D:
3378 case ImageType::AtomicInt2D: {
3379 return GPU_TEXTURE_2D;
3380 }
3381 case ImageType::Uint3D:
3382 case ImageType::AtomicUint3D:
3383 case ImageType::AtomicInt3D: {
3384 return GPU_TEXTURE_3D;
3385 }
3386 case ImageType::UintCube: {
3387 return GPU_TEXTURE_CUBE;
3388 }
3389 case ImageType::Uint1DArray: {
3390 return GPU_TEXTURE_1D_ARRAY;
3391 }
3392 case ImageType::Uint2DArray:
3393 case ImageType::AtomicUint2DArray:
3394 case ImageType::AtomicInt2DArray: {
3395 return GPU_TEXTURE_2D_ARRAY;
3396 }
3397 case ImageType::UintCubeArray: {
3399 }
3400 case ImageType::UintBuffer: {
3401 return GPU_TEXTURE_BUFFER;
3402 }
3403 default: {
3405 return GPU_TEXTURE_2D;
3406 }
3407 };
3408}
3409
3411{
3412 switch (this->type) {
3413 case ImageType::FloatBuffer:
3414 case ImageType::Float1D:
3415 case ImageType::Float1DArray:
3416 case ImageType::Float2D:
3417 case ImageType::Float2DArray:
3418 case ImageType::Float3D:
3419 case ImageType::FloatCube:
3420 case ImageType::FloatCubeArray:
3422 case ImageType::IntBuffer:
3423 case ImageType::Int1D:
3424 case ImageType::Int1DArray:
3425 case ImageType::Int2D:
3426 case ImageType::Int2DArray:
3427 case ImageType::Int3D:
3428 case ImageType::IntCube:
3429 case ImageType::IntCubeArray:
3430 case ImageType::AtomicInt2D:
3431 case ImageType::AtomicInt3D:
3432 case ImageType::AtomicInt2DArray:
3433 return GPU_SAMPLER_TYPE_INT;
3434 case ImageType::UintBuffer:
3435 case ImageType::Uint1D:
3436 case ImageType::Uint1DArray:
3437 case ImageType::Uint2D:
3438 case ImageType::Uint2DArray:
3439 case ImageType::Uint3D:
3440 case ImageType::UintCube:
3441 case ImageType::UintCubeArray:
3442 case ImageType::AtomicUint2D:
3443 case ImageType::AtomicUint3D:
3444 case ImageType::AtomicUint2DArray:
3445 return GPU_SAMPLER_TYPE_UINT;
3446 case ImageType::Shadow2D:
3447 case ImageType::Shadow2DArray:
3448 case ImageType::ShadowCube:
3449 case ImageType::ShadowCubeArray:
3450 case ImageType::Depth2D:
3451 case ImageType::Depth2DArray:
3452 case ImageType::DepthCube:
3453 case ImageType::DepthCubeArray:
3455 default:
3457 }
3459}
3460
3462
3463} // namespace blender::gpu
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
KDTree *BLI_kdtree_nd_ new(unsigned int nodes_len_capacity)
Definition kdtree_impl.h:98
MINLINE uint max_uu(uint a, uint b)
MINLINE int max_ii(int a, int b)
unsigned int uint
#define STRINGIFY(x)
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
int GPU_max_samplers()
int GPU_max_textures_vert()
static constexpr int GPU_VERT_ATTR_MAX_LEN
#define MEM_reallocN(vmemh, len)
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr bool is_empty() const
constexpr const char * c_str() 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
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
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< MSLUniform > uniforms
std::string generate_msl_compute_entry_stub(const shader::ShaderCreateInfo &info)
static MTLCapabilities & get_capabilities()
static MTLCapabilities capabilities
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
void set_vertex_function_name(NSString *vetex_function_name)
void shader_source_from_msl(NSString *input_vertex_source, NSString *input_fragment_source)
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)
StringRefNull name_get() const
#define str(s)
uint pos
#define inout
#define input
#define out
#define output
int count
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
static void error(const char *str)
#define MTL_MAX_BUFFER_BINDINGS
#define MTL_MAX_TEXTURE_SLOTS
#define MTL_MAX_DEFAULT_SAMPLERS
#define MTL_LOG_WARNING(info,...)
Definition mtl_debug.hh:42
#define MTL_LOG_ERROR(info,...)
Definition mtl_debug.hh:34
#define shader_debug_printf(...)
Definition mtl_shader.hh:49
#define MTL_SHADER_SPECIALIZATION_CONSTANT_BASE_ID
Definition mtl_shader.hh:53
#define FRAGMENT_TILE_IN_STRUCT_NAME
char datatoc_mtl_shader_shared_hh[]
#define FRAGMENT_OUT_STRUCT_NAME
@ MTL_DATATYPE_INT1010102_NORM
uint mtl_get_data_type_size(MTLInterfaceDataType type)
BLI_INLINE int to_component_count(const Type &type)
uint get_shader_stage_index(ShaderStage stage)
std::mutex msl_patch_default_lock
const char * to_string(ShaderStage stage)
Definition mtl_shader.mm:51
bool is_matrix_type(const std::string &type)
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)
MSLFragmentOutputAttribute MSLFragmentTileInputAttribute
constexpr size_t const_strlen(const char *str)
static void shared_variable_assign(const shader::ShaderCreateInfo &info, std::stringstream &ss)
static void extract_and_replace_clipping_distances(std::string &vertex_source, MSLGeneratorInterface &msl_iface)
static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &res)
const char * get_shader_stage_instance_name(ShaderStage stage)
static void generate_compilation_constant_declarations(const shader::ShaderCreateInfo *info, std::stringstream &ss)
MTLVertexFormat mtl_datatype_to_vertex_type(MTLInterfaceDataType type)
const char * to_string_msl(const shader::Interpolation &interp)
static char parameter_delimiter(bool &is_first_parameter)
static void split_array(StringRefNull input, std::string &r_name, std::string &r_array)
const char * get_stage_class_name(ShaderStage stage)
static void shared_variable_args(const shader::ShaderCreateInfo &info, std::stringstream &ss)
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)
std::string get_matrix_subtype(const std::string &type)
std::string get_attribute_conversion_function(bool *uses_conversion, const shader::Type &type)
bool is_builtin_type(std::string type)
static void generate_specialization_constant_declarations(const shader::ShaderCreateInfo *info, std::stringstream &ss)
static void shared_variable_declare(const shader::ShaderCreateInfo &info, std::stringstream &ss)
static void shared_variable_pass(const shader::ShaderCreateInfo &info, std::stringstream &ss)
const char * name
std::string get_msl_typestring_wrapper(bool is_addr) const
GPUTextureType get_texture_binding_type() const
GPUSamplerFormat get_sampler_format() const
std::string get_msl_typestring(bool is_addr) const
Describe inputs & outputs, stage interfaces, resources and sources of a shader. If all data is correc...
Vector< StageInterfaceInfo * > vertex_out_interfaces_
Vector< CompilationConstant, 0 > compilation_constants_
Vector< SpecializationConstant > specialization_constants_
i
Definition text_draw.cc:230
uint len