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