Welcome to mirror list, hosted at ThFree Co, Russian Federation.

mtl_shader_generator.hh « metal « gpu « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 43890ca0170131bb8bb81f29a93d3b6efe110267 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
/* SPDX-License-Identifier: GPL-2.0-or-later */

#pragma once

#include "gpu_shader_create_info.hh"
#include "gpu_shader_private.hh"

/** -- Metal Shader Generator for GLSL -> MSL conversion --
 *
 * The Metal shader generator class is used as a conversion utility for generating
 * a compatible MSL shader from a source GLSL shader. There are several steps
 * involved in creating a shader, and structural changes which enable the source
 * to function in the same way.
 *
 * 1) Extraction and conversion of shaders input's and output's to their Metal-compatible
 *    version. This is a subtle data transformation from GPUShaderCreateInfo, allowing
 *    for Metal-specific parameters.
 *
 * 2) Determine usage of shader features such as GL global variable usage, depth write output,
 *    clip distances, multilayered rendering, barycentric coordinates etc;
 *
 * 3) Generate MSL shader.
 *
 * 4) Populate #MTLShaderInterface, describing input/output structure, bind-points, buffer size and
 *    alignment, shader feature usage etc; Everything required by the Metal back-end to
 *    successfully enable use of shaders and GPU back-end features.
 *
 *
 *
 * For each shading stage, we generate an MSL shader following these steps:
 *
 * 1) Output custom shader defines describing modes e.g. whether we are using
 *    sampler bindings or argument buffers; at the top of the shader.
 *
 * 2) Inject common Metal headers.
 *    - `mtl_shader_defines.msl` is used to map GLSL functions to MSL.
 *    - `mtl_shader_common.msl` is added to ALL MSL shaders to provide
 *      common functionality required by the back-end. This primarily
 *      contains function-constant hooks, used in PSO generation.
 *
 * 3) Create a class Scope which wraps the GLSL shader. This is used to
 *    create a global per-thread scope around the shader source, to allow
 *    access to common shader members (GLSL globals, shader inputs/outputs etc)
 *
 * 4) Generate shader interface structs and populate local members where required for:
 *    - `VertexInputs`
 *    - `VertexOutputs`
 *    - `Uniforms`
 *    - `Uniform Blocks`
 *    - `textures` ;
 *    etc;
 *
 * 5) Inject GLSL source.
 *
 * 6) Generate MSL shader entry point function. Every Metal shader must have a
 *    vertex/fragment/kernel entry-point, which contains the function binding table.
 *    This is where bindings are specified and passed into the shader.
 *
 *    For converted shaders, the MSL entry-point will also instantiate a shader
 *    class per thread, and pass over bound resource references into the class.
 *
 *    Finally, the shaders "main()" method will be called, and outputs are copied.
 *
 *    NOTE: For position outputs, the default output position will be converted to
 *    the Metal coordinate space, which involves flipping the Y coordinate and
 *    re-mapping the depth range between 0 and 1, as with Vulkan.
 *
 *
 * The final shader structure looks as follows:
 *
 * \code{.cc}
 * -- Shader defines --
 * #define USE_ARGUMENT_BUFFER_FOR_SAMPLERS 0
 * ... etc ...;
 *
 * class MetalShaderVertexImp {
 *
 *  -- Common shader interface structs --
 *  struct VertexIn {
 *    vec4 pos [[attribute(0)]]
 *  }
 *  struct VertexOut {...}
 *  struct PushConstantBlock {...}
 *  struct drw_Globals {...}
 *  ...
 *
 *   -- GLSL source code --
 *   ...
 * };
 *
 * vertex MetalShaderVertexImp::VertexOut vertex_function_entry(
 *   MetalShaderVertexImp::VertexIn v_in [[stage_in]],
 *   constant PushConstantBlock& globals [[buffer(MTL_uniform_buffer_base_index)]]) {
 *
 *   MetalShaderVertexImp impl;
 *   -- Copy input members into impl instance --
 *   -- Execute GLSL main function --
 *   impl.main();
 *
 *   -- Copy outputs and return --
 *   MetalShaderVertexImp::VertexOut out;
 *   out.pos = impl.pos;
 *   -- transform position to Metal coordinate system --
 *   return v_out;
 * }
 * \endcode
 *
 * -- SSBO-vertex-fetchmode --
 *
 * SSBO-vertex-fetchmode is a special option wherein vertex buffers are bound directly
 * as buffers in the shader, rather than using the VertexDescriptor and [[stage_in]] vertex
 * assembly.
 *
 * The purpose of this mode is to enable random-access reading of all vertex data. This is
 * particularly useful for efficiently converting geometry shaders to Metal shading language,
 * as these techniques are not supported natively in Metal.
 *
 * Geometry shaders can be re-created by firing off a vertex shader with the desired number of
 * total output vertices. Each vertex can then read whichever input attributes it needs to
 * achieve the output result.
 * This manual reading is also used to provide support for GPU_provoking_vertex, wherein the
 * output vertex for flat shading needs to change. In these cases, the manual vertex assembly
 * can flip which vertices are read within the primitive.
 *
 * From an efficiency perspective, this is more GPU-friendly than geometry shading, due to improved
 * parallelism throughout the whole pipe, and for Apple hardware specifically, there is no
 * significant performance loss from manual vertex assembly vs under-the-hood assembly.
 *
 * This mode works by passing the required vertex descriptor information into the shader
 * as uniform data, describing the type, stride, offset, step-mode and buffer index of each
 * attribute, such that the shader SSBO-vertex-fetch utility functions know how to extract data.
 *
 * This also works with indexed rendering,
 * by similarly binding the index buffer as a manual buffer.
 *
 * When this mode is used, the code generation and shader interface generation varies to
 * accommodate the required features.
 *
 * This mode can be enabled in a shader with:
 *
 * `#pragma USE_SSBO_VERTEX_FETCH(TriangleList/LineList, output_vertex_count_per_input_primitive)`
 *
 * This mirrors the geometry shader interface `layout(triangle_strip, max_vertices = 3) out;`
 */

