mirror of
https://github.com/godotengine/godot.git
synced 2025-12-08 06:09:55 +00:00
Metal: Stable argument buffers; GPU rendering crashes; visionOS exports
Supersedes #110683
This commit is contained in:
parent
ab6c6eece8
commit
97c17aedc7
24 changed files with 2635 additions and 2082 deletions
|
|
@ -51,6 +51,7 @@
|
|||
/**************************************************************************/
|
||||
|
||||
#import "metal_device_properties.h"
|
||||
#import "metal_objects_shared.h"
|
||||
#import "metal_utils.h"
|
||||
#import "pixel_formats.h"
|
||||
#import "sha256_digest.h"
|
||||
|
|
@ -66,38 +67,8 @@
|
|||
#import <initializer_list>
|
||||
#import <optional>
|
||||
|
||||
// These types can be used in Vector and other containers that use
|
||||
// pointer operations not supported by ARC.
|
||||
namespace MTL {
|
||||
#define MTL_CLASS(name) \
|
||||
class name { \
|
||||
public: \
|
||||
name(id<MTL##name> obj = nil) : m_obj(obj) {} \
|
||||
operator id<MTL##name>() const { \
|
||||
return m_obj; \
|
||||
} \
|
||||
id<MTL##name> m_obj; \
|
||||
};
|
||||
|
||||
MTL_CLASS(Texture)
|
||||
|
||||
} //namespace MTL
|
||||
|
||||
enum ShaderStageUsage : uint32_t {
|
||||
None = 0,
|
||||
Vertex = RDD::SHADER_STAGE_VERTEX_BIT,
|
||||
Fragment = RDD::SHADER_STAGE_FRAGMENT_BIT,
|
||||
TesselationControl = RDD::SHADER_STAGE_TESSELATION_CONTROL_BIT,
|
||||
TesselationEvaluation = RDD::SHADER_STAGE_TESSELATION_EVALUATION_BIT,
|
||||
Compute = RDD::SHADER_STAGE_COMPUTE_BIT,
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ ShaderStageUsage &operator|=(ShaderStageUsage &p_a, int p_b) {
|
||||
p_a = ShaderStageUsage(uint32_t(p_a) | uint32_t(p_b));
|
||||
return p_a;
|
||||
}
|
||||
|
||||
enum StageResourceUsage : uint32_t {
|
||||
ResourceUnused = 0,
|
||||
VertexRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_VERTEX * 2),
|
||||
VertexWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_VERTEX * 2),
|
||||
FragmentRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_FRAGMENT * 2),
|
||||
|
|
@ -110,9 +81,61 @@ enum StageResourceUsage : uint32_t {
|
|||
ComputeWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_COMPUTE * 2),
|
||||
};
|
||||
|
||||
typedef LocalVector<__unsafe_unretained id<MTLResource>> ResourceVector;
|
||||
typedef id<MTLResource> __unsafe_unretained MTLResourceUnsafe;
|
||||
|
||||
template <>
|
||||
struct HashMapHasherDefaultImpl<MTLResourceUnsafe> {
|
||||
static _FORCE_INLINE_ uint32_t hash(const MTLResourceUnsafe p_pointer) { return hash_one_uint64((uint64_t)p_pointer); }
|
||||
};
|
||||
|
||||
typedef LocalVector<MTLResourceUnsafe> ResourceVector;
|
||||
typedef HashMap<StageResourceUsage, ResourceVector> ResourceUsageMap;
|
||||
|
||||
struct ResourceUsageEntry {
|
||||
StageResourceUsage usage = ResourceUnused;
|
||||
uint32_t unused = 0;
|
||||
|
||||
ResourceUsageEntry() {}
|
||||
ResourceUsageEntry(StageResourceUsage p_usage) :
|
||||
usage(p_usage) {}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct is_zero_constructible<ResourceUsageEntry> : std::true_type {};
|
||||
|
||||
/*! Track the cumulative usage for a resource during a render or compute pass */
|
||||
typedef HashMap<MTLResourceUnsafe, ResourceUsageEntry> ResourceToStageUsage;
|
||||
|
||||
/*! Track resource and ensure they are resident prior to dispatch or draw commands.
|
||||
*
|
||||
* The primary purpose of this data structure is to track all the resources that must be made resident prior
|
||||
* to issuing the next dispatch or draw command. It aggregates all resources used from argument buffers.
|
||||
*
|
||||
* As an optimization, this data structure also tracks previous usage for resources, so that
|
||||
* it may avoid binding them again in later commands if the resource is already resident and its usage flagged.
|
||||
*/
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) ResourceTracker {
|
||||
// A constant specifying how many iterations a resource can remain in
|
||||
// the _previous HashSet before it will be removed permanently.
|
||||
//
|
||||
// Keeping them in the _previous HashMap reduces churn if resources are regularly
|
||||
// bound. 256 is arbitrary, but if an object remains unused for 256 encoders,
|
||||
// it will be released.
|
||||
static constexpr uint32_t RESOURCE_UNUSED_CLEANUP_COUNT = 256;
|
||||
|
||||
// Used as a scratch buffer to periodically clean up resources from _previous.
|
||||
ResourceVector _scratch;
|
||||
// Tracks all resources and their prior usage for the duration of the encoder.
|
||||
ResourceToStageUsage _previous;
|
||||
// Tracks resources for the current command that must be made resident
|
||||
ResourceUsageMap _current;
|
||||
|
||||
void merge_from(const ResourceUsageMap &p_from);
|
||||
void encode(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc);
|
||||
void encode(id<MTLComputeCommandEncoder> __unsafe_unretained p_enc);
|
||||
void reset();
|
||||
};
|
||||
|
||||
enum class MDCommandBufferStateType {
|
||||
None,
|
||||
Render,
|
||||
|
|
@ -130,54 +153,16 @@ class MDRenderPass;
|
|||
class MDPipeline;
|
||||
class MDRenderPipeline;
|
||||
class MDComputePipeline;
|
||||
class MDFrameBuffer;
|
||||
class RenderingDeviceDriverMetal;
|
||||
class MDUniformSet;
|
||||
class MDShader;
|
||||
|
||||
struct MetalBufferDynamicInfo;
|
||||
|
||||
using RDM = RenderingDeviceDriverMetal;
|
||||
|
||||
#pragma mark - Resource Factory
|
||||
|
||||
struct ClearAttKey {
|
||||
const static uint32_t COLOR_COUNT = MAX_COLOR_ATTACHMENT_COUNT;
|
||||
const static uint32_t DEPTH_INDEX = COLOR_COUNT;
|
||||
const static uint32_t STENCIL_INDEX = DEPTH_INDEX + 1;
|
||||
const static uint32_t ATTACHMENT_COUNT = STENCIL_INDEX + 1;
|
||||
|
||||
enum Flags : uint16_t {
|
||||
CLEAR_FLAGS_NONE = 0,
|
||||
CLEAR_FLAGS_LAYERED = 1 << 0,
|
||||
};
|
||||
|
||||
Flags flags = CLEAR_FLAGS_NONE;
|
||||
uint16_t sample_count = 0;
|
||||
uint16_t pixel_formats[ATTACHMENT_COUNT] = { 0 };
|
||||
|
||||
_FORCE_INLINE_ void set_color_format(uint32_t p_idx, MTLPixelFormat p_fmt) { pixel_formats[p_idx] = p_fmt; }
|
||||
_FORCE_INLINE_ void set_depth_format(MTLPixelFormat p_fmt) { pixel_formats[DEPTH_INDEX] = p_fmt; }
|
||||
_FORCE_INLINE_ void set_stencil_format(MTLPixelFormat p_fmt) { pixel_formats[STENCIL_INDEX] = p_fmt; }
|
||||
_FORCE_INLINE_ MTLPixelFormat depth_format() const { return (MTLPixelFormat)pixel_formats[DEPTH_INDEX]; }
|
||||
_FORCE_INLINE_ MTLPixelFormat stencil_format() const { return (MTLPixelFormat)pixel_formats[STENCIL_INDEX]; }
|
||||
_FORCE_INLINE_ void enable_layered_rendering() { flags::set(flags, CLEAR_FLAGS_LAYERED); }
|
||||
|
||||
_FORCE_INLINE_ bool is_enabled(uint32_t p_idx) const { return pixel_formats[p_idx] != 0; }
|
||||
_FORCE_INLINE_ bool is_depth_enabled() const { return pixel_formats[DEPTH_INDEX] != 0; }
|
||||
_FORCE_INLINE_ bool is_stencil_enabled() const { return pixel_formats[STENCIL_INDEX] != 0; }
|
||||
_FORCE_INLINE_ bool is_layered_rendering_enabled() const { return flags::any(flags, CLEAR_FLAGS_LAYERED); }
|
||||
|
||||
_FORCE_INLINE_ bool operator==(const ClearAttKey &p_rhs) const {
|
||||
return memcmp(this, &p_rhs, sizeof(ClearAttKey)) == 0;
|
||||
}
|
||||
|
||||
uint32_t hash() const {
|
||||
uint32_t h = hash_murmur3_one_32(flags);
|
||||
h = hash_murmur3_one_32(sample_count, h);
|
||||
h = hash_murmur3_buffer(pixel_formats, ATTACHMENT_COUNT * sizeof(pixel_formats[0]), h);
|
||||
return hash_fmix32(h);
|
||||
}
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDResourceFactory {
|
||||
private:
|
||||
RenderingDeviceDriverMetal *device_driver;
|
||||
|
|
@ -309,13 +294,127 @@ public:
|
|||
MDRenderPass(Vector<MDAttachment> &p_attachments, Vector<MDSubpass> &p_subpasses);
|
||||
};
|
||||
|
||||
struct BindingCache {
|
||||
struct BufferBinding {
|
||||
id<MTLBuffer> __unsafe_unretained buffer = nil;
|
||||
NSUInteger offset = 0;
|
||||
|
||||
bool operator!=(const BufferBinding &p_other) const {
|
||||
return buffer != p_other.buffer || offset != p_other.offset;
|
||||
}
|
||||
};
|
||||
|
||||
LocalVector<id<MTLTexture> __unsafe_unretained> textures;
|
||||
LocalVector<id<MTLSamplerState> __unsafe_unretained> samplers;
|
||||
LocalVector<BufferBinding> buffers;
|
||||
|
||||
_FORCE_INLINE_ void clear() {
|
||||
textures.clear();
|
||||
samplers.clear();
|
||||
buffers.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
_FORCE_INLINE_ void ensure_size(LocalVector<T> &p_vec, uint32_t p_required) {
|
||||
if (p_vec.size() < p_required) {
|
||||
p_vec.resize_initialized(p_required);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ bool update(NSRange p_range, id<MTLTexture> __unsafe_unretained const *p_values) {
|
||||
if (p_range.length == 0) {
|
||||
return false;
|
||||
}
|
||||
uint32_t required = (uint32_t)(p_range.location + p_range.length);
|
||||
ensure_size(textures, required);
|
||||
bool changed = false;
|
||||
for (NSUInteger i = 0; i < p_range.length; ++i) {
|
||||
uint32_t slot = (uint32_t)(p_range.location + i);
|
||||
id<MTLTexture> value = p_values[i];
|
||||
if (textures[slot] != value) {
|
||||
textures[slot] = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool update(NSRange p_range, id<MTLSamplerState> __unsafe_unretained const *p_values) {
|
||||
if (p_range.length == 0) {
|
||||
return false;
|
||||
}
|
||||
uint32_t required = (uint32_t)(p_range.location + p_range.length);
|
||||
ensure_size(samplers, required);
|
||||
bool changed = false;
|
||||
for (NSUInteger i = 0; i < p_range.length; ++i) {
|
||||
uint32_t slot = (uint32_t)(p_range.location + i);
|
||||
id<MTLSamplerState> __unsafe_unretained value = p_values[i];
|
||||
if (samplers[slot] != value) {
|
||||
samplers[slot] = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool update(NSRange p_range, id<MTLBuffer> __unsafe_unretained const *p_values, const NSUInteger *p_offsets) {
|
||||
if (p_range.length == 0) {
|
||||
return false;
|
||||
}
|
||||
uint32_t required = (uint32_t)(p_range.location + p_range.length);
|
||||
ensure_size(buffers, required);
|
||||
BufferBinding *buffers_ptr = buffers.ptr() + p_range.location;
|
||||
bool changed = false;
|
||||
for (NSUInteger i = 0; i < p_range.length; ++i) {
|
||||
BufferBinding &binding = *buffers_ptr;
|
||||
BufferBinding new_binding = {
|
||||
.buffer = p_values[i],
|
||||
.offset = p_offsets[i],
|
||||
};
|
||||
if (binding != new_binding) {
|
||||
binding = new_binding;
|
||||
changed = true;
|
||||
}
|
||||
++buffers_ptr;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool update(id<MTLBuffer> __unsafe_unretained p_buffer, NSUInteger p_offset, uint32_t p_index) {
|
||||
uint32_t required = p_index + 1;
|
||||
ensure_size(buffers, required);
|
||||
BufferBinding &binding = buffers.ptr()[p_index];
|
||||
BufferBinding new_binding = {
|
||||
.buffer = p_buffer,
|
||||
.offset = p_offset,
|
||||
};
|
||||
if (binding != new_binding) {
|
||||
binding = new_binding;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDCommandBuffer {
|
||||
friend class MDUniformSet;
|
||||
|
||||
private:
|
||||
#pragma mark - Common State
|
||||
|
||||
// From RenderingDevice
|
||||
static constexpr uint32_t MAX_PUSH_CONSTANT_SIZE = 128;
|
||||
|
||||
uint8_t push_constant_data[MAX_PUSH_CONSTANT_SIZE];
|
||||
uint32_t push_constant_data_len = 0;
|
||||
uint32_t push_constant_binding = UINT32_MAX;
|
||||
|
||||
BindingCache binding_cache;
|
||||
|
||||
void reset();
|
||||
|
||||
RenderingDeviceDriverMetal *device_driver = nullptr;
|
||||
id<MTLCommandQueue> queue = nil;
|
||||
id<MTLCommandBuffer> commandBuffer = nil;
|
||||
|
|
@ -331,6 +430,16 @@ private:
|
|||
|
||||
void _end_compute_dispatch();
|
||||
void _end_blit();
|
||||
id<MTLBlitCommandEncoder> _ensure_blit_encoder();
|
||||
|
||||
enum class CopySource {
|
||||
Buffer,
|
||||
Texture,
|
||||
};
|
||||
void _copy_texture_buffer(CopySource p_source,
|
||||
RDD::TextureID p_texture,
|
||||
RDD::BufferID p_buffer,
|
||||
VectorView<RDD::BufferTextureCopyRegion> p_regions);
|
||||
|
||||
#pragma mark - Render
|
||||
|
||||
|
|
@ -368,7 +477,7 @@ public:
|
|||
uint32_t index_offset = 0;
|
||||
LocalVector<id<MTLBuffer> __unsafe_unretained> vertex_buffers;
|
||||
LocalVector<NSUInteger> vertex_offsets;
|
||||
ResourceUsageMap resource_usage;
|
||||
ResourceTracker resource_tracker;
|
||||
// clang-format off
|
||||
enum DirtyFlag: uint16_t {
|
||||
DIRTY_NONE = 0,
|
||||
|
|
@ -390,9 +499,6 @@ public:
|
|||
uint32_t dynamic_offsets = 0;
|
||||
// Bit mask of the uniform sets that are dirty, to prevent redundant binding.
|
||||
uint64_t uniform_set_mask = 0;
|
||||
uint8_t push_constant_data[MAX_PUSH_CONSTANT_SIZE];
|
||||
uint32_t push_constant_data_len = 0;
|
||||
uint32_t push_constant_bindings[2] = { ~0U, ~0U };
|
||||
|
||||
_FORCE_INLINE_ void reset();
|
||||
void end_encoding();
|
||||
|
|
@ -447,13 +553,6 @@ public:
|
|||
dirty.set_flag(DirtyFlag::DIRTY_UNIFORMS);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_push_constants_dirty() {
|
||||
if (push_constant_data_len == 0) {
|
||||
return;
|
||||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_PUSH);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_blend_dirty() {
|
||||
if (!blend_constants.has_value()) {
|
||||
return;
|
||||
|
|
@ -495,7 +594,7 @@ public:
|
|||
struct ComputeState {
|
||||
MDComputePipeline *pipeline = nullptr;
|
||||
id<MTLComputeCommandEncoder> encoder = nil;
|
||||
ResourceUsageMap resource_usage;
|
||||
ResourceTracker resource_tracker;
|
||||
// clang-format off
|
||||
enum DirtyFlag: uint16_t {
|
||||
DIRTY_NONE = 0,
|
||||
|
|
@ -511,9 +610,6 @@ public:
|
|||
uint32_t dynamic_offsets = 0;
|
||||
// Bit mask of the uniform sets that are dirty, to prevent redundant binding.
|
||||
uint64_t uniform_set_mask = 0;
|
||||
uint8_t push_constant_data[MAX_PUSH_CONSTANT_SIZE];
|
||||
uint32_t push_constant_data_len = 0;
|
||||
uint32_t push_constant_bindings[1] = { ~0U };
|
||||
|
||||
_FORCE_INLINE_ void reset();
|
||||
void end_encoding();
|
||||
|
|
@ -529,14 +625,6 @@ public:
|
|||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_UNIFORMS);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_push_constants_dirty() {
|
||||
if (push_constant_data_len == 0) {
|
||||
return;
|
||||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_PUSH);
|
||||
}
|
||||
|
||||
} compute;
|
||||
|
||||
// State specific to a blit pass.
|
||||
|
|
@ -555,9 +643,6 @@ public:
|
|||
void commit();
|
||||
void end();
|
||||
|
||||
id<MTLBlitCommandEncoder> blit_command_encoder();
|
||||
void encodeRenderCommandEncoderWithDescriptor(MTLRenderPassDescriptor *p_desc, NSString *p_label);
|
||||
|
||||
void bind_pipeline(RDD::PipelineID p_pipeline);
|
||||
void encode_push_constant_data(RDD::ShaderID p_shader, VectorView<uint32_t> p_data);
|
||||
|
||||
|
|
@ -600,6 +685,25 @@ public:
|
|||
void compute_dispatch(uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups);
|
||||
void compute_dispatch_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset);
|
||||
|
||||
#pragma mark - Transfer
|
||||
|
||||
private:
|
||||
void encodeRenderCommandEncoderWithDescriptor(MTLRenderPassDescriptor *p_desc, NSString *p_label);
|
||||
|
||||
public:
|
||||
void resolve_texture(RDD::TextureID p_src_texture, RDD::TextureLayout p_src_texture_layout, uint32_t p_src_layer, uint32_t p_src_mipmap, RDD::TextureID p_dst_texture, RDD::TextureLayout p_dst_texture_layout, uint32_t p_dst_layer, uint32_t p_dst_mipmap);
|
||||
void clear_color_texture(RDD::TextureID p_texture, RDD::TextureLayout p_texture_layout, const Color &p_color, const RDD::TextureSubresourceRange &p_subresources);
|
||||
void clear_buffer(RDD::BufferID p_buffer, uint64_t p_offset, uint64_t p_size);
|
||||
void copy_buffer(RDD::BufferID p_src_buffer, RDD::BufferID p_dst_buffer, VectorView<RDD::BufferCopyRegion> p_regions);
|
||||
void copy_texture(RDD::TextureID p_src_texture, RDD::TextureID p_dst_texture, VectorView<RDD::TextureCopyRegion> p_regions);
|
||||
void copy_buffer_to_texture(RDD::BufferID p_src_buffer, RDD::TextureID p_dst_texture, VectorView<RDD::BufferTextureCopyRegion> p_regions);
|
||||
void copy_texture_to_buffer(RDD::TextureID p_src_texture, RDD::BufferID p_dst_buffer, VectorView<RDD::BufferTextureCopyRegion> p_regions);
|
||||
|
||||
#pragma mark - Debugging
|
||||
|
||||
void begin_label(const char *p_label_name, const Color &p_color);
|
||||
void end_label();
|
||||
|
||||
MDCommandBuffer(id<MTLCommandQueue> p_queue, RenderingDeviceDriverMetal *p_device_driver) :
|
||||
device_driver(p_device_driver), queue(p_queue) {
|
||||
type = MDCommandBufferStateType::None;
|
||||
|
|
@ -615,44 +719,44 @@ public:
|
|||
#define MTLBindingAccessWriteOnly MTLArgumentAccessWriteOnly
|
||||
#endif
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) BindingInfo {
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) UniformInfo {
|
||||
uint32_t binding;
|
||||
BitField<RDD::ShaderStage> active_stages;
|
||||
MTLDataType dataType = MTLDataTypeNone;
|
||||
uint32_t index = 0;
|
||||
MTLBindingAccess access = MTLBindingAccessReadOnly;
|
||||
MTLResourceUsage usage = 0;
|
||||
MTLTextureType textureType = MTLTextureType2D;
|
||||
int imageFormat = 0;
|
||||
uint32_t imageFormat = 0;
|
||||
uint32_t arrayLength = 0;
|
||||
bool isMultisampled = false;
|
||||
bool isMultisampled = 0;
|
||||
|
||||
inline MTLArgumentDescriptor *new_argument_descriptor() const {
|
||||
MTLArgumentDescriptor *desc = MTLArgumentDescriptor.argumentDescriptor;
|
||||
desc.dataType = dataType;
|
||||
desc.index = index;
|
||||
desc.access = access;
|
||||
desc.textureType = textureType;
|
||||
desc.arrayLength = arrayLength;
|
||||
return desc;
|
||||
struct Indexes {
|
||||
uint32_t buffer = UINT32_MAX;
|
||||
uint32_t texture = UINT32_MAX;
|
||||
uint32_t sampler = UINT32_MAX;
|
||||
};
|
||||
Indexes slot;
|
||||
Indexes arg_buffer;
|
||||
|
||||
enum class IndexType {
|
||||
SLOT,
|
||||
ARG,
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ Indexes &get_indexes(IndexType p_type) {
|
||||
switch (p_type) {
|
||||
case IndexType::SLOT:
|
||||
return slot;
|
||||
case IndexType::ARG:
|
||||
return arg_buffer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using RDC = RenderingDeviceCommons;
|
||||
|
||||
typedef API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) HashMap<RDC::ShaderStage, BindingInfo> BindingInfoMap;
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) UniformInfo {
|
||||
uint32_t binding;
|
||||
ShaderStageUsage active_stages = None;
|
||||
BindingInfoMap bindings;
|
||||
BindingInfoMap bindings_secondary;
|
||||
};
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) UniformSet {
|
||||
LocalVector<UniformInfo> uniforms;
|
||||
LocalVector<uint32_t> dynamic_uniforms;
|
||||
uint32_t buffer_size = 0;
|
||||
HashMap<RDC::ShaderStage, uint32_t> offsets;
|
||||
HashMap<RDC::ShaderStage, id<MTLArgumentEncoder>> encoders;
|
||||
};
|
||||
|
||||
struct ShaderCacheEntry;
|
||||
|
|
@ -691,13 +795,6 @@ enum class ShaderLoadStrategy {
|
|||
data:(dispatch_data_t)data;
|
||||
@end
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<SHA256Digest> {
|
||||
static bool compare(const SHA256Digest &p_lhs, const SHA256Digest &p_rhs) {
|
||||
return memcmp(p_lhs.data, p_rhs.data, CC_SHA256_DIGEST_LENGTH) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// A cache entry for a Metal shader library.
|
||||
struct ShaderCacheEntry {
|
||||
RenderingDeviceDriverMetal &owner;
|
||||
|
|
@ -718,16 +815,6 @@ struct ShaderCacheEntry {
|
|||
~ShaderCacheEntry() = default;
|
||||
};
|
||||
|
||||
/// Godot limits the number of dynamic buffers to 8.
|
||||
///
|
||||
/// This is a minimum guarantee for Vulkan.
|
||||
constexpr uint32_t MAX_DYNAMIC_BUFFERS = 8;
|
||||
|
||||
/// Maximum number of queued frames.
|
||||
///
|
||||
/// See setting: rendering/rendering_device/vsync/frame_queue_size
|
||||
constexpr uint32_t MAX_FRAME_COUNT = 4;
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) DynamicOffsetLayout {
|
||||
struct Data {
|
||||
uint8_t offset : 4;
|
||||
|
|
@ -760,19 +847,15 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) DynamicOffsets {
|
||||
uint32_t data;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ uint32_t get_frame_index(const DynamicOffsetLayout &p_layout) const {
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDShader {
|
||||
public:
|
||||
CharString name;
|
||||
Vector<UniformSet> sets;
|
||||
struct {
|
||||
BitField<RDD::ShaderStage> stages = {};
|
||||
uint32_t binding = UINT32_MAX;
|
||||
uint32_t size = 0;
|
||||
} push_constants;
|
||||
DynamicOffsetLayout dynamic_offset_layout;
|
||||
bool uses_argument_buffers = true;
|
||||
|
||||
|
|
@ -783,10 +866,6 @@ public:
|
|||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDComputeShader final : public MDShader {
|
||||
public:
|
||||
struct {
|
||||
int32_t binding = -1;
|
||||
uint32_t size = 0;
|
||||
} push_constants;
|
||||
MTLSize local = {};
|
||||
|
||||
MDLibrary *kernel;
|
||||
|
|
@ -796,16 +875,6 @@ public:
|
|||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDRenderShader final : public MDShader {
|
||||
public:
|
||||
struct {
|
||||
struct {
|
||||
int32_t binding = -1;
|
||||
uint32_t size = 0;
|
||||
} vert;
|
||||
struct {
|
||||
int32_t binding = -1;
|
||||
uint32_t size = 0;
|
||||
} frag;
|
||||
} push_constants;
|
||||
bool needs_view_mask_buffer = false;
|
||||
|
||||
MDLibrary *vert;
|
||||
|
|
@ -838,52 +907,69 @@ struct HashMapComparatorDefault<RDD::ShaderID> {
|
|||
}
|
||||
};
|
||||
|
||||
struct BoundUniformSet {
|
||||
id<MTLBuffer> buffer;
|
||||
ResourceUsageMap usage_to_resources;
|
||||
/// Size of the per-frame buffer, which is 0 when there are no dynamic uniforms.
|
||||
uint32_t frame_size = 0;
|
||||
|
||||
/// Perform a 2-way merge each key of `ResourceVector` resources from this set into the
|
||||
/// destination set.
|
||||
///
|
||||
/// Assumes the vectors of resources are sorted.
|
||||
void merge_into(ResourceUsageMap &p_dst) const;
|
||||
|
||||
/// Returns true if this bound uniform set contains dynamic uniforms.
|
||||
_FORCE_INLINE_ bool is_dynamic() const { return frame_size > 0; }
|
||||
|
||||
/// Calculate the offset in the Metal buffer for the current frame.
|
||||
_FORCE_INLINE_ uint32_t frame_offset(uint32_t p_frame_index) const { return p_frame_index * frame_size; }
|
||||
|
||||
/// Calculate the offset in the buffer for the given frame index and base offset.
|
||||
_FORCE_INLINE_ uint32_t make_offset(uint32_t p_frame_index, uint32_t p_base_offset) const {
|
||||
return frame_offset(p_frame_index) + p_base_offset;
|
||||
template <>
|
||||
struct HashMapComparatorDefault<RDD::BufferID> {
|
||||
static bool compare(const RDD::BufferID &p_lhs, const RDD::BufferID &p_rhs) {
|
||||
return p_lhs.id == p_rhs.id;
|
||||
}
|
||||
};
|
||||
|
||||
BoundUniformSet() = default;
|
||||
BoundUniformSet(id<MTLBuffer> p_buffer, ResourceUsageMap &&p_usage_to_resources, uint32_t p_frame_size) :
|
||||
buffer(p_buffer), usage_to_resources(std::move(p_usage_to_resources)), frame_size(p_frame_size) {}
|
||||
template <>
|
||||
struct HashMapComparatorDefault<RDD::TextureID> {
|
||||
static bool compare(const RDD::TextureID &p_lhs, const RDD::TextureID &p_rhs) {
|
||||
return p_lhs.id == p_rhs.id;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapHasherDefaultImpl<RDD::BufferID> {
|
||||
static _FORCE_INLINE_ uint32_t hash(const RDD::BufferID &p_value) {
|
||||
return HashMapHasherDefaultImpl<uint64_t>::hash(p_value.id);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapHasherDefaultImpl<RDD::TextureID> {
|
||||
static _FORCE_INLINE_ uint32_t hash(const RDD::TextureID &p_value) {
|
||||
return HashMapHasherDefaultImpl<uint64_t>::hash(p_value.id);
|
||||
}
|
||||
};
|
||||
|
||||
// A type used to encode resources directly to a MTLCommandEncoder
|
||||
struct DirectEncoder {
|
||||
id<MTLCommandEncoder> __unsafe_unretained encoder;
|
||||
BindingCache &cache;
|
||||
enum Mode {
|
||||
RENDER,
|
||||
COMPUTE
|
||||
};
|
||||
Mode mode;
|
||||
|
||||
void set(id<MTLBuffer> __unsafe_unretained *p_buffers, const NSUInteger *p_offsets, NSRange p_range);
|
||||
void set(id<MTLBuffer> __unsafe_unretained p_buffer, const NSUInteger p_offset, uint32_t p_index);
|
||||
void set(id<MTLTexture> __unsafe_unretained *p_textures, NSRange p_range);
|
||||
void set(id<MTLSamplerState> __unsafe_unretained *p_samplers, NSRange p_range);
|
||||
|
||||
DirectEncoder(id<MTLCommandEncoder> __unsafe_unretained p_encoder, BindingCache &p_cache) :
|
||||
encoder(p_encoder), cache(p_cache) {
|
||||
if ([p_encoder conformsToProtocol:@protocol(MTLRenderCommandEncoder)]) {
|
||||
mode = RENDER;
|
||||
} else {
|
||||
mode = COMPUTE;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDUniformSet {
|
||||
private:
|
||||
void bind_uniforms_argument_buffers(MDShader *p_shader, MDCommandBuffer::RenderState &p_state, uint32_t p_set_index, uint32_t p_dynamic_offsets, uint32_t p_frame_idx, uint32_t p_frame_count);
|
||||
void bind_uniforms_direct(MDShader *p_shader, MDCommandBuffer::RenderState &p_state, uint32_t p_set_index, uint32_t p_dynamic_offsets);
|
||||
void bind_uniforms_argument_buffers(MDShader *p_shader, MDCommandBuffer::ComputeState &p_state, uint32_t p_set_index, uint32_t p_dynamic_offsets, uint32_t p_frame_idx, uint32_t p_frame_count);
|
||||
void bind_uniforms_direct(MDShader *p_shader, MDCommandBuffer::ComputeState &p_state, uint32_t p_set_index, uint32_t p_dynamic_offsets);
|
||||
|
||||
void update_dynamic_uniforms(MDShader *p_shader, ResourceUsageMap &p_resource_usage, uint32_t p_set_index, BoundUniformSet &p_bound_set, uint32_t p_dynamic_offsets, uint32_t p_frame_idx);
|
||||
|
||||
public:
|
||||
uint32_t index = 0;
|
||||
id<MTLBuffer> arg_buffer = nil;
|
||||
ResourceUsageMap usage_to_resources;
|
||||
LocalVector<RDD::BoundUniform> uniforms;
|
||||
HashMap<MDShader *, BoundUniformSet> bound_uniforms;
|
||||
|
||||
void bind_uniforms(MDShader *p_shader, MDCommandBuffer::RenderState &p_state, uint32_t p_set_index, uint32_t p_dynamic_offsets, uint32_t p_frame_idx, uint32_t p_frame_count);
|
||||
void bind_uniforms(MDShader *p_shader, MDCommandBuffer::ComputeState &p_state, uint32_t p_set_index, uint32_t p_dynamic_offsets, uint32_t p_frame_idx, uint32_t p_frame_count);
|
||||
|
||||
BoundUniformSet &bound_uniform_set(MDShader *p_shader, id<MTLDevice> p_device, ResourceUsageMap &p_resource_usage, uint32_t p_set_index, uint32_t p_dynamic_offsets, uint32_t p_frame_idx, uint32_t p_frame_count);
|
||||
void bind_uniforms_argument_buffers(MDShader *p_shader, MDCommandBuffer::RenderState &p_state, uint32_t p_set_index, uint32_t p_dynamic_offsets, uint32_t p_frame_idx, uint32_t p_frame_count);
|
||||
void bind_uniforms_argument_buffers(MDShader *p_shader, MDCommandBuffer::ComputeState &p_state, uint32_t p_set_index, uint32_t p_dynamic_offsets, uint32_t p_frame_idx, uint32_t p_frame_count);
|
||||
void bind_uniforms_direct(MDShader *p_shader, DirectEncoder p_enc, uint32_t p_set_index, uint32_t p_dynamic_offsets);
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDPipeline {
|
||||
|
|
@ -986,72 +1072,13 @@ public:
|
|||
~MDComputePipeline() final = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDFrameBuffer {
|
||||
Vector<MTL::Texture> textures;
|
||||
|
||||
public:
|
||||
Size2i size;
|
||||
MDFrameBuffer(Vector<MTL::Texture> p_textures, Size2i p_size) :
|
||||
textures(p_textures), size(p_size) {}
|
||||
MDFrameBuffer() {}
|
||||
|
||||
/// Returns the texture at the given index.
|
||||
_ALWAYS_INLINE_ MTL::Texture get_texture(uint32_t p_idx) const {
|
||||
return textures[p_idx];
|
||||
}
|
||||
|
||||
/// Returns true if the texture at the given index is not nil.
|
||||
_ALWAYS_INLINE_ bool has_texture(uint32_t p_idx) const {
|
||||
return textures[p_idx] != nil;
|
||||
}
|
||||
|
||||
/// Set the texture at the given index.
|
||||
_ALWAYS_INLINE_ void set_texture(uint32_t p_idx, MTL::Texture p_texture) {
|
||||
textures.write[p_idx] = p_texture;
|
||||
}
|
||||
|
||||
/// Unset or nil the texture at the given index.
|
||||
_ALWAYS_INLINE_ void unset_texture(uint32_t p_idx) {
|
||||
textures.write[p_idx] = nil;
|
||||
}
|
||||
|
||||
/// Resizes buffers to the specified size.
|
||||
_ALWAYS_INLINE_ void set_texture_count(uint32_t p_size) {
|
||||
textures.resize(p_size);
|
||||
}
|
||||
|
||||
virtual ~MDFrameBuffer() = default;
|
||||
};
|
||||
|
||||
// These functions are used to convert between Objective-C objects and
|
||||
// the RIDs used by Godot, respecting automatic reference counting.
|
||||
namespace rid {
|
||||
|
||||
// Converts an Objective-C object to a pointer, and incrementing the
|
||||
// reference count.
|
||||
_FORCE_INLINE_ void *owned(id p_id) {
|
||||
return (__bridge_retained void *)p_id;
|
||||
}
|
||||
|
||||
#define MAKE_ID(FROM, TO) \
|
||||
_FORCE_INLINE_ TO make(FROM p_obj) { \
|
||||
return TO(owned(p_obj)); \
|
||||
}
|
||||
|
||||
MAKE_ID(id<MTLTexture>, RDD::TextureID)
|
||||
MAKE_ID(id<MTLBuffer>, RDD::BufferID)
|
||||
MAKE_ID(id<MTLSamplerState>, RDD::SamplerID)
|
||||
MAKE_ID(MTLVertexDescriptor *, RDD::VertexFormatID)
|
||||
MAKE_ID(id<MTLCommandQueue>, RDD::CommandPoolID)
|
||||
|
||||
// Converts a pointer to an Objective-C object without changing the reference count.
|
||||
_FORCE_INLINE_ auto get(RDD::ID p_id) {
|
||||
return (p_id.id) ? (__bridge ::id)(void *)p_id.id : nil;
|
||||
}
|
||||
|
||||
// Converts a pointer to an Objective-C object, and decrements the reference count.
|
||||
_FORCE_INLINE_ auto release(RDD::ID p_id) {
|
||||
return (__bridge_transfer ::id)(void *)p_id.id;
|
||||
}
|
||||
|
||||
} // namespace rid
|
||||
#undef MAKE_ID
|
||||
} //namespace rid
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue