This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/gpu/metal/mtl_shader_interface.hh

268 lines
8.5 KiB
C++

/* 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 data-structures 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 #MTLShaderInterface, such that extraction
* of required information can be performed within the back-end.
*
* 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 bind-less 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 resource 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 Back-end.
**/
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 Attributes. */
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