/* SSBO vertex fetch attribute uniform parameter names.
 * These uniforms are used to pass the information
 * required to perform manual vertex assembly within
 * the vertex shader.
 * Each vertex attribute requires a number of properties
 * in order to correctly extract data from the bound vertex
 * buffers. */
#ifndef NDEBUG
/* Global. */
#  define UNIFORM_SSBO_USES_INDEXED_RENDERING_STR "uniform_ssbo_uses_indexed_rendering"
#  define UNIFORM_SSBO_INDEX_MODE_U16_STR "uniform_ssbo_index_mode_u16"
#  define UNIFORM_SSBO_INPUT_PRIM_TYPE_STR "uniform_ssbo_input_prim_type"
#  define UNIFORM_SSBO_INPUT_VERT_COUNT_STR "uniform_ssbo_input_vert_count"
/* Per-attribute. */
#  define UNIFORM_SSBO_OFFSET_STR "uniform_ssbo_offset_"
#  define UNIFORM_SSBO_STRIDE_STR "uniform_ssbo_stride_"
#  define UNIFORM_SSBO_FETCHMODE_STR "uniform_ssbo_fetchmode_"
#  define UNIFORM_SSBO_VBO_ID_STR "uniform_ssbo_vbo_id_"
#  define UNIFORM_SSBO_TYPE_STR "uniform_ssbo_type_"
#else
/* Global. */
#  define UNIFORM_SSBO_USES_INDEXED_RENDERING_STR "_ir"
#  define UNIFORM_SSBO_INDEX_MODE_U16_STR "_mu"
#  define UNIFORM_SSBO_INPUT_PRIM_TYPE_STR "_pt"
#  define UNIFORM_SSBO_INPUT_VERT_COUNT_STR "_vc"
/* Per-attribute. */
#  define UNIFORM_SSBO_OFFSET_STR "_so"
#  define UNIFORM_SSBO_STRIDE_STR "_ss"
#  define UNIFORM_SSBO_FETCHMODE_STR "_sf"
#  define UNIFORM_SSBO_VBO_ID_STR "_sv"
#  define UNIFORM_SSBO_TYPE_STR "_st"
#endif

