268 lines
8.5 KiB
C++
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
|