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

mtl_shader_interface.hh « metal « gpu « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 0f04c04031d4b31924d1aeec8b39c1e1d220ef9f (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
/* SPDX-License-Identifier: GPL-2.0-or-later */

/** \file
 * \ingroup gpu
 */

#pragma once

#include "MEM_guardedalloc.h"

#include "BLI_vector.hh"

#include "gpu_shader_interface.hh"
#include "mtl_capabilities.hh"
#include "mtl_shader_interface_type.hh"

#include "GPU_common.h"
#include "GPU_common_types.h"
#include "GPU_texture.h"
#include "gpu_texture_private.hh"
#include <Metal/Metal.h>
#include <functional>

namespace blender::gpu {

/* MTLShaderInterface describes the layout and properties of a given shader,
 * including input and output bindings, and any special properties or modes
 * that the shader may require.
 *
 * -- Shader input/output bindings --
 *
 * We require custom datastructures for the binding information in Metal.
 * This is because certain bindings contain and require more information to
 * be stored than can be tracked solely within the `ShaderInput` struct.
 * e.g. data sizes and offsets.
 *
 * Upon interface completion, `prepare_common_shader_inputs` is used to
 * populate the global ShaderInput* array to enable correct functionality
 * of shader binding location lookups. These returned locations act as indices
 * into the arrays stored here in the MTLShaderInterace, such that extraction
 * of required information can be performed within the backend.
 *
 * e.g. `int loc = GPU_shader_get_uniform(...)`
 * `loc` will match the index into the MTLShaderUniform uniforms_[] array
 * to fetch the required Metal specific information.
 *
 *
 *
 * -- Argument Buffers and Argument Encoders --
 *
 * We can use ArgumentBuffers (AB's) in Metal to extend the resource bind limitations
 * by providing bindless support.
 *
 * Argument Buffers are used for sampler bindings when the builtin
 * sampler limit of 16 is exceeded, as in all cases for Blender,
 * each individual texture is associated with a given sampler, and this
 * lower limit would otherwise reduce the total availability of textures
 * used in shaders.
 *
 * In future, argument buffers may be extended to support other resource
 * types, if overall bind limits are ever increased within Blender.
 *
 * The ArgumentEncoder cache used to store the generated ArgumentEncoders for a given
 * shader permutation. The ArgumentEncoder is the resource used to write resource binding
 * information to a specified buffer, and is unique to the shader's resource interface.
 */

enum class ShaderStage : uint32_t {
  VERTEX = 1 << 0,
  FRAGMENT = 1 << 1,
  BOTH = (ShaderStage::VERTEX | ShaderStage::FRAGMENT),
};
ENUM_OPERATORS(ShaderStage, ShaderStage::BOTH);

inline uint get_shader_stage_index(ShaderStage stage)
{
  switch (stage) {
    case ShaderStage::VERTEX:
      return 0;
    case ShaderStage::FRAGMENT:
      return 1;
    default:
      BLI_assert_unreachable();
      return 0;
  }
  return 0;
}

/* Shader input/output binding information. */
struct MTLShaderInputAttribute {
  uint32_t name_offset;
  MTLVertexFormat format;
  uint32_t index;
  uint32_t location;
  uint32_t size;
  uint32_t buffer_index;
  uint32_t offset;
  /* For attributes of Matrix/array types, we need to insert "fake" attributes for
   * each element, as matrix types are not natively supported.
   *
   *   > 1 if matrix/arrays are used, specifying number of elements.
   *   = 1 for non-matrix types
   *   = 0 if used as a dummy slot for "fake" matrix attributes. */
  uint32_t matrix_element_count;
};

struct MTLShaderUniformBlock {
  uint32_t name_offset;
  uint32_t size = 0;
  /* Buffer resouce bind index in shader [[buffer(index)]]. */
  uint32_t buffer_index;

  /* Tracking for manual uniform addition. */
  uint32_t current_offset;
  ShaderStage stage_mask;
};

struct MTLShaderUniform {
  uint32_t name_offset;
  /* Index of `MTLShaderUniformBlock` this uniform belongs to. */
  uint32_t size_in_bytes;
  uint32_t byte_offset;
  eMTLDataType type;
  uint32_t array_len;
};

struct MTLShaderTexture {
  bool used;
  uint32_t name_offset;
  /* Texture resource bind slot in shader [[texture(n)]]. */
  int slot_index;
  eGPUTextureType type;
  ShaderStage stage_mask;
};

struct MTLShaderSampler {
  uint32_t name_offset;
  /* Sampler resource bind slot in shader [[sampler(n)]]. */
  uint32_t slot_index = 0;
};

/* Utility Functions. */
MTLVertexFormat mtl_datatype_to_vertex_type(eMTLDataType type);

/**
 * Implementation of Shader interface for Metal Backend.
 **/
class MTLShaderInterface : public ShaderInterface {

 private:
  /* Argument encoders caching.
   * Static size is based on common input permutation variations. */
  static const int ARGUMENT_ENCODERS_CACHE_SIZE = 3;
  struct ArgumentEncoderCacheEntry {
    id<MTLArgumentEncoder> encoder;
    int buffer_index;
  };
  ArgumentEncoderCacheEntry arg_encoders_[ARGUMENT_ENCODERS_CACHE_SIZE] = {};

  /* Vertex input Attribues. */
  uint32_t total_attributes_;
  uint32_t total_vert_stride_;
  MTLShaderInputAttribute attributes_[MTL_MAX_VERTEX_INPUT_ATTRIBUTES];

  /* Uniforms. */
  uint32_t total_uniforms_;
  MTLShaderUniform uniforms_[MTL_MAX_UNIFORMS_PER_BLOCK];

  /* Uniform Blocks. */
  uint32_t total_uniform_blocks_;
  MTLShaderUniformBlock ubos_[MTL_MAX_UNIFORM_BUFFER_BINDINGS];
  MTLShaderUniformBlock push_constant_block_;

  /* Textures. */
  /* Textures support explicit binding indices, so some texture slots
   * remain unused. */
  uint32_t total_textures_;
  int max_texture_index_;
  MTLShaderTexture textures_[MTL_MAX_TEXTURE_SLOTS];

  /* Whether argument buffers are used for sampler bindings. */
  bool sampler_use_argument_buffer_;
  int sampler_argument_buffer_bind_index_vert_;
  int sampler_argument_buffer_bind_index_frag_;

  /* Attribute Mask. */
  uint32_t enabled_attribute_mask_;

  /* Debug. */
  char name[256];

 public:
  MTLShaderInterface(const char *name);
  ~MTLShaderInterface();

  void init();
  void add_input_attribute(uint32_t name_offset,
                           uint32_t attribute_location,
                           MTLVertexFormat format,
                           uint32_t buffer_index,
                           uint32_t size,
                           uint32_t offset,
                           int matrix_element_count = 1);
  uint32_t add_uniform_block(uint32_t name_offset,
                             uint32_t buffer_index,
                             uint32_t size,
                             ShaderStage stage_mask = ShaderStage::BOTH);
  void add_uniform(uint32_t name_offset, eMTLDataType type, int array_len = 1);
  void add_texture(uint32_t name_offset,
                   uint32_t texture_slot,
                   eGPUTextureType tex_binding_type,
                   ShaderStage stage_mask = ShaderStage::FRAGMENT);
  void add_push_constant_block(uint32_t name_offset);

  /* Resolve and cache locations of builtin uniforms and uniform blocks. */
  void map_builtins();
  void set_sampler_properties(bool use_argument_buffer,
                              uint32_t argument_buffer_bind_index_vert,
                              uint32_t argument_buffer_bind_index_frag);

  /* Prepare ShaderInput interface for binding resolution. */
  void prepare_common_shader_inputs();

  /* Fetch Uniforms. */
  const MTLShaderUniform &get_uniform(uint index) const;
  uint32_t get_total_uniforms() const;

  /* Fetch Uniform Blocks. */
  const MTLShaderUniformBlock &get_uniform_block(uint index) const;
  uint32_t get_total_uniform_blocks() const;
  bool has_uniform_block(uint32_t block_index) const;
  uint32_t get_uniform_block_size(uint32_t block_index) const;

  /* Push constant uniform data block should always be available. */
  const MTLShaderUniformBlock &get_push_constant_block() const;

  /* Fetch textures. */
  const MTLShaderTexture &get_texture(uint index) const;
  uint32_t get_total_textures() const;
  uint32_t get_max_texture_index() const;
  bool get_use_argument_buffer_for_samplers(int *vertex_arg_buffer_bind_index,
                                            int *fragment_arg_buffer_bind_index) const;

  /* Fetch Attributes. */
  const MTLShaderInputAttribute &get_attribute(uint index) const;
  uint32_t get_total_attributes() const;
  uint32_t get_total_vertex_stride() const;
  uint32_t get_enabled_attribute_mask() const;

  /* Name buffer fetching. */
  const char *get_name_at_offset(uint32_t offset) const;

  /* Interface name. */
  const char *get_name() const
  {
    return this->name;
  }

  /* Argument buffer encoder management. */
  id<MTLArgumentEncoder> find_argument_encoder(int buffer_index) const;

  void insert_argument_encoder(int buffer_index, id encoder);

  MEM_CXX_CLASS_ALLOC_FUNCS("MTLShaderInterface");
};

}  // namespace blender::gpu