namespace blender::gpu {

struct MSLUniform {
  shader::Type type;
  std::string name;
  bool is_array;
  int array_elems;
  ShaderStage stage;

  MSLUniform(shader::Type uniform_type,
             std::string uniform_name,
             bool is_array_type,
             uint32_t num_elems = 1)
      : type(uniform_type), name(uniform_name), is_array(is_array_type), array_elems(num_elems)
  {
  }

  bool operator==(const MSLUniform &right) const
  {
    return (type == right.type && name == right.name && is_array == right.is_array &&
            array_elems == right.array_elems);
  }
};

struct MSLUniformBlock {
  std::string type_name;
  std::string name;
  ShaderStage stage;
  bool is_array;

  bool operator==(const MSLUniformBlock &right) const
  {
    return (type_name == right.type_name && name == right.name);
  }
};

enum MSLTextureSamplerAccess {
  TEXTURE_ACCESS_NONE = 0,
  TEXTURE_ACCESS_SAMPLE,
  TEXTURE_ACCESS_READ,
  TEXTURE_ACCESS_WRITE,
  TEXTURE_ACCESS_READWRITE,
};

struct MSLTextureSampler {
  ShaderStage stage;
  shader::ImageType type;
  std::string name;
  MSLTextureSamplerAccess access;
  uint location;

  eGPUTextureType get_texture_binding_type() const;

  void resolve_binding_indices();

  MSLTextureSampler(ShaderStage in_stage,
                    shader::ImageType in_sampler_type,
                    std::string in_sampler_name,
                    MSLTextureSamplerAccess in_access,
                    uint in_location)
      : stage(in_stage),
        type(in_sampler_type),
        name(in_sampler_name),
        access(in_access),
        location(in_location)
  {
  }

  bool operator==(const MSLTextureSampler &right) const
  {
    /* We do not compare stage as we want to avoid duplication of resources used across multiple
     * stages. */
    return (type == right.type && name == right.name && access == right.access);
  }

  std::string get_msl_access_str() const
  {
    switch (access) {
      case TEXTURE_ACCESS_SAMPLE:
        return "access::sample";
      case TEXTURE_ACCESS_READ:
        return "access::read";
      case TEXTURE_ACCESS_WRITE:
        return "access::write";
      case TEXTURE_ACCESS_READWRITE:
        return "access::read_write";
      default:
        BLI_assert(false);
        return "";
    }
    return "";
  }

  /* Get typestring for wrapped texture class members.
   * wrapper struct type contains combined texture and sampler, templated
   * against the texture type.
   * See `COMBINED_SAMPLER_TYPE` in `mtl_shader_defines.msl`. */
  std::string get_msl_typestring_wrapper(bool is_addr) const
  {
    std::string str;
    str = this->get_msl_wrapper_type_str() + "<" + this->get_msl_return_type_str() + "," +
          this->get_msl_access_str() + ">" + ((is_addr) ? "* " : " ") + this->name;
    return str;
  }

  /* Get raw texture typestring -- used in entry-point function argument table. */
  std::string get_msl_typestring(bool is_addr) const
  {
    std::string str;
    str = this->get_msl_texture_type_str() + "<" + this->get_msl_return_type_str() + "," +
          this->get_msl_access_str() + ">" + ((is_addr) ? "* " : " ") + this->name;
    return str;
  }

  std::string get_msl_return_type_str() const;
  std::string get_msl_texture_type_str() const;
  std::string get_msl_wrapper_type_str() const;
};

struct MSLVertexInputAttribute {
  /* layout_location of -1 means unspecified and will
   * be populated manually. */
  int layout_location;
  shader::Type type;
  std::string name;

  bool operator==(const MSLVertexInputAttribute &right) const
  {
    return (layout_location == right.layout_location && type == right.type && name == right.name);
  }
};

struct MSLVertexOutputAttribute {
  std::string type;
  std::string name;
  /* Instance name specified if attributes belong to a struct. */
  std::string instance_name;
  /* Interpolation qualifier can be any of smooth (default), flat, no_perspective. */
  std::string interpolation_qualifier;
  bool is_array;
  int array_elems;

  bool operator==(const MSLVertexOutputAttribute &right) const
  {
    return (type == right.type && name == right.name &&
            interpolation_qualifier == right.interpolation_qualifier &&
            is_array == right.is_array && array_elems == right.array_elems);
  }
  std::string get_mtl_interpolation_qualifier() const
  {
    if (interpolation_qualifier == "" || interpolation_qualifier == "smooth") {
      return "";
    }
    else if (interpolation_qualifier == "flat") {
      return " [[flat]]";
    }
    else if (interpolation_qualifier == "noperspective") {
      return " [[center_no_perspective]]";
    }
    return "";
  }
};

struct MSLFragmentOutputAttribute {
  /* Explicit output binding location N for [[color(N)]] -1 = unspecified. */
  int layout_location;
  /* Output index for dual source blending. -1 = unspecified. */
  int layout_index;
  shader::Type type;
  std::string name;

  bool operator==(const MSLFragmentOutputAttribute &right) const
  {
    return (layout_location == right.layout_location && type == right.type && name == right.name &&
            layout_index == right.layout_index);
  }
};

class MSLGeneratorInterface {
  static char *msl_patch_default;

 public:
  /** Shader stage input/output binding information.
   * Derived from shader source reflection or GPUShaderCreateInfo. */
  blender::Vector<MSLUniformBlock> uniform_blocks;
  blender::Vector<MSLUniform> uniforms;
  blender::Vector<MSLTextureSampler> texture_samplers;
  blender::Vector<MSLVertexInputAttribute> vertex_input_attributes;
  blender::Vector<MSLVertexOutputAttribute> vertex_output_varyings;
  /* Should match vertex outputs, but defined separately as
   * some shader permutations will not utilize all inputs/outputs.
   * Final shader uses the intersection between the two sets. */
  blender::Vector<MSLVertexOutputAttribute> fragment_input_varyings;
  blender::Vector<MSLFragmentOutputAttribute> fragment_outputs;
  /* Transform feedback interface. */
  blender::Vector<MSLVertexOutputAttribute> vertex_output_varyings_tf;
  /* Clip Distances. */
  blender::Vector<std::string> clip_distances;

  /** GL Global usage. */
  /* Whether GL position is used, or an alternative vertex output should be the default. */
  bool uses_gl_Position;
  /* Whether gl_FragColor is used, or whether an alternative fragment output
   * should be the default. */
  bool uses_gl_FragColor;
  /* Whether gl_PointCoord is used in the fragment shader. If so,
   * we define float2 gl_PointCoord [[point_coord]]. */
  bool uses_gl_PointCoord;
  /* Writes out to gl_PointSize in the vertex shader output. */
  bool uses_gl_PointSize;
  bool uses_gl_VertexID;
  bool uses_gl_InstanceID;
  bool uses_gl_BaseInstanceARB;
  bool uses_gl_FrontFacing;
  /* Sets the output render target array index when using multilayered rendering. */
  bool uses_gl_FragDepth;
  bool uses_mtl_array_index_;
  bool uses_transform_feedback;
  bool uses_barycentrics;

