summaryrefslogtreecommitdiffstats
path: root/drivers/metal/metal_objects.h
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/metal/metal_objects.h')
-rw-r--r--drivers/metal/metal_objects.h838
1 files changed, 838 insertions, 0 deletions
diff --git a/drivers/metal/metal_objects.h b/drivers/metal/metal_objects.h
new file mode 100644
index 0000000000..70f86f2fac
--- /dev/null
+++ b/drivers/metal/metal_objects.h
@@ -0,0 +1,838 @@
+/**************************************************************************/
+/* metal_objects.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+/**************************************************************************/
+/* */
+/* Portions of this code were derived from MoltenVK. */
+/* */
+/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
+/* (http://www.brenwill.com) */
+/* */
+/* Licensed under the Apache License, Version 2.0 (the "License"); */
+/* you may not use this file except in compliance with the License. */
+/* You may obtain a copy of the License at */
+/* */
+/* http://www.apache.org/licenses/LICENSE-2.0 */
+/* */
+/* Unless required by applicable law or agreed to in writing, software */
+/* distributed under the License is distributed on an "AS IS" BASIS, */
+/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
+/* implied. See the License for the specific language governing */
+/* permissions and limitations under the License. */
+/**************************************************************************/
+
+#ifndef METAL_OBJECTS_H
+#define METAL_OBJECTS_H
+
+#import "metal_device_properties.h"
+#import "metal_utils.h"
+#import "pixel_formats.h"
+
+#import "servers/rendering/rendering_device_driver.h"
+
+#import <Foundation/Foundation.h>
+#import <Metal/Metal.h>
+#import <QuartzCore/CAMetalLayer.h>
+#import <simd/simd.h>
+#import <initializer_list>
+#import <optional>
+#import <spirv.hpp>
+
+// 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 class MDCommandBufferStateType {
+ None,
+ Render,
+ Compute,
+ Blit,
+};
+
+enum class MDPipelineType {
+ None,
+ Render,
+ Compute,
+};
+
+class MDRenderPass;
+class MDPipeline;
+class MDRenderPipeline;
+class MDComputePipeline;
+class MDFrameBuffer;
+class RenderingDeviceDriverMetal;
+class MDUniformSet;
+class MDShader;
+
+#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;
+
+ 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_ 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 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(sample_count);
+ h = hash_murmur3_buffer(pixel_formats, ATTACHMENT_COUNT * sizeof(pixel_formats[0]), h);
+ return h;
+ }
+};
+
+class API_AVAILABLE(macos(11.0), ios(14.0)) MDResourceFactory {
+private:
+ RenderingDeviceDriverMetal *device_driver;
+
+ id<MTLFunction> new_func(NSString *p_source, NSString *p_name, NSError **p_error);
+ id<MTLFunction> new_clear_vert_func(ClearAttKey &p_key);
+ id<MTLFunction> new_clear_frag_func(ClearAttKey &p_key);
+ NSString *get_format_type_string(MTLPixelFormat p_fmt);
+
+public:
+ id<MTLRenderPipelineState> new_clear_pipeline_state(ClearAttKey &p_key, NSError **p_error);
+ id<MTLDepthStencilState> new_depth_stencil_state(bool p_use_depth, bool p_use_stencil);
+
+ MDResourceFactory(RenderingDeviceDriverMetal *p_device_driver) :
+ device_driver(p_device_driver) {}
+ ~MDResourceFactory() = default;
+};
+
+class API_AVAILABLE(macos(11.0), ios(14.0)) MDResourceCache {
+private:
+ typedef HashMap<ClearAttKey, id<MTLRenderPipelineState>, HashableHasher<ClearAttKey>> HashMap;
+ std::unique_ptr<MDResourceFactory> resource_factory;
+ HashMap clear_states;
+
+ struct {
+ id<MTLDepthStencilState> all;
+ id<MTLDepthStencilState> depth_only;
+ id<MTLDepthStencilState> stencil_only;
+ id<MTLDepthStencilState> none;
+ } clear_depth_stencil_state;
+
+public:
+ id<MTLRenderPipelineState> get_clear_render_pipeline_state(ClearAttKey &p_key, NSError **p_error);
+ id<MTLDepthStencilState> get_depth_stencil_state(bool p_use_depth, bool p_use_stencil);
+
+ explicit MDResourceCache(RenderingDeviceDriverMetal *p_device_driver) :
+ resource_factory(new MDResourceFactory(p_device_driver)) {}
+ ~MDResourceCache() = default;
+};
+
+class API_AVAILABLE(macos(11.0), ios(14.0)) MDCommandBuffer {
+private:
+ RenderingDeviceDriverMetal *device_driver = nullptr;
+ id<MTLCommandQueue> queue = nil;
+ id<MTLCommandBuffer> commandBuffer = nil;
+
+ void _end_compute_dispatch();
+ void _end_blit();
+
+#pragma mark - Render
+
+ void _render_set_dirty_state();
+ void _render_bind_uniform_sets();
+
+ static void _populate_vertices(simd::float4 *p_vertices, Size2i p_fb_size, VectorView<Rect2i> p_rects);
+ static uint32_t _populate_vertices(simd::float4 *p_vertices, uint32_t p_index, Rect2i const &p_rect, Size2i p_fb_size);
+ void _end_render_pass();
+ void _render_clear_render_area();
+
+public:
+ MDCommandBufferStateType type = MDCommandBufferStateType::None;
+
+ struct RenderState {
+ MDRenderPass *pass = nullptr;
+ MDFrameBuffer *frameBuffer = nullptr;
+ MDRenderPipeline *pipeline = nullptr;
+ LocalVector<RDD::RenderPassClearValue> clear_values;
+ LocalVector<MTLViewport> viewports;
+ LocalVector<MTLScissorRect> scissors;
+ std::optional<Color> blend_constants;
+ uint32_t current_subpass = UINT32_MAX;
+ Rect2i render_area = {};
+ bool is_rendering_entire_area = false;
+ MTLRenderPassDescriptor *desc = nil;
+ id<MTLRenderCommandEncoder> encoder = nil;
+ id<MTLBuffer> __unsafe_unretained index_buffer = nil; // Buffer is owned by RDD.
+ MTLIndexType index_type = MTLIndexTypeUInt16;
+ LocalVector<id<MTLBuffer> __unsafe_unretained> vertex_buffers;
+ LocalVector<NSUInteger> vertex_offsets;
+ // clang-format off
+ enum DirtyFlag: uint8_t {
+ DIRTY_NONE = 0b0000'0000,
+ DIRTY_PIPELINE = 0b0000'0001, //! pipeline state
+ DIRTY_UNIFORMS = 0b0000'0010, //! uniform sets
+ DIRTY_DEPTH = 0b0000'0100, //! depth / stenci state
+ DIRTY_VERTEX = 0b0000'1000, //! vertex buffers
+ DIRTY_VIEWPORT = 0b0001'0000, //! viewport rectangles
+ DIRTY_SCISSOR = 0b0010'0000, //! scissor rectangles
+ DIRTY_BLEND = 0b0100'0000, //! blend state
+ DIRTY_RASTER = 0b1000'0000, //! encoder state like cull mode
+
+ DIRTY_ALL = 0xff,
+ };
+ // clang-format on
+ BitField<DirtyFlag> dirty = DIRTY_NONE;
+
+ LocalVector<MDUniformSet *> uniform_sets;
+ // Bit mask of the uniform sets that are dirty, to prevent redundant binding.
+ uint64_t uniform_set_mask = 0;
+
+ _FORCE_INLINE_ void reset() {
+ pass = nil;
+ frameBuffer = nil;
+ pipeline = nil;
+ current_subpass = UINT32_MAX;
+ render_area = {};
+ is_rendering_entire_area = false;
+ desc = nil;
+ encoder = nil;
+ index_buffer = nil;
+ index_type = MTLIndexTypeUInt16;
+ dirty = DIRTY_NONE;
+ uniform_sets.clear();
+ uniform_set_mask = 0;
+ clear_values.clear();
+ viewports.clear();
+ scissors.clear();
+ blend_constants.reset();
+ vertex_buffers.clear();
+ vertex_offsets.clear();
+ }
+
+ _FORCE_INLINE_ void mark_viewport_dirty() {
+ if (viewports.is_empty()) {
+ return;
+ }
+ dirty.set_flag(DirtyFlag::DIRTY_VIEWPORT);
+ }
+
+ _FORCE_INLINE_ void mark_scissors_dirty() {
+ if (scissors.is_empty()) {
+ return;
+ }
+ dirty.set_flag(DirtyFlag::DIRTY_SCISSOR);
+ }
+
+ _FORCE_INLINE_ void mark_vertex_dirty() {
+ if (vertex_buffers.is_empty()) {
+ return;
+ }
+ dirty.set_flag(DirtyFlag::DIRTY_VERTEX);
+ }
+
+ _FORCE_INLINE_ void mark_uniforms_dirty(std::initializer_list<uint32_t> l) {
+ if (uniform_sets.is_empty()) {
+ return;
+ }
+ for (uint32_t i : l) {
+ if (i < uniform_sets.size() && uniform_sets[i] != nullptr) {
+ uniform_set_mask |= 1 << i;
+ }
+ }
+ dirty.set_flag(DirtyFlag::DIRTY_UNIFORMS);
+ }
+
+ _FORCE_INLINE_ void mark_uniforms_dirty(void) {
+ if (uniform_sets.is_empty()) {
+ return;
+ }
+ for (uint32_t i = 0; i < uniform_sets.size(); i++) {
+ if (uniform_sets[i] != nullptr) {
+ uniform_set_mask |= 1 << i;
+ }
+ }
+ dirty.set_flag(DirtyFlag::DIRTY_UNIFORMS);
+ }
+
+ MTLScissorRect clip_to_render_area(MTLScissorRect p_rect) const {
+ uint32_t raLeft = render_area.position.x;
+ uint32_t raRight = raLeft + render_area.size.width;
+ uint32_t raBottom = render_area.position.y;
+ uint32_t raTop = raBottom + render_area.size.height;
+
+ p_rect.x = CLAMP(p_rect.x, raLeft, MAX(raRight - 1, raLeft));
+ p_rect.y = CLAMP(p_rect.y, raBottom, MAX(raTop - 1, raBottom));
+ p_rect.width = MIN(p_rect.width, raRight - p_rect.x);
+ p_rect.height = MIN(p_rect.height, raTop - p_rect.y);
+
+ return p_rect;
+ }
+
+ Rect2i clip_to_render_area(Rect2i p_rect) const {
+ int32_t raLeft = render_area.position.x;
+ int32_t raRight = raLeft + render_area.size.width;
+ int32_t raBottom = render_area.position.y;
+ int32_t raTop = raBottom + render_area.size.height;
+
+ p_rect.position.x = CLAMP(p_rect.position.x, raLeft, MAX(raRight - 1, raLeft));
+ p_rect.position.y = CLAMP(p_rect.position.y, raBottom, MAX(raTop - 1, raBottom));
+ p_rect.size.width = MIN(p_rect.size.width, raRight - p_rect.position.x);
+ p_rect.size.height = MIN(p_rect.size.height, raTop - p_rect.position.y);
+
+ return p_rect;
+ }
+
+ } render;
+
+ // State specific for a compute pass.
+ struct {
+ MDComputePipeline *pipeline = nullptr;
+ id<MTLComputeCommandEncoder> encoder = nil;
+ _FORCE_INLINE_ void reset() {
+ pipeline = nil;
+ encoder = nil;
+ }
+ } compute;
+
+ // State specific to a blit pass.
+ struct {
+ id<MTLBlitCommandEncoder> encoder = nil;
+ _FORCE_INLINE_ void reset() {
+ encoder = nil;
+ }
+ } blit;
+
+ _FORCE_INLINE_ id<MTLCommandBuffer> get_command_buffer() const {
+ return commandBuffer;
+ }
+
+ void begin();
+ void commit();
+ void end();
+
+ id<MTLBlitCommandEncoder> blit_command_encoder();
+ void encodeRenderCommandEncoderWithDescriptor(MTLRenderPassDescriptor *p_desc, NSString *p_label);
+
+ void bind_pipeline(RDD::PipelineID p_pipeline);
+
+#pragma mark - Render Commands
+
+ void render_bind_uniform_set(RDD::UniformSetID p_uniform_set, RDD::ShaderID p_shader, uint32_t p_set_index);
+ void render_clear_attachments(VectorView<RDD::AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects);
+ void render_set_viewport(VectorView<Rect2i> p_viewports);
+ void render_set_scissor(VectorView<Rect2i> p_scissors);
+ void render_set_blend_constants(const Color &p_constants);
+ void render_begin_pass(RDD::RenderPassID p_render_pass,
+ RDD::FramebufferID p_frameBuffer,
+ RDD::CommandBufferType p_cmd_buffer_type,
+ const Rect2i &p_rect,
+ VectorView<RDD::RenderPassClearValue> p_clear_values);
+ void render_next_subpass();
+ void render_draw(uint32_t p_vertex_count,
+ uint32_t p_instance_count,
+ uint32_t p_base_vertex,
+ uint32_t p_first_instance);
+ void render_bind_vertex_buffers(uint32_t p_binding_count, const RDD::BufferID *p_buffers, const uint64_t *p_offsets);
+ void render_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint64_t p_offset);
+
+ void render_draw_indexed(uint32_t p_index_count,
+ uint32_t p_instance_count,
+ uint32_t p_first_index,
+ int32_t p_vertex_offset,
+ uint32_t p_first_instance);
+
+ void render_draw_indexed_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride);
+ void render_draw_indexed_indirect_count(RDD::BufferID p_indirect_buffer, uint64_t p_offset, RDD::BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride);
+ void render_draw_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride);
+ void render_draw_indirect_count(RDD::BufferID p_indirect_buffer, uint64_t p_offset, RDD::BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride);
+
+ void render_end_pass();
+
+#pragma mark - Compute Commands
+
+ void compute_bind_uniform_set(RDD::UniformSetID p_uniform_set, RDD::ShaderID p_shader, uint32_t p_set_index);
+ 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);
+
+ MDCommandBuffer(id<MTLCommandQueue> p_queue, RenderingDeviceDriverMetal *p_device_driver) :
+ device_driver(p_device_driver), queue(p_queue) {
+ type = MDCommandBufferStateType::None;
+ }
+
+ MDCommandBuffer() = default;
+};
+
+#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 140000) || (TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED < 170000)
+#define MTLBindingAccess MTLArgumentAccess
+#define MTLBindingAccessReadOnly MTLArgumentAccessReadOnly
+#define MTLBindingAccessReadWrite MTLArgumentAccessReadWrite
+#define MTLBindingAccessWriteOnly MTLArgumentAccessWriteOnly
+#endif
+
+struct API_AVAILABLE(macos(11.0), ios(14.0)) BindingInfo {
+ MTLDataType dataType = MTLDataTypeNone;
+ uint32_t index = 0;
+ MTLBindingAccess access = MTLBindingAccessReadOnly;
+ MTLResourceUsage usage = 0;
+ MTLTextureType textureType = MTLTextureType2D;
+ spv::ImageFormat imageFormat = spv::ImageFormatUnknown;
+ uint32_t arrayLength = 0;
+ bool isMultisampled = false;
+
+ 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;
+ }
+
+ size_t serialize_size() const {
+ return sizeof(uint32_t) * 8 /* 8 uint32_t fields */;
+ }
+
+ template <typename W>
+ void serialize(W &p_writer) const {
+ p_writer.write((uint32_t)dataType);
+ p_writer.write(index);
+ p_writer.write((uint32_t)access);
+ p_writer.write((uint32_t)usage);
+ p_writer.write((uint32_t)textureType);
+ p_writer.write(imageFormat);
+ p_writer.write(arrayLength);
+ p_writer.write(isMultisampled);
+ }
+
+ template <typename R>
+ void deserialize(R &p_reader) {
+ p_reader.read((uint32_t &)dataType);
+ p_reader.read(index);
+ p_reader.read((uint32_t &)access);
+ p_reader.read((uint32_t &)usage);
+ p_reader.read((uint32_t &)textureType);
+ p_reader.read((uint32_t &)imageFormat);
+ p_reader.read(arrayLength);
+ p_reader.read(isMultisampled);
+ }
+};
+
+using RDC = RenderingDeviceCommons;
+
+typedef API_AVAILABLE(macos(11.0), ios(14.0)) HashMap<RDC::ShaderStage, BindingInfo> BindingInfoMap;
+
+struct API_AVAILABLE(macos(11.0), ios(14.0)) UniformInfo {
+ uint32_t binding;
+ ShaderStageUsage active_stages = None;
+ BindingInfoMap bindings;
+ BindingInfoMap bindings_secondary;
+};
+
+struct API_AVAILABLE(macos(11.0), ios(14.0)) UniformSet {
+ LocalVector<UniformInfo> uniforms;
+ uint32_t buffer_size = 0;
+ HashMap<RDC::ShaderStage, uint32_t> offsets;
+ HashMap<RDC::ShaderStage, id<MTLArgumentEncoder>> encoders;
+};
+
+class API_AVAILABLE(macos(11.0), ios(14.0)) MDShader {
+public:
+ CharString name;
+ Vector<UniformSet> sets;
+
+ virtual void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) = 0;
+
+ MDShader(CharString p_name, Vector<UniformSet> p_sets) :
+ name(p_name), sets(p_sets) {}
+ virtual ~MDShader() = default;
+};
+
+class API_AVAILABLE(macos(11.0), ios(14.0)) MDComputeShader final : public MDShader {
+public:
+ struct {
+ uint32_t binding = -1;
+ uint32_t size = 0;
+ } push_constants;
+ MTLSize local = {};
+
+ id<MTLLibrary> kernel;
+#if DEV_ENABLED
+ CharString kernel_source;
+#endif
+
+ void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) final;
+
+ MDComputeShader(CharString p_name, Vector<UniformSet> p_sets, id<MTLLibrary> p_kernel);
+ ~MDComputeShader() override = default;
+};
+
+class API_AVAILABLE(macos(11.0), ios(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;
+
+ id<MTLLibrary> vert;
+ id<MTLLibrary> frag;
+#if DEV_ENABLED
+ CharString vert_source;
+ CharString frag_source;
+#endif
+
+ void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) final;
+
+ MDRenderShader(CharString p_name, Vector<UniformSet> p_sets, id<MTLLibrary> p_vert, id<MTLLibrary> p_frag);
+ ~MDRenderShader() override = default;
+};
+
+enum StageResourceUsage : uint32_t {
+ VertexRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_VERTEX * 2),
+ VertexWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_VERTEX * 2),
+ FragmentRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_FRAGMENT * 2),
+ FragmentWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_FRAGMENT * 2),
+ TesselationControlRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
+ TesselationControlWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
+ TesselationEvaluationRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
+ TesselationEvaluationWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
+ ComputeRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_COMPUTE * 2),
+ ComputeWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_COMPUTE * 2),
+};
+
+_FORCE_INLINE_ StageResourceUsage &operator|=(StageResourceUsage &p_a, uint32_t p_b) {
+ p_a = StageResourceUsage(uint32_t(p_a) | p_b);
+ return p_a;
+}
+
+_FORCE_INLINE_ StageResourceUsage stage_resource_usage(RDC::ShaderStage p_stage, MTLResourceUsage p_usage) {
+ return StageResourceUsage(p_usage << (p_stage * 2));
+}
+
+_FORCE_INLINE_ MTLResourceUsage resource_usage_for_stage(StageResourceUsage p_usage, RDC::ShaderStage p_stage) {
+ return MTLResourceUsage((p_usage >> (p_stage * 2)) & 0b11);
+}
+
+template <>
+struct HashMapComparatorDefault<RDD::ShaderID> {
+ static bool compare(const RDD::ShaderID &p_lhs, const RDD::ShaderID &p_rhs) {
+ return p_lhs.id == p_rhs.id;
+ }
+};
+
+struct BoundUniformSet {
+ id<MTLBuffer> buffer;
+ HashMap<id<MTLResource>, StageResourceUsage> bound_resources;
+};
+
+class API_AVAILABLE(macos(11.0), ios(14.0)) MDUniformSet {
+public:
+ uint32_t index;
+ LocalVector<RDD::BoundUniform> uniforms;
+ HashMap<MDShader *, BoundUniformSet> bound_uniforms;
+
+ BoundUniformSet &boundUniformSetForShader(MDShader *p_shader, id<MTLDevice> p_device);
+};
+
+enum class MDAttachmentType : uint8_t {
+ None = 0,
+ Color = 1 << 0,
+ Depth = 1 << 1,
+ Stencil = 1 << 2,
+};
+
+_FORCE_INLINE_ MDAttachmentType &operator|=(MDAttachmentType &p_a, MDAttachmentType p_b) {
+ flags::set(p_a, p_b);
+ return p_a;
+}
+
+_FORCE_INLINE_ bool operator&(MDAttachmentType p_a, MDAttachmentType p_b) {
+ return uint8_t(p_a) & uint8_t(p_b);
+}
+
+struct MDSubpass {
+ uint32_t subpass_index = 0;
+ LocalVector<RDD::AttachmentReference> input_references;
+ LocalVector<RDD::AttachmentReference> color_references;
+ RDD::AttachmentReference depth_stencil_reference;
+ LocalVector<RDD::AttachmentReference> resolve_references;
+
+ MTLFmtCaps getRequiredFmtCapsForAttachmentAt(uint32_t p_index) const;
+};
+
+struct API_AVAILABLE(macos(11.0), ios(14.0)) MDAttachment {
+private:
+ uint32_t index = 0;
+ uint32_t firstUseSubpassIndex = 0;
+ uint32_t lastUseSubpassIndex = 0;
+
+public:
+ MTLPixelFormat format = MTLPixelFormatInvalid;
+ MDAttachmentType type = MDAttachmentType::None;
+ MTLLoadAction loadAction = MTLLoadActionDontCare;
+ MTLStoreAction storeAction = MTLStoreActionDontCare;
+ MTLLoadAction stencilLoadAction = MTLLoadActionDontCare;
+ MTLStoreAction stencilStoreAction = MTLStoreActionDontCare;
+ uint32_t samples = 1;
+
+ /*!
+ * @brief Returns true if this attachment is first used in the given subpass.
+ * @param p_subpass
+ * @return
+ */
+ _FORCE_INLINE_ bool isFirstUseOf(MDSubpass const &p_subpass) const {
+ return p_subpass.subpass_index == firstUseSubpassIndex;
+ }
+
+ /*!
+ * @brief Returns true if this attachment is last used in the given subpass.
+ * @param p_subpass
+ * @return
+ */
+ _FORCE_INLINE_ bool isLastUseOf(MDSubpass const &p_subpass) const {
+ return p_subpass.subpass_index == lastUseSubpassIndex;
+ }
+
+ void linkToSubpass(MDRenderPass const &p_pass);
+
+ MTLStoreAction getMTLStoreAction(MDSubpass const &p_subpass,
+ bool p_is_rendering_entire_area,
+ bool p_has_resolve,
+ bool p_can_resolve,
+ bool p_is_stencil) const;
+ bool configureDescriptor(MTLRenderPassAttachmentDescriptor *p_desc,
+ PixelFormats &p_pf,
+ MDSubpass const &p_subpass,
+ id<MTLTexture> p_attachment,
+ bool p_is_rendering_entire_area,
+ bool p_has_resolve,
+ bool p_can_resolve,
+ bool p_is_stencil) const;
+ /** Returns whether this attachment should be cleared in the subpass. */
+ bool shouldClear(MDSubpass const &p_subpass, bool p_is_stencil) const;
+};
+
+class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderPass {
+public:
+ Vector<MDAttachment> attachments;
+ Vector<MDSubpass> subpasses;
+
+ uint32_t get_sample_count() const {
+ return attachments.is_empty() ? 1 : attachments[0].samples;
+ }
+
+ MDRenderPass(Vector<MDAttachment> &p_attachments, Vector<MDSubpass> &p_subpasses);
+};
+
+class API_AVAILABLE(macos(11.0), ios(14.0)) MDPipeline {
+public:
+ MDPipelineType type;
+
+ explicit MDPipeline(MDPipelineType p_type) :
+ type(p_type) {}
+ virtual ~MDPipeline() = default;
+};
+
+class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderPipeline final : public MDPipeline {
+public:
+ id<MTLRenderPipelineState> state = nil;
+ id<MTLDepthStencilState> depth_stencil = nil;
+ uint32_t push_constant_size = 0;
+ uint32_t push_constant_stages_mask = 0;
+ SampleCount sample_count = SampleCount1;
+
+ struct {
+ MTLCullMode cull_mode = MTLCullModeNone;
+ MTLTriangleFillMode fill_mode = MTLTriangleFillModeFill;
+ MTLDepthClipMode clip_mode = MTLDepthClipModeClip;
+ MTLWinding winding = MTLWindingClockwise;
+ MTLPrimitiveType render_primitive = MTLPrimitiveTypePoint;
+
+ struct {
+ bool enabled = false;
+ } depth_test;
+
+ struct {
+ bool enabled = false;
+ float depth_bias = 0.0;
+ float slope_scale = 0.0;
+ float clamp = 0.0;
+ _FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
+ if (!enabled) {
+ return;
+ }
+ [p_enc setDepthBias:depth_bias slopeScale:slope_scale clamp:clamp];
+ }
+ } depth_bias;
+
+ struct {
+ bool enabled = false;
+ uint32_t front_reference = 0;
+ uint32_t back_reference = 0;
+ _FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
+ if (!enabled)
+ return;
+ [p_enc setStencilFrontReferenceValue:front_reference backReferenceValue:back_reference];
+ };
+ } stencil;
+
+ struct {
+ bool enabled = false;
+ float r = 0.0;
+ float g = 0.0;
+ float b = 0.0;
+ float a = 0.0;
+
+ _FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
+ //if (!enabled)
+ // return;
+ [p_enc setBlendColorRed:r green:g blue:b alpha:a];
+ };
+ } blend;
+
+ _FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
+ [p_enc setCullMode:cull_mode];
+ [p_enc setTriangleFillMode:fill_mode];
+ [p_enc setDepthClipMode:clip_mode];
+ [p_enc setFrontFacingWinding:winding];
+ depth_bias.apply(p_enc);
+ stencil.apply(p_enc);
+ blend.apply(p_enc);
+ }
+
+ } raster_state;
+
+ MDRenderShader *shader = nil;
+
+ MDRenderPipeline() :
+ MDPipeline(MDPipelineType::Render) {}
+ ~MDRenderPipeline() final = default;
+};
+
+class API_AVAILABLE(macos(11.0), ios(14.0)) MDComputePipeline final : public MDPipeline {
+public:
+ id<MTLComputePipelineState> state = nil;
+ struct {
+ MTLSize local = {};
+ } compute_state;
+
+ MDComputeShader *shader = nil;
+
+ explicit MDComputePipeline(id<MTLComputePipelineState> p_state) :
+ MDPipeline(MDPipelineType::Compute), state(p_state) {}
+ ~MDComputePipeline() final = default;
+};
+
+class API_AVAILABLE(macos(11.0), ios(14.0)) MDFrameBuffer {
+public:
+ Vector<MTL::Texture> textures;
+ Size2i size;
+ MDFrameBuffer(Vector<MTL::Texture> p_textures, Size2i p_size) :
+ textures(p_textures), size(p_size) {}
+ MDFrameBuffer() {}
+
+ 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
+
+#endif // METAL_OBJECTS_H