  /* Parameters. */
  shader::DepthWrite depth_write;

  /* Shader buffer bind indices for argument buffers. */
  int sampler_argument_buffer_bind_index[2] = {-1, -1};

  /*** SSBO Vertex fetch mode. ***/
  /* Indicates whether to pass in Vertex Buffer's as a regular buffers instead of using vertex
   * assembly in the PSO descriptor. Enabled with special pragma. */
  bool uses_ssbo_vertex_fetch_mode;

 private:
  /* Parent shader instance. */
  MTLShader &parent_shader_;

  /* If prepared from Create info. */
  const shader::ShaderCreateInfo *create_info_;

 public:
  MSLGeneratorInterface(MTLShader &shader) : parent_shader_(shader){};

  /** Prepare MSLGeneratorInterface from create-info. **/
  void prepare_from_createinfo(const shader::ShaderCreateInfo *info);

  /* When SSBO Vertex Fetch mode is used, uniforms are used to pass on the required information
   * about vertex attribute bindings, in order to perform manual vertex assembly and random-access
   * vertex lookup throughout the bound VBOs.
   *
   * Some parameters are global for the shader, others change with the currently bound
   * VertexBuffers, and their format, as they do with regular GPUBatch's.
   *
   * (Where ##attr is the attributes name)
   *  uniform_ssbo_stride_##attr  -- Representing the stride between elements of attribute(attr)
   *  uniform_ssbo_offset_##attr  -- Representing the base offset within the vertex
   *  uniform_ssbo_fetchmode_##attr -- Whether using per-vertex fetch or per-instance fetch
   * (0=vert, 1=inst) uniform_ssbo_vbo_id_##attr -- index of the vertex buffer within which the
   * data for this attribute is contained uniform_ssbo_type_##attr - The type of data in the
   * currently bound buffer -- Could be a mismatch with the Officially reported type. */
  void prepare_ssbo_vertex_fetch_uniforms();

  /* Samplers. */
  bool use_argument_buffer_for_samplers() const;
  uint32_t num_samplers_for_stage(ShaderStage stage) const;

  /* Returns the bind index, relative to MTL_uniform_buffer_base_index. */
  uint32_t get_sampler_argument_buffer_bind_index(ShaderStage stage);

  /* Code generation utility functions. */
  std::string generate_msl_uniform_structs(ShaderStage shader_stage);
  std::string generate_msl_vertex_in_struct();
  std::string generate_msl_vertex_out_struct(ShaderStage shader_stage);
  std::string generate_msl_vertex_transform_feedback_out_struct(ShaderStage shader_stage);
  std::string generate_msl_fragment_out_struct();
  std::string generate_msl_vertex_inputs_string();
  std::string generate_msl_fragment_inputs_string();
  std::string generate_msl_vertex_entry_stub();
  std::string generate_msl_fragment_entry_stub();
  std::string generate_msl_global_uniform_population(ShaderStage stage);
  std::string generate_ubo_block_macro_chain(MSLUniformBlock block);
  std::string generate_msl_uniform_block_population(ShaderStage stage);
  std::string generate_msl_vertex_attribute_input_population();
  std::string generate_msl_vertex_output_population();
  std::string generate_msl_vertex_output_tf_population();
  std::string generate_msl_fragment_input_population();
  std::string generate_msl_fragment_output_population();
  std::string generate_msl_uniform_undefs(ShaderStage stage);
  std::string generate_ubo_block_undef_chain(ShaderStage stage);
  std::string generate_msl_texture_vars(ShaderStage shader_stage);
  void generate_msl_textures_input_string(std::stringstream &out, ShaderStage stage);
  void generate_msl_uniforms_input_string(std::stringstream &out, ShaderStage stage);

  /* Location is not always specified, so this will resolve outstanding locations. */
  void resolve_input_attribute_locations();
  void resolve_fragment_output_locations();

  /* Create shader interface for converted GLSL shader. */
  MTLShaderInterface *bake_shader_interface(const char *name);

  /* Fetch combined shader source header. */
  char *msl_patch_default_get();

  MEM_CXX_CLASS_ALLOC_FUNCS("MSLGeneratorInterface");
};

inline std::string get_stage_class_name(ShaderStage stage)
{
  switch (stage) {
    case ShaderStage::VERTEX:
      return "MTLShaderVertexImpl";
    case ShaderStage::FRAGMENT:
      return "MTLShaderFragmentImpl";
    default:
      BLI_assert_unreachable();
      return "";
  }
  return "";
}

inline bool is_builtin_type(std::string type)
{
  /* Add Types as needed. */
  /* TODO(Metal): Consider replacing this with a switch and constexpr hash and switch.
   * Though most efficient and maintainable approach to be determined. */
  static std::map<std::string, eMTLDataType> glsl_builtin_types = {
      {"float", MTL_DATATYPE_FLOAT},
      {"vec2", MTL_DATATYPE_FLOAT2},
      {"vec3", MTL_DATATYPE_FLOAT3},
      {"vec4", MTL_DATATYPE_FLOAT4},
      {"int", MTL_DATATYPE_INT},
      {"ivec2", MTL_DATATYPE_INT2},
      {"ivec3", MTL_DATATYPE_INT3},
      {"ivec4", MTL_DATATYPE_INT4},
      {"uint32_t", MTL_DATATYPE_UINT},
      {"uvec2", MTL_DATATYPE_UINT2},
      {"uvec3", MTL_DATATYPE_UINT3},
      {"uvec4", MTL_DATATYPE_UINT4},
      {"mat3", MTL_DATATYPE_FLOAT3x3},
      {"mat4", MTL_DATATYPE_FLOAT4x4},
      {"bool", MTL_DATATYPE_INT},
      {"uchar", MTL_DATATYPE_UCHAR},
      {"uchar2", MTL_DATATYPE_UCHAR2},
      {"uchar2", MTL_DATATYPE_UCHAR3},
      {"uchar4", MTL_DATATYPE_UCHAR4},
      {"vec3_1010102_Unorm", MTL_DATATYPE_UINT1010102_NORM},
      {"vec3_1010102_Inorm", MTL_DATATYPE_INT1010102_NORM},
  };
  return (glsl_builtin_types.find(type) != glsl_builtin_types.end());
}

inline bool is_matrix_type(const std::string &type)
{
  /* Matrix type support. Add types as necessary. */
  return (type == "mat4");
}

inline bool is_matrix_type(const shader::Type &type)
{
  /* Matrix type support. Add types as necessary. */
  return (type == shader::Type::MAT4 || type == shader::Type::MAT3);
}

inline int get_matrix_location_count(const std::string &type)
{
  /* Matrix type support. Add types as necessary. */
  if (type == "mat4") {
    return 4;
  }
  if (type == "mat3") {
    return 3;
  }
  return 1;
}

inline int get_matrix_location_count(const shader::Type &type)
{
  /* Matrix type support. Add types as necessary. */
  if (type == shader::Type::MAT4) {
    return 4;
  }
  else if (type == shader::Type::MAT3) {
    return 3;
  }
  return 1;
}

inline std::string get_matrix_subtype(const std::string &type)
{
  if (type == "mat4") {
    return "vec4";
  }
  return type;
}

inline shader::Type get_matrix_subtype(const shader::Type &type)
{
  if (type == shader::Type::MAT4) {
    return shader::Type::VEC4;
  }
  if (type == shader::Type::MAT3) {
    return shader::Type::VEC3;
  }
  return type;
}

inline std::string get_attribute_conversion_function(bool *uses_conversion,
                                                     const shader::Type &type)
{
  /* NOTE(Metal): Add more attribute types as required. */
  if (type == shader::Type::FLOAT) {
    *uses_conversion = true;
    return "internal_vertex_attribute_convert_read_float";
  }
  else if (type == shader::Type::VEC2) {
    *uses_conversion = true;
    return "internal_vertex_attribute_convert_read_float2";
  }
  else if (type == shader::Type::VEC3) {
    *uses_conversion = true;
    return "internal_vertex_attribute_convert_read_float3";
  }
  else if (type == shader::Type::VEC4) {
    *uses_conversion = true;
    return "internal_vertex_attribute_convert_read_float4";
  }
  *uses_conversion = false;
  return "";
}

inline const char *to_string(const shader::PrimitiveOut &layout)
{
  switch (layout) {
    case shader::PrimitiveOut::POINTS:
      return "points";
    case shader::PrimitiveOut::LINE_STRIP:
      return "line_strip";
    case shader::PrimitiveOut::TRIANGLE_STRIP:
      return "triangle_strip";
    default:
      BLI_assert(false);
      return "unknown";
  }
}

inline const char *to_string(const shader::PrimitiveIn &layout)
{
  switch (layout) {
    case shader::PrimitiveIn::POINTS:
      return "points";
    case shader::PrimitiveIn::LINES:
      return "lines";
    case shader::PrimitiveIn::LINES_ADJACENCY:
      return "lines_adjacency";
    case shader::PrimitiveIn::TRIANGLES:
      return "triangles";
    case shader::PrimitiveIn::TRIANGLES_ADJACENCY:
      return "triangles_adjacency";
    default:
      BLI_assert(false);
      return "unknown";
  }
}

inline const char *to_string(const shader::Interpolation &interp)
{
  switch (interp) {
    case shader::Interpolation::SMOOTH:
      return "smooth";
    case shader::Interpolation::FLAT:
      return "flat";
    case shader::Interpolation::NO_PERSPECTIVE:
      return "noperspective";
    default:
      BLI_assert(false);
      return "unkown";
  }
}

inline const char *to_string_msl(const shader::Interpolation &interp)
{
  switch (interp) {
    case shader::Interpolation::SMOOTH:
      return "[[smooth]]";
    case shader::Interpolation::FLAT:
      return "[[flat]]";
    case shader::Interpolation::NO_PERSPECTIVE:
      return "[[center_no_perspective]]";
    default:
      return "";
  }
}

inline const char *to_string(const shader::Type &type)
{
  switch (type) {
    case shader::Type::FLOAT:
      return "float";
    case shader::Type::VEC2:
      return "vec2";
    case shader::Type::VEC3:
      return "vec3";
    case shader::Type::VEC3_101010I2:
      return "vec3_1010102_Inorm";
    case shader::Type::VEC4:
      return "vec4";
    case shader::Type::MAT3:
      return "mat3";
    case shader::Type::MAT4:
      return "mat4";
    case shader::Type::UINT:
      return "uint32_t";
    case shader::Type::UVEC2:
      return "uvec2";
    case shader::Type::UVEC3:
      return "uvec3";
    case shader::Type::UVEC4:
      return "uvec4";
    case shader::Type::INT:
      return "int";
    case shader::Type::IVEC2:
      return "ivec2";
    case shader::Type::IVEC3:
      return "ivec3";
    case shader::Type::IVEC4:
      return "ivec4";
    case shader::Type::BOOL:
      return "bool";
    case shader::Type::UCHAR:
      return "uchar";
    case shader::Type::UCHAR2:
      return "uchar2";
    case shader::Type::UCHAR3:
      return "uchar3";
    case shader::Type::UCHAR4:
      return "uchar4";
    case shader::Type::CHAR:
      return "char";
    case shader::Type::CHAR2:
      return "char2";
    case shader::Type::CHAR3:
      return "char3";
    case shader::Type::CHAR4:
      return "char4";
    default:
      BLI_assert(false);
      return "unkown";
  }
}

}  // namespace blender::gpu