mirror of
https://github.com/godotengine/godot.git
synced 2026-02-07 03:15:13 +00:00
Metal: Refactor for future Metal 4, switch to C++; fix dynamic uniforms
This commit is contained in:
@@ -432,6 +432,11 @@ Comment: meshoptimizer
|
||||
Copyright: 2016-2024, Arseny Kapoulkine
|
||||
License: Expat
|
||||
|
||||
Files: thirdparty/metal-cpp/*
|
||||
Comment: metal-cpp
|
||||
Copyright: 2024, Apple Inc.
|
||||
License: Apache-2.0
|
||||
|
||||
Files: thirdparty/mingw-std-threads/*
|
||||
Comment: mingw-std-threads
|
||||
Copyright: 2016, Mega Limited
|
||||
|
||||
@@ -30,7 +30,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/NSString.h>
|
||||
#else
|
||||
#include <Foundation/NSString.hpp>
|
||||
#endif
|
||||
|
||||
class String;
|
||||
template <typename T>
|
||||
@@ -38,12 +42,20 @@ class CharStringT;
|
||||
|
||||
using CharString = CharStringT<char>;
|
||||
|
||||
template <typename T>
|
||||
class Span;
|
||||
|
||||
namespace conv {
|
||||
|
||||
#ifdef __OBJC__
|
||||
/**
|
||||
* Converts a Godot String to an NSString without allocating an intermediate UTF-8 buffer.
|
||||
* */
|
||||
NSString *to_nsstring(const String &p_str);
|
||||
/**
|
||||
* Converts a Godot String to an NSString without allocating an intermediate UTF-8 buffer.
|
||||
* */
|
||||
NSString *to_nsstring(Span<char> p_str);
|
||||
/**
|
||||
* Converts a Godot CharString to an NSString without allocating an intermediate UTF-8 buffer.
|
||||
* */
|
||||
@@ -52,5 +64,24 @@ NSString *to_nsstring(const CharString &p_str);
|
||||
* Converts an NSString to a Godot String without allocating intermediate buffers.
|
||||
* */
|
||||
String to_string(NSString *p_str);
|
||||
#else
|
||||
/**
|
||||
* Converts a Godot String to an NSString without allocating an intermediate UTF-8 buffer.
|
||||
* */
|
||||
NS::String *to_nsstring(const String &p_str);
|
||||
/**
|
||||
* Converts a Godot String to an NSString without allocating an intermediate UTF-8 buffer.
|
||||
* */
|
||||
NS::String *to_nsstring(Span<char> p_str);
|
||||
/**
|
||||
* Converts a Godot CharString to an NSString without allocating an intermediate UTF-8 buffer.
|
||||
* */
|
||||
NS::String *to_nsstring(const CharString &p_str);
|
||||
/**
|
||||
* Converts an NSString to a Godot String without allocating intermediate buffers.
|
||||
* */
|
||||
String to_string(NS::String *p_str);
|
||||
|
||||
#endif
|
||||
|
||||
} //namespace conv
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#import "foundation_helpers.h"
|
||||
|
||||
#import "core/string/ustring.h"
|
||||
#import "core/templates/span.h"
|
||||
|
||||
#import <CoreFoundation/CFString.h>
|
||||
|
||||
@@ -42,6 +43,12 @@ NSString *to_nsstring(const String &p_str) {
|
||||
encoding:NSUTF32LittleEndianStringEncoding];
|
||||
}
|
||||
|
||||
NSString *to_nsstring(Span<char> p_str) {
|
||||
return [[NSString alloc] initWithBytes:(const void *)p_str.ptr()
|
||||
length:p_str.size()
|
||||
encoding:NSASCIIStringEncoding];
|
||||
}
|
||||
|
||||
NSString *to_nsstring(const CharString &p_str) {
|
||||
return [[NSString alloc] initWithBytes:(const void *)p_str.ptr()
|
||||
length:p_str.length()
|
||||
|
||||
@@ -27,6 +27,9 @@ setup_swift_builder(
|
||||
vulkan_dir = "#thirdparty/vulkan"
|
||||
env_apple_embedded.Prepend(CPPPATH=[vulkan_dir, vulkan_dir + "/include"])
|
||||
|
||||
# Use bundled metal-cpp headers
|
||||
env_apple_embedded.Prepend(CPPPATH=["#thirdparty/metal-cpp"])
|
||||
|
||||
# Driver source files
|
||||
env_apple_embedded.add_source_files(env_apple_embedded.drivers_sources, "*.mm")
|
||||
env_apple_embedded.add_source_files(env_apple_embedded.drivers_sources, "*.swift")
|
||||
|
||||
@@ -96,7 +96,7 @@ DisplayServerAppleEmbedded::DisplayServerAppleEmbedded(const String &p_rendering
|
||||
if (rendering_driver == "metal") {
|
||||
if (@available(iOS 14.0, *)) {
|
||||
layer = [GDTAppDelegateService.viewController.godotView initializeRenderingForDriver:@"metal"];
|
||||
wpd.metal.layer = (CAMetalLayer *)layer;
|
||||
wpd.metal.layer = (__bridge CA::MetalLayer *)layer;
|
||||
rendering_context = memnew(RenderingContextDriverMetal);
|
||||
} else {
|
||||
OS::get_singleton()->alert("Metal is only supported on iOS 14.0 and later.");
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <dlfcn.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <iterator>
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
|
||||
|
||||
@@ -3,6 +3,9 @@ from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
# If we're using Metal, we need metal-cpp.
|
||||
env.Prepend(CPPPATH=["#thirdparty/metal-cpp/"])
|
||||
|
||||
env_metal = env.Clone()
|
||||
|
||||
# Thirdparty source files
|
||||
@@ -23,6 +26,11 @@ thirdparty_sources = [
|
||||
]
|
||||
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
|
||||
|
||||
# Include metal-cpp
|
||||
thirdparty_sources += [
|
||||
"#thirdparty/metal-cpp/metal_cpp.cpp",
|
||||
]
|
||||
|
||||
env_metal.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include"])
|
||||
env_metal.Prepend(CPPPATH=[thirdparty_spirv_headers_dir + "include/spirv/unified1"])
|
||||
|
||||
@@ -48,7 +56,6 @@ env_metal.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
|
||||
|
||||
driver_obj = []
|
||||
|
||||
env_metal.add_source_files(driver_obj, "*.mm")
|
||||
env_metal.add_source_files(driver_obj, "*.cpp")
|
||||
env.drivers_sources += driver_obj
|
||||
|
||||
|
||||
1806
drivers/metal/metal3_objects.cpp
Normal file
1806
drivers/metal/metal3_objects.cpp
Normal file
File diff suppressed because it is too large
Load Diff
590
drivers/metal/metal3_objects.h
Normal file
590
drivers/metal/metal3_objects.h
Normal file
@@ -0,0 +1,590 @@
|
||||
/**************************************************************************/
|
||||
/* metal3_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "metal_objects_shared.h"
|
||||
|
||||
#include "servers/rendering/rendering_device_driver.h"
|
||||
|
||||
#include <Metal/Metal.hpp>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <optional>
|
||||
|
||||
namespace MTL3 {
|
||||
|
||||
// These types are defined in the global namespace (metal_objects_shared.h / rendering_device_driver_metal.h)
|
||||
using ::MDAttachment;
|
||||
using ::MDAttachmentType;
|
||||
using ::MDCommandBufferBase;
|
||||
using ::MDCommandBufferStateType;
|
||||
using ::MDFrameBuffer;
|
||||
using ::MDRenderPass;
|
||||
using ::MDRingBuffer;
|
||||
using ::MDSubpass;
|
||||
using ::RenderStateBase;
|
||||
|
||||
using ::DynamicOffsetLayout;
|
||||
using ::MDComputePipeline;
|
||||
using ::MDComputeShader;
|
||||
using ::MDLibrary;
|
||||
using ::MDPipeline;
|
||||
using ::MDPipelineType;
|
||||
using ::MDRenderPipeline;
|
||||
using ::MDRenderShader;
|
||||
using ::MDShader;
|
||||
using ::MDUniformSet;
|
||||
using ::ShaderCacheEntry;
|
||||
using ::ShaderLoadStrategy;
|
||||
using ::UniformInfo;
|
||||
using ::UniformSet;
|
||||
|
||||
using RDM = ::RenderingDeviceDriverMetal;
|
||||
|
||||
struct ResourceUsageEntry {
|
||||
StageResourceUsage usage = ResourceUnused;
|
||||
uint32_t unused = 0;
|
||||
|
||||
ResourceUsageEntry() {}
|
||||
ResourceUsageEntry(StageResourceUsage p_usage) :
|
||||
usage(p_usage) {}
|
||||
};
|
||||
|
||||
} // namespace MTL3
|
||||
|
||||
template <>
|
||||
struct is_zero_constructible<MTL3::ResourceUsageEntry> : std::true_type {};
|
||||
|
||||
namespace MTL3 {
|
||||
|
||||
/*! Track the cumulative usage for a resource during a render or compute pass */
|
||||
typedef HashMap<MTL::Resource *, 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(MTL::RenderCommandEncoder *p_enc);
|
||||
void encode(MTL::ComputeCommandEncoder *p_enc);
|
||||
void reset();
|
||||
};
|
||||
|
||||
struct BindingCache {
|
||||
struct BufferBinding {
|
||||
MTL::Buffer *buffer = nullptr;
|
||||
NS::UInteger offset = 0;
|
||||
|
||||
bool operator!=(const BufferBinding &p_other) const {
|
||||
return buffer != p_other.buffer || offset != p_other.offset;
|
||||
}
|
||||
};
|
||||
|
||||
LocalVector<MTL::Texture *> textures;
|
||||
LocalVector<MTL::SamplerState *> 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(NS::Range p_range, MTL::Texture *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 (NS::UInteger i = 0; i < p_range.length; ++i) {
|
||||
uint32_t slot = (uint32_t)(p_range.location + i);
|
||||
MTL::Texture *value = p_values[i];
|
||||
if (textures[slot] != value) {
|
||||
textures[slot] = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool update(NS::Range p_range, MTL::SamplerState *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 (NS::UInteger i = 0; i < p_range.length; ++i) {
|
||||
uint32_t slot = (uint32_t)(p_range.location + i);
|
||||
MTL::SamplerState *value = p_values[i];
|
||||
if (samplers[slot] != value) {
|
||||
samplers[slot] = value;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool update(NS::Range p_range, MTL::Buffer *const *p_values, const NS::UInteger *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 (NS::UInteger 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(MTL::Buffer *p_buffer, NS::UInteger 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;
|
||||
}
|
||||
};
|
||||
|
||||
// A type used to encode resources directly to a MTLCommandEncoder
|
||||
struct DirectEncoder {
|
||||
MTL::CommandEncoder *encoder;
|
||||
BindingCache &cache;
|
||||
enum Mode {
|
||||
RENDER,
|
||||
COMPUTE
|
||||
};
|
||||
Mode mode;
|
||||
|
||||
void set(MTL::Buffer **p_buffers, const NS::UInteger *p_offsets, NS::Range p_range);
|
||||
void set(MTL::Buffer *p_buffer, NS::UInteger p_offset, uint32_t p_index);
|
||||
void set(MTL::Texture **p_textures, NS::Range p_range);
|
||||
void set(MTL::SamplerState **p_samplers, NS::Range p_range);
|
||||
|
||||
DirectEncoder(MTL::CommandEncoder *p_encoder, BindingCache &p_cache, Mode p_mode) :
|
||||
encoder(p_encoder), cache(p_cache), mode(p_mode) {}
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MDCommandBuffer : public MDCommandBufferBase {
|
||||
friend class MDUniformSet;
|
||||
|
||||
private:
|
||||
#pragma mark - Common State
|
||||
|
||||
BindingCache binding_cache;
|
||||
|
||||
#pragma mark - Argument Buffer Ring Allocator
|
||||
|
||||
using Alloc = MDRingBuffer::Allocation;
|
||||
|
||||
// Used for argument buffers that contain dynamic uniforms.
|
||||
MDRingBuffer _scratch;
|
||||
|
||||
/// Allocates from the ring buffer for dynamic argument buffers.
|
||||
Alloc allocate_arg_buffer(uint32_t p_size);
|
||||
|
||||
struct {
|
||||
NS::SharedPtr<MTL::ResidencySet> rs;
|
||||
} _frame_state;
|
||||
|
||||
#pragma mark - Synchronization
|
||||
|
||||
enum {
|
||||
STAGE_RENDER,
|
||||
STAGE_COMPUTE,
|
||||
STAGE_BLIT,
|
||||
STAGE_MAX,
|
||||
};
|
||||
bool use_barriers = false;
|
||||
MTL::Stages pending_after_stages[STAGE_MAX] = { 0, 0, 0 };
|
||||
MTL::Stages pending_before_queue_stages[STAGE_MAX] = { 0, 0, 0 };
|
||||
void _encode_barrier(MTL::CommandEncoder *p_enc);
|
||||
|
||||
void reset();
|
||||
|
||||
MTL::CommandQueue *queue = nullptr;
|
||||
NS::SharedPtr<MTL::CommandBuffer> commandBuffer;
|
||||
bool state_begin = false;
|
||||
|
||||
MTL::CommandBuffer *command_buffer();
|
||||
|
||||
void _end_compute_dispatch();
|
||||
void _end_blit();
|
||||
MTL::BlitCommandEncoder *_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
|
||||
|
||||
void _render_set_dirty_state();
|
||||
void _render_bind_uniform_sets();
|
||||
void _bind_uniforms_argument_buffers(MDUniformSet *p_set, MDShader *p_shader, uint32_t p_set_index, uint32_t p_dynamic_offsets);
|
||||
void _bind_uniforms_direct(MDUniformSet *p_set, MDShader *p_shader, DirectEncoder p_enc, uint32_t p_set_index, uint32_t p_dynamic_offsets);
|
||||
|
||||
#pragma mark - Compute
|
||||
|
||||
void _compute_set_dirty_state();
|
||||
void _compute_bind_uniform_sets();
|
||||
void _bind_uniforms_argument_buffers_compute(MDUniformSet *p_set, MDShader *p_shader, uint32_t p_set_index, uint32_t p_dynamic_offsets);
|
||||
|
||||
protected:
|
||||
void mark_push_constants_dirty() override;
|
||||
RenderStateBase &get_render_state_base() override { return render; }
|
||||
uint32_t get_current_view_count() const override { return render.get_subpass().view_count; }
|
||||
MDRenderPass *get_render_pass() const override { return render.pass; }
|
||||
MDFrameBuffer *get_frame_buffer() const override { return render.frameBuffer; }
|
||||
const MDSubpass &get_current_subpass() const override { return render.get_subpass(); }
|
||||
LocalVector<RDD::RenderPassClearValue> &get_clear_values() override { return render.clear_values; }
|
||||
const Rect2i &get_render_area() const override { return render.render_area; }
|
||||
void end_render_encoding() override { render.end_encoding(); }
|
||||
|
||||
public:
|
||||
struct RenderState : public RenderStateBase {
|
||||
MDRenderPass *pass = nullptr;
|
||||
MDFrameBuffer *frameBuffer = nullptr;
|
||||
MDRenderPipeline *pipeline = nullptr;
|
||||
LocalVector<RDD::RenderPassClearValue> clear_values;
|
||||
uint32_t current_subpass = UINT32_MAX;
|
||||
Rect2i render_area = {};
|
||||
bool is_rendering_entire_area = false;
|
||||
NS::SharedPtr<MTL::RenderPassDescriptor> desc;
|
||||
NS::SharedPtr<MTL::RenderCommandEncoder> encoder;
|
||||
MTL::Buffer *index_buffer = nullptr; // Buffer is owned by RDD.
|
||||
MTL::IndexType index_type = MTL::IndexTypeUInt16;
|
||||
uint32_t index_offset = 0;
|
||||
LocalVector<MTL::Buffer *> vertex_buffers;
|
||||
LocalVector<NS::UInteger> vertex_offsets;
|
||||
ResourceTracker resource_tracker;
|
||||
|
||||
LocalVector<MDUniformSet *> uniform_sets;
|
||||
uint32_t dynamic_offsets = 0;
|
||||
// Bit mask of the uniform sets that are dirty, to prevent redundant binding.
|
||||
uint64_t uniform_set_mask = 0;
|
||||
|
||||
_FORCE_INLINE_ void reset();
|
||||
void end_encoding();
|
||||
|
||||
_ALWAYS_INLINE_ const MDSubpass &get_subpass() const {
|
||||
DEV_ASSERT(pass != nullptr);
|
||||
return pass->subpasses[current_subpass];
|
||||
}
|
||||
|
||||
_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);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_blend_dirty() {
|
||||
if (!blend_constants.has_value()) {
|
||||
return;
|
||||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_BLEND);
|
||||
}
|
||||
|
||||
MTL::ScissorRect clip_to_render_area(MTL::ScissorRect 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 ComputeState {
|
||||
MDComputePipeline *pipeline = nullptr;
|
||||
NS::SharedPtr<MTL::ComputeCommandEncoder> encoder;
|
||||
ResourceTracker resource_tracker;
|
||||
// clang-format off
|
||||
enum DirtyFlag: uint16_t {
|
||||
DIRTY_NONE = 0,
|
||||
DIRTY_PIPELINE = 1 << 0, //! pipeline state
|
||||
DIRTY_UNIFORMS = 1 << 1, //! uniform sets
|
||||
DIRTY_PUSH = 1 << 2, //! push constants
|
||||
DIRTY_ALL = (1 << 3) - 1,
|
||||
};
|
||||
// clang-format on
|
||||
BitField<DirtyFlag> dirty = DIRTY_NONE;
|
||||
|
||||
LocalVector<MDUniformSet *> uniform_sets;
|
||||
uint32_t dynamic_offsets = 0;
|
||||
// Bit mask of the uniform sets that are dirty, to prevent redundant binding.
|
||||
uint64_t uniform_set_mask = 0;
|
||||
|
||||
_FORCE_INLINE_ void reset();
|
||||
void end_encoding();
|
||||
|
||||
_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);
|
||||
}
|
||||
} compute;
|
||||
|
||||
// State specific to a blit pass.
|
||||
struct {
|
||||
NS::SharedPtr<MTL::BlitCommandEncoder> encoder;
|
||||
_FORCE_INLINE_ void reset() {
|
||||
encoder.reset();
|
||||
}
|
||||
} blit;
|
||||
|
||||
_FORCE_INLINE_ MTL::CommandBuffer *get_command_buffer() const {
|
||||
return commandBuffer.get();
|
||||
}
|
||||
|
||||
void begin() override;
|
||||
void commit() override;
|
||||
void end() override;
|
||||
|
||||
void bind_pipeline(RDD::PipelineID p_pipeline) override;
|
||||
|
||||
#pragma mark - Render Commands
|
||||
|
||||
void render_bind_uniform_sets(VectorView<RDD::UniformSetID> p_uniform_sets, RDD::ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count, uint32_t p_dynamic_offsets) override;
|
||||
void render_clear_attachments(VectorView<RDD::AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) override;
|
||||
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) override;
|
||||
void render_next_subpass() override;
|
||||
void render_draw(uint32_t p_vertex_count,
|
||||
uint32_t p_instance_count,
|
||||
uint32_t p_base_vertex,
|
||||
uint32_t p_first_instance) override;
|
||||
void render_bind_vertex_buffers(uint32_t p_binding_count, const RDD::BufferID *p_buffers, const uint64_t *p_offsets, uint64_t p_dynamic_offsets) override;
|
||||
void render_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint64_t p_offset) override;
|
||||
|
||||
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) override;
|
||||
|
||||
void render_draw_indexed_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override;
|
||||
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) override;
|
||||
void render_draw_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override;
|
||||
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) override;
|
||||
|
||||
void render_end_pass() override;
|
||||
|
||||
#pragma mark - Compute Commands
|
||||
|
||||
void compute_bind_uniform_sets(VectorView<RDD::UniformSetID> p_uniform_sets, RDD::ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count, uint32_t p_dynamic_offsets) override;
|
||||
void compute_dispatch(uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) override;
|
||||
void compute_dispatch_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset) override;
|
||||
|
||||
#pragma mark - Transfer
|
||||
|
||||
private:
|
||||
MTL::RenderCommandEncoder *get_new_render_encoder_with_descriptor(MTL::RenderPassDescriptor *p_desc);
|
||||
|
||||
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) override;
|
||||
void clear_color_texture(RDD::TextureID p_texture, RDD::TextureLayout p_texture_layout, const Color &p_color, const RDD::TextureSubresourceRange &p_subresources) override;
|
||||
void clear_depth_stencil_texture(RDD::TextureID p_texture, RDD::TextureLayout p_texture_layout, float p_depth, uint8_t p_stencil, const RDD::TextureSubresourceRange &p_subresources) override;
|
||||
void clear_buffer(RDD::BufferID p_buffer, uint64_t p_offset, uint64_t p_size) override;
|
||||
void copy_buffer(RDD::BufferID p_src_buffer, RDD::BufferID p_dst_buffer, VectorView<RDD::BufferCopyRegion> p_regions) override;
|
||||
void copy_texture(RDD::TextureID p_src_texture, RDD::TextureID p_dst_texture, VectorView<RDD::TextureCopyRegion> p_regions) override;
|
||||
void copy_buffer_to_texture(RDD::BufferID p_src_buffer, RDD::TextureID p_dst_texture, VectorView<RDD::BufferTextureCopyRegion> p_regions) override;
|
||||
void copy_texture_to_buffer(RDD::TextureID p_src_texture, RDD::BufferID p_dst_buffer, VectorView<RDD::BufferTextureCopyRegion> p_regions) override;
|
||||
|
||||
#pragma mark - Synchronization
|
||||
|
||||
void pipeline_barrier(BitField<RDD::PipelineStageBits> p_src_stages,
|
||||
BitField<RDD::PipelineStageBits> p_dst_stages,
|
||||
VectorView<RDD::MemoryAccessBarrier> p_memory_barriers,
|
||||
VectorView<RDD::BufferBarrier> p_buffer_barriers,
|
||||
VectorView<RDD::TextureBarrier> p_texture_barriers,
|
||||
VectorView<RDD::AccelerationStructureBarrier> p_acceleration_structure_barriers) override;
|
||||
|
||||
#pragma mark - Debugging
|
||||
|
||||
void begin_label(const char *p_label_name, const Color &p_color) override;
|
||||
void end_label() override;
|
||||
|
||||
MDCommandBuffer(MTL::CommandQueue *p_queue, ::RenderingDeviceDriverMetal *p_device_driver);
|
||||
MDCommandBuffer() = default;
|
||||
};
|
||||
|
||||
} // namespace MTL3
|
||||
|
||||
// C++ helper to get mipmap level size from texture
|
||||
_FORCE_INLINE_ static MTL::Size mipmapLevelSizeFromTexture(MTL::Texture *p_tex, NS::UInteger p_level) {
|
||||
MTL::Size lvlSize;
|
||||
lvlSize.width = MAX(p_tex->width() >> p_level, 1UL);
|
||||
lvlSize.height = MAX(p_tex->height() >> p_level, 1UL);
|
||||
lvlSize.depth = MAX(p_tex->depth() >> p_level, 1UL);
|
||||
return lvlSize;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**************************************************************************/
|
||||
/* metal_device_properties.mm */
|
||||
/* metal_device_properties.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
@@ -48,41 +48,46 @@
|
||||
/* permissions and limitations under the License. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "metal_device_properties.h"
|
||||
#include "metal_device_properties.h"
|
||||
|
||||
#import "metal_utils.h"
|
||||
#include "metal_utils.h"
|
||||
|
||||
#import "servers/rendering/renderer_rd/effects/metal_fx.h"
|
||||
#include "servers/rendering/renderer_rd/effects/metal_fx.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <MetalFX/MetalFX.h>
|
||||
#import <spirv_cross.hpp>
|
||||
#import <spirv_msl.hpp>
|
||||
#include <MetalFX/MetalFX.hpp>
|
||||
#include <spirv_cross.hpp>
|
||||
#include <spirv_msl.hpp>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
// Common scaling multipliers.
|
||||
#define KIBI (1024)
|
||||
#define MEBI (KIBI * KIBI)
|
||||
|
||||
#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 140000) || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < 170000)
|
||||
#define MTLGPUFamilyApple9 (MTLGPUFamily)1009
|
||||
constexpr MTL::GPUFamily GPUFamilyApple9 = static_cast<MTL::GPUFamily>(1009);
|
||||
#else
|
||||
constexpr MTL::GPUFamily GPUFamilyApple9 = MTL::GPUFamilyApple9;
|
||||
#endif
|
||||
|
||||
API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0), visionos(1.0))
|
||||
MTLGPUFamily &operator--(MTLGPUFamily &p_family) {
|
||||
p_family = static_cast<MTLGPUFamily>(static_cast<int>(p_family) - 1);
|
||||
if (p_family < MTLGPUFamilyApple1) {
|
||||
p_family = MTLGPUFamilyApple9;
|
||||
MTL::GPUFamily &operator--(MTL::GPUFamily &p_family) {
|
||||
p_family = static_cast<MTL::GPUFamily>(static_cast<int>(p_family) - 1);
|
||||
if (p_family < MTL::GPUFamilyApple1) {
|
||||
p_family = GPUFamilyApple9;
|
||||
}
|
||||
|
||||
return p_family;
|
||||
}
|
||||
|
||||
void MetalDeviceProperties::init_features(id<MTLDevice> p_device) {
|
||||
void MetalDeviceProperties::init_features(MTL::Device *p_device) {
|
||||
features = {};
|
||||
|
||||
MTLCompileOptions *opts = [MTLCompileOptions new];
|
||||
features.msl_max_version = make_msl_version((opts.languageVersion >> 0x10) & 0xff, (opts.languageVersion >> 0x00) & 0xff);
|
||||
MTL::CompileOptions *opts = MTL::CompileOptions::alloc()->init();
|
||||
MTL::LanguageVersion lang_version = opts->languageVersion();
|
||||
features.msl_max_version = make_msl_version((static_cast<uint32_t>(lang_version) >> 0x10) & 0xff, (static_cast<uint32_t>(lang_version) >> 0x00) & 0xff);
|
||||
features.msl_target_version = features.msl_max_version;
|
||||
opts->release();
|
||||
if (String version = OS::get_singleton()->get_environment("GODOT_MTL_TARGET_VERSION"); !version.is_empty()) {
|
||||
if (version != "max") {
|
||||
Vector<String> parts = version.split(".", true, 2);
|
||||
@@ -102,55 +107,55 @@ void MetalDeviceProperties::init_features(id<MTLDevice> p_device) {
|
||||
}
|
||||
}
|
||||
|
||||
features.highestFamily = MTLGPUFamilyApple1;
|
||||
for (MTLGPUFamily family = MTLGPUFamilyApple9; family >= MTLGPUFamilyApple1; --family) {
|
||||
if ([p_device supportsFamily:family]) {
|
||||
features.highestFamily = MTL::GPUFamilyApple1;
|
||||
for (MTL::GPUFamily family = GPUFamilyApple9; family >= MTL::GPUFamilyApple1; --family) {
|
||||
if (p_device->supportsFamily(family)) {
|
||||
features.highestFamily = family;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (@available(macOS 11, iOS 16.4, tvOS 16.4, *)) {
|
||||
features.supportsBCTextureCompression = p_device.supportsBCTextureCompression;
|
||||
if (__builtin_available(macOS 11, iOS 16.4, tvOS 16.4, *)) {
|
||||
features.supportsBCTextureCompression = p_device->supportsBCTextureCompression();
|
||||
} else {
|
||||
features.supportsBCTextureCompression = false;
|
||||
}
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
features.supportsDepth24Stencil8 = p_device.isDepth24Stencil8PixelFormatSupported;
|
||||
features.supportsDepth24Stencil8 = p_device->isDepth24Stencil8PixelFormatSupported();
|
||||
#endif
|
||||
|
||||
if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
|
||||
features.supports32BitFloatFiltering = p_device.supports32BitFloatFiltering;
|
||||
features.supports32BitMSAA = p_device.supports32BitMSAA;
|
||||
if (__builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
|
||||
features.supports32BitFloatFiltering = p_device->supports32BitFloatFiltering();
|
||||
features.supports32BitMSAA = p_device->supports32BitMSAA();
|
||||
}
|
||||
|
||||
if (@available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
|
||||
if (__builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
|
||||
features.supports_gpu_address = true;
|
||||
}
|
||||
|
||||
features.hostMemoryPageSize = sysconf(_SC_PAGESIZE);
|
||||
|
||||
for (SampleCount sc = SampleCount1; sc <= SampleCount64; sc <<= 1) {
|
||||
if ([p_device supportsTextureSampleCount:sc]) {
|
||||
if (p_device->supportsTextureSampleCount(sc)) {
|
||||
features.supportedSampleCounts |= sc;
|
||||
}
|
||||
}
|
||||
|
||||
features.layeredRendering = [p_device supportsFamily:MTLGPUFamilyApple5];
|
||||
features.multisampleLayeredRendering = [p_device supportsFamily:MTLGPUFamilyApple7];
|
||||
features.tessellationShader = [p_device supportsFamily:MTLGPUFamilyApple3];
|
||||
features.imageCubeArray = [p_device supportsFamily:MTLGPUFamilyApple3];
|
||||
features.quadPermute = [p_device supportsFamily:MTLGPUFamilyApple4];
|
||||
features.simdPermute = [p_device supportsFamily:MTLGPUFamilyApple6];
|
||||
features.simdReduction = [p_device supportsFamily:MTLGPUFamilyApple7];
|
||||
features.argument_buffers_tier = p_device.argumentBuffersSupport;
|
||||
features.supports_image_atomic_32_bit = [p_device supportsFamily:MTLGPUFamilyApple6];
|
||||
features.supports_image_atomic_64_bit = [p_device supportsFamily:MTLGPUFamilyApple9] || ([p_device supportsFamily:MTLGPUFamilyApple8] && [p_device supportsFamily:MTLGPUFamilyMac2]);
|
||||
features.layeredRendering = p_device->supportsFamily(MTL::GPUFamilyApple5);
|
||||
features.multisampleLayeredRendering = p_device->supportsFamily(MTL::GPUFamilyApple7);
|
||||
features.tessellationShader = p_device->supportsFamily(MTL::GPUFamilyApple3);
|
||||
features.imageCubeArray = p_device->supportsFamily(MTL::GPUFamilyApple3);
|
||||
features.quadPermute = p_device->supportsFamily(MTL::GPUFamilyApple4);
|
||||
features.simdPermute = p_device->supportsFamily(MTL::GPUFamilyApple6);
|
||||
features.simdReduction = p_device->supportsFamily(MTL::GPUFamilyApple7);
|
||||
features.argument_buffers_tier = p_device->argumentBuffersSupport();
|
||||
features.supports_image_atomic_32_bit = p_device->supportsFamily(MTL::GPUFamilyApple6);
|
||||
features.supports_image_atomic_64_bit = p_device->supportsFamily(GPUFamilyApple9) || (p_device->supportsFamily(MTL::GPUFamilyApple8) && p_device->supportsFamily(MTL::GPUFamilyMac2));
|
||||
|
||||
if (features.msl_target_version >= MSL_VERSION_31) {
|
||||
// Native atomics are only supported on 3.1 and above.
|
||||
if (@available(macOS 14.0, iOS 17.0, tvOS 17.0, visionOS 1.0, *)) {
|
||||
if (__builtin_available(macOS 14.0, iOS 17.0, tvOS 17.0, visionOS 1.0, *)) {
|
||||
features.supports_native_image_atomics = true;
|
||||
}
|
||||
}
|
||||
@@ -159,31 +164,31 @@ void MetalDeviceProperties::init_features(id<MTLDevice> p_device) {
|
||||
features.supports_native_image_atomics = false;
|
||||
}
|
||||
|
||||
if (@available(macOS 15.0, iOS 18.0, tvOS 18.0, visionOS 2.0, *)) {
|
||||
if (__builtin_available(macOS 15.0, iOS 18.0, tvOS 18.0, visionOS 2.0, *)) {
|
||||
features.supports_residency_sets = true;
|
||||
} else {
|
||||
features.supports_residency_sets = false;
|
||||
}
|
||||
|
||||
if (@available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
|
||||
features.needs_arg_encoders = !([p_device supportsFamily:MTLGPUFamilyMetal3] && features.argument_buffers_tier == MTLArgumentBuffersTier2);
|
||||
if (__builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
|
||||
features.needs_arg_encoders = !(p_device->supportsFamily(MTL::GPUFamilyMetal3) && features.argument_buffers_tier == MTL::ArgumentBuffersTier2);
|
||||
}
|
||||
|
||||
if (String v = OS::get_singleton()->get_environment("GODOT_MTL_DISABLE_ARGUMENT_BUFFERS"); v == "1") {
|
||||
features.use_argument_buffers = false;
|
||||
}
|
||||
|
||||
if (@available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
|
||||
features.metal_fx_spatial = [MTLFXSpatialScalerDescriptor supportsDevice:p_device];
|
||||
if (__builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
|
||||
features.metal_fx_spatial = MTLFX::SpatialScalerDescriptor::supportsDevice(p_device);
|
||||
#ifdef METAL_MFXTEMPORAL_ENABLED
|
||||
features.metal_fx_temporal = [MTLFXTemporalScalerDescriptor supportsDevice:p_device];
|
||||
features.metal_fx_temporal = MTLFX::TemporalScalerDescriptor::supportsDevice(p_device);
|
||||
#else
|
||||
features.metal_fx_temporal = false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
|
||||
void MetalDeviceProperties::init_limits(MTL::Device *p_device) {
|
||||
using std::max;
|
||||
using std::min;
|
||||
|
||||
@@ -191,7 +196,7 @@ void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
|
||||
|
||||
// FST: Maximum number of layers per 1D texture array, 2D texture array, or 3D texture.
|
||||
limits.maxImageArrayLayers = 2048;
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple3]) {
|
||||
if (p_device->supportsFamily(MTL::GPUFamilyApple3)) {
|
||||
// FST: Maximum 2D texture width and height.
|
||||
limits.maxFramebufferWidth = 16384;
|
||||
limits.maxFramebufferHeight = 16384;
|
||||
@@ -219,7 +224,7 @@ void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
|
||||
// FST: Maximum 3D texture width, height, and depth.
|
||||
limits.maxImageDimension3D = 2048;
|
||||
|
||||
limits.maxThreadsPerThreadGroup = p_device.maxThreadsPerThreadgroup;
|
||||
limits.maxThreadsPerThreadGroup = p_device->maxThreadsPerThreadgroup();
|
||||
// No effective limits.
|
||||
limits.maxComputeWorkGroupCount = { std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max() };
|
||||
// https://github.com/KhronosGroup/MoltenVK/blob/568cc3acc0e2299931fdaecaaa1fc3ec5b4af281/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h#L85
|
||||
@@ -228,25 +233,25 @@ void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
|
||||
limits.maxColorAttachments = 8;
|
||||
|
||||
// Maximum number of textures the device can access, per stage, from an argument buffer.
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
|
||||
if (p_device->supportsFamily(MTL::GPUFamilyApple6)) {
|
||||
limits.maxTexturesPerArgumentBuffer = 1'000'000;
|
||||
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
|
||||
} else if (p_device->supportsFamily(MTL::GPUFamilyApple4)) {
|
||||
limits.maxTexturesPerArgumentBuffer = 96;
|
||||
} else {
|
||||
limits.maxTexturesPerArgumentBuffer = 31;
|
||||
}
|
||||
|
||||
// Maximum number of samplers the device can access, per stage, from an argument buffer.
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
|
||||
if (p_device->supportsFamily(MTL::GPUFamilyApple6)) {
|
||||
limits.maxSamplersPerArgumentBuffer = 1024;
|
||||
} else {
|
||||
limits.maxSamplersPerArgumentBuffer = 16;
|
||||
}
|
||||
|
||||
// Maximum number of buffers the device can access, per stage, from an argument buffer.
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
|
||||
if (p_device->supportsFamily(MTL::GPUFamilyApple6)) {
|
||||
limits.maxBuffersPerArgumentBuffer = std::numeric_limits<uint64_t>::max();
|
||||
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
|
||||
} else if (p_device->supportsFamily(MTL::GPUFamilyApple4)) {
|
||||
limits.maxBuffersPerArgumentBuffer = 96;
|
||||
} else {
|
||||
limits.maxBuffersPerArgumentBuffer = 31;
|
||||
@@ -283,13 +288,13 @@ void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_QUAD_BIT);
|
||||
}
|
||||
|
||||
limits.maxBufferLength = p_device.maxBufferLength;
|
||||
limits.maxBufferLength = p_device->maxBufferLength();
|
||||
|
||||
// FST: Maximum size of vertex descriptor layout stride.
|
||||
limits.maxVertexDescriptorLayoutStride = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
// Maximum number of viewports.
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple5]) {
|
||||
if (p_device->supportsFamily(MTL::GPUFamilyApple5)) {
|
||||
limits.maxViewports = 16;
|
||||
} else {
|
||||
limits.maxViewports = 1;
|
||||
@@ -297,9 +302,9 @@ void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
|
||||
|
||||
limits.maxPerStageBufferCount = 31;
|
||||
limits.maxPerStageSamplerCount = 16;
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
|
||||
if (p_device->supportsFamily(MTL::GPUFamilyApple6)) {
|
||||
limits.maxPerStageTextureCount = 128;
|
||||
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
|
||||
} else if (p_device->supportsFamily(MTL::GPUFamilyApple4)) {
|
||||
limits.maxPerStageTextureCount = 96;
|
||||
} else {
|
||||
limits.maxPerStageTextureCount = 31;
|
||||
@@ -310,9 +315,9 @@ void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
|
||||
limits.maxVertexInputBindingStride = (2 * KIBI);
|
||||
limits.maxShaderVaryings = 31; // Accurate on Apple4 and above. See: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
|
||||
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
|
||||
if (p_device->supportsFamily(MTL::GPUFamilyApple4)) {
|
||||
limits.maxThreadGroupMemoryAllocation = 32768;
|
||||
} else if ([p_device supportsFamily:MTLGPUFamilyApple3]) {
|
||||
} else if (p_device->supportsFamily(MTL::GPUFamilyApple3)) {
|
||||
limits.maxThreadGroupMemoryAllocation = 16384;
|
||||
} else {
|
||||
limits.maxThreadGroupMemoryAllocation = 16352;
|
||||
@@ -330,9 +335,9 @@ void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
|
||||
limits.maxDrawIndexedIndexValue = std::numeric_limits<uint32_t>::max() - 1;
|
||||
|
||||
#ifdef METAL_MFXTEMPORAL_ENABLED
|
||||
if (@available(macOS 14.0, iOS 17.0, tvOS 17.0, *)) {
|
||||
limits.temporalScalerInputContentMinScale = (double)[MTLFXTemporalScalerDescriptor supportedInputContentMinScaleForDevice:p_device];
|
||||
limits.temporalScalerInputContentMaxScale = (double)[MTLFXTemporalScalerDescriptor supportedInputContentMaxScaleForDevice:p_device];
|
||||
if (__builtin_available(macOS 14.0, iOS 17.0, tvOS 17.0, *)) {
|
||||
limits.temporalScalerInputContentMinScale = MTLFX::TemporalScalerDescriptor::supportedInputContentMinScale(p_device);
|
||||
limits.temporalScalerInputContentMaxScale = MTLFX::TemporalScalerDescriptor::supportedInputContentMaxScale(p_device);
|
||||
} else {
|
||||
// Defaults taken from macOS 14+
|
||||
limits.temporalScalerInputContentMinScale = 1.0;
|
||||
@@ -346,11 +351,11 @@ void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
|
||||
}
|
||||
|
||||
void MetalDeviceProperties::init_os_props() {
|
||||
NSOperatingSystemVersion ver = NSProcessInfo.processInfo.operatingSystemVersion;
|
||||
NS::OperatingSystemVersion ver = NS::ProcessInfo::processInfo()->operatingSystemVersion();
|
||||
os_version = (uint32_t)ver.majorVersion * 10000 + (uint32_t)ver.minorVersion * 100 + (uint32_t)ver.patchVersion;
|
||||
}
|
||||
|
||||
MetalDeviceProperties::MetalDeviceProperties(id<MTLDevice> p_device) {
|
||||
MetalDeviceProperties::MetalDeviceProperties(MTL::Device *p_device) {
|
||||
init_features(p_device);
|
||||
init_limits(p_device);
|
||||
init_os_props();
|
||||
@@ -50,16 +50,16 @@
|
||||
/* permissions and limitations under the License. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "servers/rendering/rendering_device.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
#include <Metal/Metal.hpp>
|
||||
#include <cstddef>
|
||||
|
||||
/** The buffer index to use for vertex content. */
|
||||
const static uint32_t VERT_CONTENT_BUFFER_INDEX = 0;
|
||||
const static uint32_t MAX_COLOR_ATTACHMENT_COUNT = 8;
|
||||
|
||||
typedef NS_OPTIONS(NSUInteger, SampleCount) {
|
||||
enum SampleCount : NS::UInteger {
|
||||
SampleCount1 = (1UL << 0),
|
||||
SampleCount2 = (1UL << 1),
|
||||
SampleCount4 = (1UL << 2),
|
||||
@@ -69,6 +69,22 @@ typedef NS_OPTIONS(NSUInteger, SampleCount) {
|
||||
SampleCount64 = (1UL << 6),
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ SampleCount operator|(SampleCount a, SampleCount b) {
|
||||
return static_cast<SampleCount>(static_cast<NS::UInteger>(a) | static_cast<NS::UInteger>(b));
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ SampleCount &operator|=(SampleCount &a, SampleCount b) {
|
||||
return a = a | b;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ SampleCount operator<<(SampleCount a, int shift) {
|
||||
return static_cast<SampleCount>(static_cast<NS::UInteger>(a) << shift);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ SampleCount &operator<<=(SampleCount &a, int shift) {
|
||||
return a = a << shift;
|
||||
}
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalFeatures {
|
||||
/// Maximum version of the Metal Shading Language version available.
|
||||
uint32_t msl_max_version = 0;
|
||||
@@ -78,7 +94,7 @@ struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalFeatures {
|
||||
* for engine developers for testing.
|
||||
*/
|
||||
uint32_t msl_target_version = 0;
|
||||
MTLGPUFamily highestFamily = MTLGPUFamilyApple4;
|
||||
MTL::GPUFamily highestFamily = MTL::GPUFamilyApple4;
|
||||
bool supportsBCTextureCompression = false;
|
||||
bool supportsDepth24Stencil8 = false;
|
||||
bool supports32BitFloatFiltering = false;
|
||||
@@ -93,7 +109,7 @@ struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalFeatures {
|
||||
bool simdReduction = false; /**< If true, SIMD-group reduction functions (arithmetic) are supported in shaders. */
|
||||
bool tessellationShader = false; /**< If true, tessellation shaders are supported. */
|
||||
bool imageCubeArray = false; /**< If true, image cube arrays are supported. */
|
||||
MTLArgumentBuffersTier argument_buffers_tier = MTLArgumentBuffersTier1;
|
||||
MTL::ArgumentBuffersTier argument_buffers_tier = MTL::ArgumentBuffersTier1;
|
||||
bool needs_arg_encoders = true; /**< If true, argument encoders are required to encode arguments into an argument buffer. */
|
||||
bool use_argument_buffers = true; /**< If true, argument buffers are can be used instead of slot binding, if available. */
|
||||
bool metal_fx_spatial = false; /**< If true, Metal FX spatial functions are supported. */
|
||||
@@ -108,7 +124,7 @@ struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalFeatures {
|
||||
* Check if argument buffers are fully supported, which requires tier 2 support and no need for argument encoders.
|
||||
*/
|
||||
_FORCE_INLINE_ bool argument_buffers_supported() const {
|
||||
return argument_buffers_tier == MTLArgumentBuffersTier2 && needs_arg_encoders == false;
|
||||
return argument_buffers_tier == MTL::ArgumentBuffersTier2 && needs_arg_encoders == false;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -129,8 +145,8 @@ struct MetalLimits {
|
||||
uint64_t maxImageDimensionCube;
|
||||
uint64_t maxViewportDimensionX;
|
||||
uint64_t maxViewportDimensionY;
|
||||
MTLSize maxThreadsPerThreadGroup;
|
||||
MTLSize maxComputeWorkGroupCount;
|
||||
MTL::Size maxThreadsPerThreadGroup;
|
||||
MTL::Size maxComputeWorkGroupCount;
|
||||
uint64_t maxBoundDescriptorSets;
|
||||
uint64_t maxColorAttachments;
|
||||
uint64_t maxTexturesPerArgumentBuffer;
|
||||
@@ -161,8 +177,8 @@ struct MetalLimits {
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalDeviceProperties {
|
||||
private:
|
||||
void init_features(id<MTLDevice> p_device);
|
||||
void init_limits(id<MTLDevice> p_device);
|
||||
void init_features(MTL::Device *p_device);
|
||||
void init_limits(MTL::Device *p_device);
|
||||
void init_os_props();
|
||||
|
||||
public:
|
||||
@@ -174,7 +190,7 @@ public:
|
||||
|
||||
SampleCount find_nearest_supported_sample_count(RenderingDevice::TextureSamples p_samples) const;
|
||||
|
||||
MetalDeviceProperties(id<MTLDevice> p_device);
|
||||
MetalDeviceProperties(MTL::Device *p_device);
|
||||
~MetalDeviceProperties();
|
||||
|
||||
private:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
897
drivers/metal/metal_objects_shared.cpp
Normal file
897
drivers/metal/metal_objects_shared.cpp
Normal file
@@ -0,0 +1,897 @@
|
||||
/**************************************************************************/
|
||||
/* metal_objects_shared.cpp */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "metal_objects_shared.h"
|
||||
|
||||
#include "rendering_device_driver_metal.h"
|
||||
|
||||
#include <os/signpost.h>
|
||||
#include <simd/simd.h>
|
||||
#include <string>
|
||||
|
||||
#pragma mark - Resource Factory
|
||||
|
||||
NS::SharedPtr<MTL::Function> MDResourceFactory::new_func(NS::String *p_source, NS::String *p_name, NS::Error **p_error) {
|
||||
NS::SharedPtr<NS::AutoreleasePool> pool = NS::TransferPtr(NS::AutoreleasePool::alloc()->init());
|
||||
NS::SharedPtr<MTL::CompileOptions> options = NS::TransferPtr(MTL::CompileOptions::alloc()->init());
|
||||
NS::Error *err = nullptr;
|
||||
NS::SharedPtr<MTL::Library> mtlLib = NS::TransferPtr(device->newLibrary(p_source, options.get(), &err));
|
||||
if (err) {
|
||||
if (p_error != nullptr) {
|
||||
*p_error = err;
|
||||
}
|
||||
}
|
||||
return NS::TransferPtr(mtlLib->newFunction(p_name));
|
||||
}
|
||||
|
||||
NS::SharedPtr<MTL::Function> MDResourceFactory::new_clear_vert_func(ClearAttKey &p_key) {
|
||||
NS::SharedPtr<NS::AutoreleasePool> pool = NS::TransferPtr(NS::AutoreleasePool::alloc()->init());
|
||||
char msl[1024];
|
||||
snprintf(msl, sizeof(msl), R"(
|
||||
#include <metal_stdlib>
|
||||
using namespace metal;
|
||||
|
||||
typedef struct {
|
||||
float4 a_position [[attribute(0)]];
|
||||
} AttributesPos;
|
||||
|
||||
typedef struct {
|
||||
float4 colors[9];
|
||||
} ClearColorsIn;
|
||||
|
||||
typedef struct {
|
||||
float4 v_position [[position]];
|
||||
uint layer%s;
|
||||
} VaryingsPos;
|
||||
|
||||
vertex VaryingsPos vertClear(AttributesPos attributes [[stage_in]], constant ClearColorsIn& ccIn [[buffer(0)]]) {
|
||||
VaryingsPos varyings;
|
||||
varyings.v_position = float4(attributes.a_position.x, -attributes.a_position.y, ccIn.colors[%d].r, 1.0);
|
||||
varyings.layer = uint(attributes.a_position.w);
|
||||
return varyings;
|
||||
}
|
||||
)",
|
||||
p_key.is_layered_rendering_enabled() ? " [[render_target_array_index]]" : "", ClearAttKey::DEPTH_INDEX);
|
||||
|
||||
return new_func(NS::String::string(msl, NS::UTF8StringEncoding), MTLSTR("vertClear"), nullptr);
|
||||
}
|
||||
|
||||
NS::SharedPtr<MTL::Function> MDResourceFactory::new_clear_frag_func(ClearAttKey &p_key) {
|
||||
NS::SharedPtr<NS::AutoreleasePool> pool = NS::TransferPtr(NS::AutoreleasePool::alloc()->init());
|
||||
std::string msl;
|
||||
msl.reserve(2048);
|
||||
|
||||
msl += R"(
|
||||
#include <metal_stdlib>
|
||||
using namespace metal;
|
||||
|
||||
typedef struct {
|
||||
float4 v_position [[position]];
|
||||
} VaryingsPos;
|
||||
|
||||
typedef struct {
|
||||
float4 colors[9];
|
||||
} ClearColorsIn;
|
||||
|
||||
typedef struct {
|
||||
)";
|
||||
|
||||
char line[128];
|
||||
for (uint32_t caIdx = 0; caIdx < ClearAttKey::COLOR_COUNT; caIdx++) {
|
||||
if (p_key.is_enabled(caIdx)) {
|
||||
const char *typeStr = get_format_type_string((MTL::PixelFormat)p_key.pixel_formats[caIdx]);
|
||||
snprintf(line, sizeof(line), " %s4 color%u [[color(%u)]];\n", typeStr, caIdx, caIdx);
|
||||
msl += line;
|
||||
}
|
||||
}
|
||||
msl += R"(} ClearColorsOut;
|
||||
|
||||
fragment ClearColorsOut fragClear(VaryingsPos varyings [[stage_in]], constant ClearColorsIn& ccIn [[buffer(0)]]) {
|
||||
|
||||
ClearColorsOut ccOut;
|
||||
)";
|
||||
for (uint32_t caIdx = 0; caIdx < ClearAttKey::COLOR_COUNT; caIdx++) {
|
||||
if (p_key.is_enabled(caIdx)) {
|
||||
const char *typeStr = get_format_type_string((MTL::PixelFormat)p_key.pixel_formats[caIdx]);
|
||||
snprintf(line, sizeof(line), " ccOut.color%u = %s4(ccIn.colors[%u]);\n", caIdx, typeStr, caIdx);
|
||||
msl += line;
|
||||
}
|
||||
}
|
||||
msl += R"( return ccOut;
|
||||
})";
|
||||
|
||||
return new_func(NS::String::string(msl.c_str(), NS::UTF8StringEncoding), MTLSTR("fragClear"), nullptr);
|
||||
}
|
||||
|
||||
const char *MDResourceFactory::get_format_type_string(MTL::PixelFormat p_fmt) const {
|
||||
switch (pixel_formats.getFormatType(p_fmt)) {
|
||||
case MTLFormatType::ColorInt8:
|
||||
case MTLFormatType::ColorInt16:
|
||||
return "short";
|
||||
case MTLFormatType::ColorUInt8:
|
||||
case MTLFormatType::ColorUInt16:
|
||||
return "ushort";
|
||||
case MTLFormatType::ColorInt32:
|
||||
return "int";
|
||||
case MTLFormatType::ColorUInt32:
|
||||
return "uint";
|
||||
case MTLFormatType::ColorHalf:
|
||||
return "half";
|
||||
case MTLFormatType::ColorFloat:
|
||||
case MTLFormatType::DepthStencil:
|
||||
case MTLFormatType::Compressed:
|
||||
return "float";
|
||||
case MTLFormatType::None:
|
||||
default:
|
||||
return "unexpected_MTLPixelFormatInvalid";
|
||||
}
|
||||
}
|
||||
|
||||
NS::SharedPtr<MTL::DepthStencilState> MDResourceFactory::new_depth_stencil_state(bool p_use_depth, bool p_use_stencil) {
|
||||
NS::SharedPtr<MTL::DepthStencilDescriptor> dsDesc = NS::TransferPtr(MTL::DepthStencilDescriptor::alloc()->init());
|
||||
dsDesc->setDepthCompareFunction(MTL::CompareFunctionAlways);
|
||||
dsDesc->setDepthWriteEnabled(p_use_depth);
|
||||
|
||||
if (p_use_stencil) {
|
||||
NS::SharedPtr<MTL::StencilDescriptor> sDesc = NS::TransferPtr(MTL::StencilDescriptor::alloc()->init());
|
||||
sDesc->setStencilCompareFunction(MTL::CompareFunctionAlways);
|
||||
sDesc->setStencilFailureOperation(MTL::StencilOperationReplace);
|
||||
sDesc->setDepthFailureOperation(MTL::StencilOperationReplace);
|
||||
sDesc->setDepthStencilPassOperation(MTL::StencilOperationReplace);
|
||||
|
||||
dsDesc->setFrontFaceStencil(sDesc.get());
|
||||
dsDesc->setBackFaceStencil(sDesc.get());
|
||||
} else {
|
||||
dsDesc->setFrontFaceStencil(nullptr);
|
||||
dsDesc->setBackFaceStencil(nullptr);
|
||||
}
|
||||
|
||||
return NS::TransferPtr(device->newDepthStencilState(dsDesc.get()));
|
||||
}
|
||||
|
||||
NS::SharedPtr<MTL::RenderPipelineState> MDResourceFactory::new_clear_pipeline_state(ClearAttKey &p_key, NS::Error **p_error) {
|
||||
NS::SharedPtr<MTL::Function> vtxFunc = new_clear_vert_func(p_key);
|
||||
NS::SharedPtr<MTL::Function> fragFunc = new_clear_frag_func(p_key);
|
||||
NS::SharedPtr<MTL::RenderPipelineDescriptor> plDesc = NS::TransferPtr(MTL::RenderPipelineDescriptor::alloc()->init());
|
||||
plDesc->setLabel(MTLSTR("ClearRenderAttachments"));
|
||||
plDesc->setVertexFunction(vtxFunc.get());
|
||||
plDesc->setFragmentFunction(fragFunc.get());
|
||||
plDesc->setRasterSampleCount(p_key.sample_count);
|
||||
plDesc->setInputPrimitiveTopology(MTL::PrimitiveTopologyClassTriangle);
|
||||
|
||||
for (uint32_t caIdx = 0; caIdx < ClearAttKey::COLOR_COUNT; caIdx++) {
|
||||
MTL::RenderPipelineColorAttachmentDescriptor *colorDesc = plDesc->colorAttachments()->object(caIdx);
|
||||
colorDesc->setPixelFormat((MTL::PixelFormat)p_key.pixel_formats[caIdx]);
|
||||
colorDesc->setWriteMask(p_key.is_enabled(caIdx) ? MTL::ColorWriteMaskAll : MTL::ColorWriteMaskNone);
|
||||
}
|
||||
|
||||
MTL::PixelFormat mtlDepthFormat = (MTL::PixelFormat)p_key.depth_format();
|
||||
if (pixel_formats.isDepthFormat(mtlDepthFormat)) {
|
||||
plDesc->setDepthAttachmentPixelFormat(mtlDepthFormat);
|
||||
}
|
||||
|
||||
MTL::PixelFormat mtlStencilFormat = (MTL::PixelFormat)p_key.stencil_format();
|
||||
if (pixel_formats.isStencilFormat(mtlStencilFormat)) {
|
||||
plDesc->setStencilAttachmentPixelFormat(mtlStencilFormat);
|
||||
}
|
||||
|
||||
MTL::VertexDescriptor *vtxDesc = plDesc->vertexDescriptor();
|
||||
|
||||
// Vertex attribute descriptors.
|
||||
NS::UInteger vtxBuffIdx = get_vertex_buffer_index(VERT_CONTENT_BUFFER_INDEX);
|
||||
NS::UInteger vtxStride = 0;
|
||||
|
||||
// Vertex location.
|
||||
MTL::VertexAttributeDescriptor *vaDesc = vtxDesc->attributes()->object(0);
|
||||
vaDesc->setFormat(MTL::VertexFormatFloat4);
|
||||
vaDesc->setBufferIndex(vtxBuffIdx);
|
||||
vaDesc->setOffset(vtxStride);
|
||||
vtxStride += sizeof(simd::float4);
|
||||
|
||||
// Vertex attribute buffer.
|
||||
MTL::VertexBufferLayoutDescriptor *vbDesc = vtxDesc->layouts()->object(vtxBuffIdx);
|
||||
vbDesc->setStepFunction(MTL::VertexStepFunctionPerVertex);
|
||||
vbDesc->setStepRate(1);
|
||||
vbDesc->setStride(vtxStride);
|
||||
|
||||
NS::Error *err = nullptr;
|
||||
NS::SharedPtr<MTL::RenderPipelineState> state = NS::TransferPtr(device->newRenderPipelineState(plDesc.get(), &err));
|
||||
if (p_error != nullptr) {
|
||||
*p_error = err;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
NS::SharedPtr<MTL::RenderPipelineState> MDResourceFactory::new_empty_draw_pipeline_state(ClearAttKey &p_key, NS::Error **p_error) {
|
||||
DEV_ASSERT(!p_key.is_layered_rendering_enabled());
|
||||
DEV_ASSERT(p_key.is_enabled(0));
|
||||
DEV_ASSERT(!p_key.is_depth_enabled());
|
||||
DEV_ASSERT(!p_key.is_stencil_enabled());
|
||||
|
||||
NS::SharedPtr<NS::AutoreleasePool> pool = NS::TransferPtr(NS::AutoreleasePool::alloc()->init());
|
||||
static const char *msl = R"(#include <metal_stdlib>
|
||||
using namespace metal;
|
||||
|
||||
struct FullscreenNoopOut {
|
||||
float4 position [[position]];
|
||||
};
|
||||
|
||||
vertex FullscreenNoopOut fullscreenNoopVert(uint vid [[vertex_id]]) {
|
||||
float2 positions[3] = { float2(-1.0, -1.0), float2(3.0, -1.0), float2(-1.0, 3.0) };
|
||||
float2 pos = positions[vid];
|
||||
|
||||
FullscreenNoopOut out;
|
||||
out.position = float4(pos, 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
fragment void fullscreenNoopFrag(float4 gl_FragCoord [[position]]) {
|
||||
}
|
||||
)";
|
||||
|
||||
NS::Error *err = nullptr;
|
||||
NS::SharedPtr<MTL::CompileOptions> options = NS::TransferPtr(MTL::CompileOptions::alloc()->init());
|
||||
NS::SharedPtr<MTL::Library> mtlLib = NS::TransferPtr(device->newLibrary(NS::String::string(msl, NS::UTF8StringEncoding), options.get(), &err));
|
||||
if (err && p_error != nullptr) {
|
||||
*p_error = err;
|
||||
}
|
||||
|
||||
if (mtlLib.get() == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
NS::SharedPtr<MTL::Function> vtxFunc = NS::TransferPtr(mtlLib->newFunction(MTLSTR("fullscreenNoopVert")));
|
||||
NS::SharedPtr<MTL::Function> fragFunc = NS::TransferPtr(mtlLib->newFunction(MTLSTR("fullscreenNoopFrag")));
|
||||
|
||||
NS::SharedPtr<MTL::RenderPipelineDescriptor> plDesc = NS::TransferPtr(MTL::RenderPipelineDescriptor::alloc()->init());
|
||||
plDesc->setLabel(MTLSTR("EmptyDrawFullscreenTriangle"));
|
||||
plDesc->setVertexFunction(vtxFunc.get());
|
||||
plDesc->setFragmentFunction(fragFunc.get());
|
||||
plDesc->setRasterSampleCount(p_key.sample_count ? p_key.sample_count : 1);
|
||||
plDesc->setInputPrimitiveTopology(MTL::PrimitiveTopologyClassTriangle);
|
||||
|
||||
MTL::RenderPipelineColorAttachmentDescriptor *colorDesc = plDesc->colorAttachments()->object(0);
|
||||
colorDesc->setPixelFormat((MTL::PixelFormat)p_key.pixel_formats[0]);
|
||||
colorDesc->setWriteMask(MTL::ColorWriteMaskNone);
|
||||
|
||||
err = nullptr;
|
||||
NS::SharedPtr<MTL::RenderPipelineState> state = NS::TransferPtr(device->newRenderPipelineState(plDesc.get(), &err));
|
||||
if (p_error != nullptr && err != nullptr) {
|
||||
*p_error = err;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
#pragma mark - Resource Cache
|
||||
|
||||
MTL::RenderPipelineState *MDResourceCache::get_clear_render_pipeline_state(ClearAttKey &p_key, NS::Error **p_error) {
|
||||
HashMap::ConstIterator it = clear_states.find(p_key);
|
||||
if (it != clear_states.end()) {
|
||||
return it->value.get();
|
||||
}
|
||||
|
||||
NS::SharedPtr<MTL::RenderPipelineState> state = resource_factory->new_clear_pipeline_state(p_key, p_error);
|
||||
MTL::RenderPipelineState *result = state.get();
|
||||
clear_states[p_key] = std::move(state);
|
||||
return result;
|
||||
}
|
||||
|
||||
MTL::RenderPipelineState *MDResourceCache::get_empty_draw_pipeline_state(ClearAttKey &p_key, NS::Error **p_error) {
|
||||
HashMap::ConstIterator it = empty_draw_states.find(p_key);
|
||||
if (it != empty_draw_states.end()) {
|
||||
return it->value.get();
|
||||
}
|
||||
|
||||
NS::SharedPtr<MTL::RenderPipelineState> state = resource_factory->new_empty_draw_pipeline_state(p_key, p_error);
|
||||
MTL::RenderPipelineState *result = state.get();
|
||||
empty_draw_states[p_key] = std::move(state);
|
||||
return result;
|
||||
}
|
||||
|
||||
MTL::DepthStencilState *MDResourceCache::get_depth_stencil_state(bool p_use_depth, bool p_use_stencil) {
|
||||
if (p_use_depth && p_use_stencil) {
|
||||
if (!clear_depth_stencil_state.all) {
|
||||
clear_depth_stencil_state.all = resource_factory->new_depth_stencil_state(true, true);
|
||||
}
|
||||
return clear_depth_stencil_state.all.get();
|
||||
} else if (p_use_depth) {
|
||||
if (!clear_depth_stencil_state.depth_only) {
|
||||
clear_depth_stencil_state.depth_only = resource_factory->new_depth_stencil_state(true, false);
|
||||
}
|
||||
return clear_depth_stencil_state.depth_only.get();
|
||||
} else if (p_use_stencil) {
|
||||
if (!clear_depth_stencil_state.stencil_only) {
|
||||
clear_depth_stencil_state.stencil_only = resource_factory->new_depth_stencil_state(false, true);
|
||||
}
|
||||
return clear_depth_stencil_state.stencil_only.get();
|
||||
} else {
|
||||
if (!clear_depth_stencil_state.none) {
|
||||
clear_depth_stencil_state.none = resource_factory->new_depth_stencil_state(false, false);
|
||||
}
|
||||
return clear_depth_stencil_state.none.get();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Render Pass Types
|
||||
|
||||
MTLFmtCaps MDSubpass::getRequiredFmtCapsForAttachmentAt(uint32_t p_index) const {
|
||||
MTLFmtCaps caps = kMTLFmtCapsNone;
|
||||
|
||||
for (RDD::AttachmentReference const &ar : input_references) {
|
||||
if (ar.attachment == p_index) {
|
||||
flags::set(caps, kMTLFmtCapsRead);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (RDD::AttachmentReference const &ar : color_references) {
|
||||
if (ar.attachment == p_index) {
|
||||
flags::set(caps, kMTLFmtCapsColorAtt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (RDD::AttachmentReference const &ar : resolve_references) {
|
||||
if (ar.attachment == p_index) {
|
||||
flags::set(caps, kMTLFmtCapsResolve);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (depth_stencil_reference.attachment == p_index) {
|
||||
flags::set(caps, kMTLFmtCapsDSAtt);
|
||||
}
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
void MDAttachment::linkToSubpass(const MDRenderPass &p_pass) {
|
||||
firstUseSubpassIndex = UINT32_MAX;
|
||||
lastUseSubpassIndex = 0;
|
||||
|
||||
for (MDSubpass const &subpass : p_pass.subpasses) {
|
||||
MTLFmtCaps reqCaps = subpass.getRequiredFmtCapsForAttachmentAt(index);
|
||||
if (reqCaps) {
|
||||
firstUseSubpassIndex = MIN(subpass.subpass_index, firstUseSubpassIndex);
|
||||
lastUseSubpassIndex = MAX(subpass.subpass_index, lastUseSubpassIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MTL::StoreAction MDAttachment::getMTLStoreAction(MDSubpass const &p_subpass,
|
||||
bool p_is_rendering_entire_area,
|
||||
bool p_has_resolve,
|
||||
bool p_can_resolve,
|
||||
bool p_is_stencil) const {
|
||||
if (!p_is_rendering_entire_area || !isLastUseOf(p_subpass)) {
|
||||
return p_has_resolve && p_can_resolve ? MTL::StoreActionStoreAndMultisampleResolve : MTL::StoreActionStore;
|
||||
}
|
||||
|
||||
switch (p_is_stencil ? stencilStoreAction : storeAction) {
|
||||
case MTL::StoreActionStore:
|
||||
return p_has_resolve && p_can_resolve ? MTL::StoreActionStoreAndMultisampleResolve : MTL::StoreActionStore;
|
||||
case MTL::StoreActionDontCare:
|
||||
return p_has_resolve ? (p_can_resolve ? MTL::StoreActionMultisampleResolve : MTL::StoreActionStore) : MTL::StoreActionDontCare;
|
||||
|
||||
default:
|
||||
return MTL::StoreActionStore;
|
||||
}
|
||||
}
|
||||
|
||||
bool MDAttachment::shouldClear(const MDSubpass &p_subpass, bool p_is_stencil) const {
|
||||
// If the subpass is not the first subpass to use this attachment, don't clear this attachment.
|
||||
if (p_subpass.subpass_index != firstUseSubpassIndex) {
|
||||
return false;
|
||||
}
|
||||
return (p_is_stencil ? stencilLoadAction : loadAction) == MTL::LoadActionClear;
|
||||
}
|
||||
|
||||
MDRenderPass::MDRenderPass(Vector<MDAttachment> &p_attachments, Vector<MDSubpass> &p_subpasses) :
|
||||
attachments(p_attachments), subpasses(p_subpasses) {
|
||||
for (MDAttachment &att : attachments) {
|
||||
att.linkToSubpass(*this);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Command Buffer Base
|
||||
|
||||
void MDCommandBufferBase::retain_resource(CFTypeRef p_resource) {
|
||||
CFRetain(p_resource);
|
||||
_retained_resources.push_back(p_resource);
|
||||
}
|
||||
|
||||
void MDCommandBufferBase::release_resources() {
|
||||
for (CFTypeRef r : _retained_resources) {
|
||||
CFRelease(r);
|
||||
}
|
||||
_retained_resources.clear();
|
||||
}
|
||||
|
||||
void MDCommandBufferBase::render_set_viewport(VectorView<Rect2i> p_viewports) {
|
||||
RenderStateBase &state = get_render_state_base();
|
||||
state.viewports.resize(p_viewports.size());
|
||||
for (uint32_t i = 0; i < p_viewports.size(); i += 1) {
|
||||
Rect2i const &vp = p_viewports[i];
|
||||
state.viewports[i] = {
|
||||
.originX = static_cast<double>(vp.position.x),
|
||||
.originY = static_cast<double>(vp.position.y),
|
||||
.width = static_cast<double>(vp.size.width),
|
||||
.height = static_cast<double>(vp.size.height),
|
||||
.znear = 0.0,
|
||||
.zfar = 1.0,
|
||||
};
|
||||
}
|
||||
state.dirty.set_flag(RenderStateBase::DIRTY_VIEWPORT);
|
||||
}
|
||||
|
||||
void MDCommandBufferBase::render_set_scissor(VectorView<Rect2i> p_scissors) {
|
||||
RenderStateBase &state = get_render_state_base();
|
||||
state.scissors.resize(p_scissors.size());
|
||||
for (uint32_t i = 0; i < p_scissors.size(); i += 1) {
|
||||
Rect2i const &vp = p_scissors[i];
|
||||
state.scissors[i] = {
|
||||
.x = static_cast<NS::UInteger>(vp.position.x),
|
||||
.y = static_cast<NS::UInteger>(vp.position.y),
|
||||
.width = static_cast<NS::UInteger>(vp.size.width),
|
||||
.height = static_cast<NS::UInteger>(vp.size.height),
|
||||
};
|
||||
}
|
||||
state.dirty.set_flag(RenderStateBase::DIRTY_SCISSOR);
|
||||
}
|
||||
|
||||
void MDCommandBufferBase::render_set_blend_constants(const Color &p_constants) {
|
||||
DEV_ASSERT(type == MDCommandBufferStateType::Render);
|
||||
RenderStateBase &state = get_render_state_base();
|
||||
if (state.blend_constants != p_constants) {
|
||||
state.blend_constants = p_constants;
|
||||
state.dirty.set_flag(RenderStateBase::DIRTY_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
void MDCommandBufferBase::_populate_vertices(simd::float4 *p_vertices, Size2i p_fb_size, VectorView<Rect2i> p_rects) {
|
||||
uint32_t idx = 0;
|
||||
for (uint32_t i = 0; i < p_rects.size(); i++) {
|
||||
Rect2i const &rect = p_rects[i];
|
||||
idx = _populate_vertices(p_vertices, idx, rect, p_fb_size);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t MDCommandBufferBase::_populate_vertices(simd::float4 *p_vertices, uint32_t p_index, Rect2i const &p_rect, Size2i p_fb_size) {
|
||||
// Determine the positions of the four edges of the
|
||||
// clear rectangle as a fraction of the attachment size.
|
||||
float leftPos = (float)(p_rect.position.x) / (float)p_fb_size.width;
|
||||
float rightPos = (float)(p_rect.size.width) / (float)p_fb_size.width + leftPos;
|
||||
float bottomPos = (float)(p_rect.position.y) / (float)p_fb_size.height;
|
||||
float topPos = (float)(p_rect.size.height) / (float)p_fb_size.height + bottomPos;
|
||||
|
||||
// Transform to clip-space coordinates, which are bounded by (-1.0 < p < 1.0) in clip-space.
|
||||
leftPos = (leftPos * 2.0f) - 1.0f;
|
||||
rightPos = (rightPos * 2.0f) - 1.0f;
|
||||
bottomPos = (bottomPos * 2.0f) - 1.0f;
|
||||
topPos = (topPos * 2.0f) - 1.0f;
|
||||
|
||||
simd::float4 vtx;
|
||||
|
||||
uint32_t idx = p_index;
|
||||
uint32_t endLayer = get_current_view_count();
|
||||
|
||||
for (uint32_t layer = 0; layer < endLayer; layer++) {
|
||||
vtx.z = 0.0;
|
||||
vtx.w = (float)layer;
|
||||
|
||||
// Top left vertex - First triangle.
|
||||
vtx.y = topPos;
|
||||
vtx.x = leftPos;
|
||||
p_vertices[idx++] = vtx;
|
||||
|
||||
// Bottom left vertex.
|
||||
vtx.y = bottomPos;
|
||||
vtx.x = leftPos;
|
||||
p_vertices[idx++] = vtx;
|
||||
|
||||
// Bottom right vertex.
|
||||
vtx.y = bottomPos;
|
||||
vtx.x = rightPos;
|
||||
p_vertices[idx++] = vtx;
|
||||
|
||||
// Bottom right vertex - Second triangle.
|
||||
p_vertices[idx++] = vtx;
|
||||
|
||||
// Top right vertex.
|
||||
vtx.y = topPos;
|
||||
vtx.x = rightPos;
|
||||
p_vertices[idx++] = vtx;
|
||||
|
||||
// Top left vertex.
|
||||
vtx.y = topPos;
|
||||
vtx.x = leftPos;
|
||||
p_vertices[idx++] = vtx;
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
void MDCommandBufferBase::_end_render_pass() {
|
||||
MDFrameBuffer const &fb_info = *get_frame_buffer();
|
||||
MDSubpass const &subpass = get_current_subpass();
|
||||
|
||||
PixelFormats &pf = device_driver->get_pixel_formats();
|
||||
|
||||
for (uint32_t i = 0; i < subpass.resolve_references.size(); i++) {
|
||||
uint32_t color_index = subpass.color_references[i].attachment;
|
||||
uint32_t resolve_index = subpass.resolve_references[i].attachment;
|
||||
DEV_ASSERT((color_index == RDD::AttachmentReference::UNUSED) == (resolve_index == RDD::AttachmentReference::UNUSED));
|
||||
if (color_index == RDD::AttachmentReference::UNUSED || !fb_info.has_texture(color_index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MTL::Texture *resolve_tex = fb_info.get_texture(resolve_index);
|
||||
|
||||
CRASH_COND_MSG(!flags::all(pf.getCapabilities(resolve_tex->pixelFormat()), kMTLFmtCapsResolve), "not implemented: unresolvable texture types");
|
||||
// see: https://github.com/KhronosGroup/MoltenVK/blob/d20d13fe2735adb845636a81522df1b9d89c0fba/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm#L407
|
||||
}
|
||||
|
||||
end_render_encoding();
|
||||
}
|
||||
|
||||
void MDCommandBufferBase::_render_clear_render_area() {
|
||||
MDRenderPass const &pass = *get_render_pass();
|
||||
MDSubpass const &subpass = get_current_subpass();
|
||||
LocalVector<RDD::RenderPassClearValue> &clear_values = get_clear_values();
|
||||
|
||||
uint32_t ds_index = subpass.depth_stencil_reference.attachment;
|
||||
bool clear_depth = (ds_index != RDD::AttachmentReference::UNUSED && pass.attachments[ds_index].shouldClear(subpass, false));
|
||||
bool clear_stencil = (ds_index != RDD::AttachmentReference::UNUSED && pass.attachments[ds_index].shouldClear(subpass, true));
|
||||
|
||||
uint32_t color_count = subpass.color_references.size();
|
||||
uint32_t clears_size = color_count + (clear_depth || clear_stencil ? 1 : 0);
|
||||
if (clears_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
RDD::AttachmentClear *clears = ALLOCA_ARRAY(RDD::AttachmentClear, clears_size);
|
||||
uint32_t clears_count = 0;
|
||||
|
||||
for (uint32_t i = 0; i < color_count; i++) {
|
||||
uint32_t idx = subpass.color_references[i].attachment;
|
||||
if (idx != RDD::AttachmentReference::UNUSED && pass.attachments[idx].shouldClear(subpass, false)) {
|
||||
clears[clears_count++] = { .aspect = RDD::TEXTURE_ASPECT_COLOR_BIT, .color_attachment = idx, .value = clear_values[idx] };
|
||||
}
|
||||
}
|
||||
|
||||
if (clear_depth || clear_stencil) {
|
||||
MDAttachment const &attachment = pass.attachments[ds_index];
|
||||
BitField<RDD::TextureAspectBits> bits = {};
|
||||
if (clear_depth && attachment.type & MDAttachmentType::Depth) {
|
||||
bits.set_flag(RDD::TEXTURE_ASPECT_DEPTH_BIT);
|
||||
}
|
||||
if (clear_stencil && attachment.type & MDAttachmentType::Stencil) {
|
||||
bits.set_flag(RDD::TEXTURE_ASPECT_STENCIL_BIT);
|
||||
}
|
||||
|
||||
clears[clears_count++] = { .aspect = bits, .color_attachment = ds_index, .value = clear_values[ds_index] };
|
||||
}
|
||||
|
||||
if (clears_count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
render_clear_attachments(VectorView(clears, clears_count), { get_render_area() });
|
||||
}
|
||||
|
||||
void MDCommandBufferBase::encode_push_constant_data(RDD::ShaderID p_shader, VectorView<uint32_t> p_data) {
|
||||
switch (type) {
|
||||
case MDCommandBufferStateType::Render:
|
||||
case MDCommandBufferStateType::Compute: {
|
||||
MDShader *shader = (MDShader *)(p_shader.id);
|
||||
if (shader->push_constants.binding == UINT32_MAX) {
|
||||
return;
|
||||
}
|
||||
push_constant_binding = shader->push_constants.binding;
|
||||
void const *ptr = p_data.ptr();
|
||||
push_constant_data_len = p_data.size() * sizeof(uint32_t);
|
||||
DEV_ASSERT(push_constant_data_len <= sizeof(push_constant_data));
|
||||
memcpy(push_constant_data, ptr, push_constant_data_len);
|
||||
if (push_constant_data_len > 0) {
|
||||
mark_push_constants_dirty();
|
||||
}
|
||||
} break;
|
||||
case MDCommandBufferStateType::Blit:
|
||||
case MDCommandBufferStateType::None:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Metal Library
|
||||
|
||||
static const char *SHADER_STAGE_NAMES[] = {
|
||||
[RD::SHADER_STAGE_VERTEX] = "vert",
|
||||
[RD::SHADER_STAGE_FRAGMENT] = "frag",
|
||||
[RD::SHADER_STAGE_TESSELATION_CONTROL] = "tess_ctrl",
|
||||
[RD::SHADER_STAGE_TESSELATION_EVALUATION] = "tess_eval",
|
||||
[RD::SHADER_STAGE_COMPUTE] = "comp",
|
||||
};
|
||||
|
||||
void ShaderCacheEntry::notify_free() const {
|
||||
owner.shader_cache_free_entry(key);
|
||||
}
|
||||
|
||||
#pragma mark - MDLibrary
|
||||
|
||||
MDLibrary::MDLibrary(ShaderCacheEntry *p_entry
|
||||
#ifdef DEV_ENABLED
|
||||
,
|
||||
NS::String *p_source
|
||||
#endif
|
||||
) :
|
||||
_entry(p_entry) {
|
||||
#ifdef DEV_ENABLED
|
||||
_original_source = NS::RetainPtr(p_source);
|
||||
#endif
|
||||
}
|
||||
|
||||
MDLibrary::~MDLibrary() {
|
||||
_entry->notify_free();
|
||||
}
|
||||
|
||||
void MDLibrary::set_label(NS::String *p_label) {
|
||||
}
|
||||
|
||||
#pragma mark - MDLazyLibrary
|
||||
|
||||
/// Loads the MTLLibrary when the library is first accessed.
|
||||
class MDLazyLibrary final : public MDLibrary {
|
||||
NS::SharedPtr<MTL::Library> _library;
|
||||
NS::Error *_error = nullptr;
|
||||
std::shared_mutex _mu;
|
||||
bool _loaded = false;
|
||||
MTL::Device *_device = nullptr;
|
||||
NS::SharedPtr<NS::String> _source;
|
||||
NS::SharedPtr<MTL::CompileOptions> _options;
|
||||
|
||||
void _load();
|
||||
|
||||
public:
|
||||
MDLazyLibrary(ShaderCacheEntry *p_entry,
|
||||
MTL::Device *p_device,
|
||||
NS::String *p_source,
|
||||
MTL::CompileOptions *p_options);
|
||||
|
||||
MTL::Library *get_library() override;
|
||||
NS::Error *get_error() override;
|
||||
};
|
||||
|
||||
MDLazyLibrary::MDLazyLibrary(ShaderCacheEntry *p_entry,
|
||||
MTL::Device *p_device,
|
||||
NS::String *p_source,
|
||||
MTL::CompileOptions *p_options) :
|
||||
MDLibrary(p_entry
|
||||
#ifdef DEV_ENABLED
|
||||
,
|
||||
p_source
|
||||
#endif
|
||||
),
|
||||
_device(p_device),
|
||||
_source(NS::RetainPtr(p_source)),
|
||||
_options(NS::RetainPtr(p_options)) {
|
||||
}
|
||||
|
||||
void MDLazyLibrary::_load() {
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(_mu);
|
||||
if (_loaded) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock<std::shared_mutex> lock(_mu);
|
||||
if (_loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
os_signpost_id_t compile_id = (os_signpost_id_t)(uintptr_t)this;
|
||||
os_signpost_interval_begin(LOG_INTERVALS, compile_id, "shader_compile",
|
||||
"shader_name=%{public}s stage=%{public}s hash=%X",
|
||||
_entry->name.get_data(), SHADER_STAGE_NAMES[_entry->stage], _entry->key.short_sha());
|
||||
NS::Error *error = nullptr;
|
||||
_library = NS::TransferPtr(_device->newLibrary(_source.get(), _options.get(), &error));
|
||||
os_signpost_interval_end(LOG_INTERVALS, compile_id, "shader_compile");
|
||||
_error = error;
|
||||
_device = nullptr;
|
||||
_source.reset();
|
||||
_options.reset();
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
MTL::Library *MDLazyLibrary::get_library() {
|
||||
_load();
|
||||
return _library.get();
|
||||
}
|
||||
|
||||
NS::Error *MDLazyLibrary::get_error() {
|
||||
_load();
|
||||
return _error;
|
||||
}
|
||||
|
||||
#pragma mark - MDImmediateLibrary
|
||||
|
||||
/// Loads the MTLLibrary immediately on initialization, using Metal's async compilation API.
|
||||
class MDImmediateLibrary final : public MDLibrary {
|
||||
NS::SharedPtr<MTL::Library> _library;
|
||||
NS::Error *_error = nullptr;
|
||||
std::mutex _cv_mutex;
|
||||
std::condition_variable _cv;
|
||||
std::atomic<bool> _complete{ false };
|
||||
bool _ready = false;
|
||||
|
||||
public:
|
||||
MDImmediateLibrary(ShaderCacheEntry *p_entry,
|
||||
MTL::Device *p_device,
|
||||
NS::String *p_source,
|
||||
MTL::CompileOptions *p_options);
|
||||
|
||||
MTL::Library *get_library() override;
|
||||
NS::Error *get_error() override;
|
||||
};
|
||||
|
||||
MDImmediateLibrary::MDImmediateLibrary(ShaderCacheEntry *p_entry,
|
||||
MTL::Device *p_device,
|
||||
NS::String *p_source,
|
||||
MTL::CompileOptions *p_options) :
|
||||
MDLibrary(p_entry
|
||||
#ifdef DEV_ENABLED
|
||||
,
|
||||
p_source
|
||||
#endif
|
||||
) {
|
||||
os_signpost_id_t compile_id = (os_signpost_id_t)(uintptr_t)this;
|
||||
os_signpost_interval_begin(LOG_INTERVALS, compile_id, "shader_compile",
|
||||
"shader_name=%{public}s stage=%{public}s hash=%X",
|
||||
p_entry->name.get_data(), SHADER_STAGE_NAMES[p_entry->stage], p_entry->key.short_sha());
|
||||
|
||||
// Use Metal's async compilation API with std::function callback.
|
||||
p_device->newLibrary(p_source, p_options, [this, compile_id, p_entry](MTL::Library *library, NS::Error *error) {
|
||||
os_signpost_interval_end(LOG_INTERVALS, compile_id, "shader_compile");
|
||||
if (library) {
|
||||
_library = NS::RetainPtr(library);
|
||||
}
|
||||
_error = error;
|
||||
if (error) {
|
||||
ERR_PRINT(vformat(U"Error compiling shader %s: %s", p_entry->name.get_data(), error->localizedDescription()->utf8String()));
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_cv_mutex);
|
||||
_ready = true;
|
||||
}
|
||||
_cv.notify_all();
|
||||
_complete = true;
|
||||
});
|
||||
}
|
||||
|
||||
MTL::Library *MDImmediateLibrary::get_library() {
|
||||
if (!_complete) {
|
||||
std::unique_lock<std::mutex> lock(_cv_mutex);
|
||||
_cv.wait(lock, [this] { return _ready; });
|
||||
}
|
||||
return _library.get();
|
||||
}
|
||||
|
||||
NS::Error *MDImmediateLibrary::get_error() {
|
||||
if (!_complete) {
|
||||
std::unique_lock<std::mutex> lock(_cv_mutex);
|
||||
_cv.wait(lock, [this] { return _ready; });
|
||||
}
|
||||
return _error;
|
||||
}
|
||||
|
||||
#pragma mark - MDBinaryLibrary
|
||||
|
||||
/// Loads the MTLLibrary from pre-compiled binary data.
|
||||
class MDBinaryLibrary final : public MDLibrary {
|
||||
NS::SharedPtr<MTL::Library> _library;
|
||||
NS::Error *_error = nullptr;
|
||||
|
||||
public:
|
||||
MDBinaryLibrary(ShaderCacheEntry *p_entry,
|
||||
MTL::Device *p_device,
|
||||
#ifdef DEV_ENABLED
|
||||
NS::String *p_source,
|
||||
#endif
|
||||
dispatch_data_t p_data);
|
||||
|
||||
MTL::Library *get_library() override;
|
||||
NS::Error *get_error() override;
|
||||
};
|
||||
|
||||
MDBinaryLibrary::MDBinaryLibrary(ShaderCacheEntry *p_entry,
|
||||
MTL::Device *p_device,
|
||||
#ifdef DEV_ENABLED
|
||||
NS::String *p_source,
|
||||
#endif
|
||||
dispatch_data_t p_data) :
|
||||
MDLibrary(p_entry
|
||||
#ifdef DEV_ENABLED
|
||||
,
|
||||
p_source
|
||||
#endif
|
||||
) {
|
||||
NS::Error *error = nullptr;
|
||||
_library = NS::TransferPtr(p_device->newLibrary(p_data, &error));
|
||||
if (error != nullptr) {
|
||||
_error = error;
|
||||
ERR_PRINT(vformat("Unable to load shader library: %s", error->localizedDescription()->utf8String()));
|
||||
}
|
||||
}
|
||||
|
||||
MTL::Library *MDBinaryLibrary::get_library() {
|
||||
return _library.get();
|
||||
}
|
||||
|
||||
NS::Error *MDBinaryLibrary::get_error() {
|
||||
return _error;
|
||||
}
|
||||
|
||||
#pragma mark - MDLibrary Factory Methods
|
||||
|
||||
std::shared_ptr<MDLibrary> MDLibrary::create(ShaderCacheEntry *p_entry,
|
||||
MTL::Device *p_device,
|
||||
NS::String *p_source,
|
||||
MTL::CompileOptions *p_options,
|
||||
ShaderLoadStrategy p_strategy) {
|
||||
std::shared_ptr<MDLibrary> lib;
|
||||
switch (p_strategy) {
|
||||
case ShaderLoadStrategy::IMMEDIATE:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
lib = std::make_shared<MDImmediateLibrary>(p_entry, p_device, p_source, p_options);
|
||||
break;
|
||||
case ShaderLoadStrategy::LAZY:
|
||||
lib = std::make_shared<MDLazyLibrary>(p_entry, p_device, p_source, p_options);
|
||||
break;
|
||||
}
|
||||
p_entry->library = lib;
|
||||
return lib;
|
||||
}
|
||||
|
||||
std::shared_ptr<MDLibrary> MDLibrary::create(ShaderCacheEntry *p_entry,
|
||||
MTL::Device *p_device,
|
||||
#ifdef DEV_ENABLED
|
||||
NS::String *p_source,
|
||||
#endif
|
||||
dispatch_data_t p_data) {
|
||||
std::shared_ptr<MDLibrary> lib = std::make_shared<MDBinaryLibrary>(p_entry, p_device,
|
||||
#ifdef DEV_ENABLED
|
||||
p_source,
|
||||
#endif
|
||||
p_data);
|
||||
p_entry->library = lib;
|
||||
return lib;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,9 +30,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <os/log.h>
|
||||
#include <os/log.h>
|
||||
|
||||
#import <functional>
|
||||
#include <functional>
|
||||
|
||||
/// Godot limits the number of dynamic buffers to 8.
|
||||
///
|
||||
@@ -93,19 +93,32 @@ static constexpr uint64_t round_up_to_alignment(uint64_t p_value, uint64_t p_ali
|
||||
return aligned_value;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
class Defer {
|
||||
public:
|
||||
Defer(std::function<void()> func) :
|
||||
func_(func) {}
|
||||
explicit Defer(F &&f) :
|
||||
func_(std::forward<F>(f)) {}
|
||||
~Defer() { func_(); }
|
||||
|
||||
// Non-copyable (correct RAII semantics)
|
||||
Defer(const Defer &) = delete;
|
||||
Defer &operator=(const Defer &) = delete;
|
||||
|
||||
// Movable
|
||||
Defer(Defer &&) = default;
|
||||
Defer &operator=(Defer &&) = default;
|
||||
|
||||
private:
|
||||
std::function<void()> func_;
|
||||
F func_;
|
||||
};
|
||||
|
||||
// C++17 class template argument deduction.
|
||||
template <typename F>
|
||||
Defer(F &&) -> Defer<std::decay_t<F>>;
|
||||
|
||||
#define CONCAT_INTERNAL(x, y) x##y
|
||||
#define CONCAT(x, y) CONCAT_INTERNAL(x, y)
|
||||
#define DEFER const Defer &CONCAT(defer__, __LINE__) = Defer
|
||||
#define DEFER const auto &CONCAT(defer__, __LINE__) = Defer
|
||||
|
||||
extern os_log_t LOG_DRIVER;
|
||||
// Used for dynamic tracing.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**************************************************************************/
|
||||
/* pixel_formats.mm */
|
||||
/* pixel_formats.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
@@ -48,53 +48,53 @@
|
||||
/* permissions and limitations under the License. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "pixel_formats.h"
|
||||
#include "pixel_formats.h"
|
||||
|
||||
#import "metal_utils.h"
|
||||
#include "metal_utils.h"
|
||||
|
||||
#if TARGET_OS_IPHONE || TARGET_OS_TV
|
||||
#if !(__IPHONE_OS_VERSION_MAX_ALLOWED >= 160400) // iOS/tvOS 16.4
|
||||
#define MTLPixelFormatBC1_RGBA MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatBC1_RGBA_sRGB MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatBC2_RGBA MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatBC2_RGBA_sRGB MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatBC3_RGBA MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatBC3_RGBA_sRGB MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatBC4_RUnorm MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatBC4_RSnorm MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatBC5_RGUnorm MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatBC5_RGSnorm MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatBC6H_RGBUfloat MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatBC6H_RGBFloat MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatBC7_RGBAUnorm MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatBC7_RGBAUnorm_sRGB MTLPixelFormatInvalid
|
||||
#define PixelFormatBC1_RGBA PixelFormatInvalid
|
||||
#define PixelFormatBC1_RGBA_sRGB PixelFormatInvalid
|
||||
#define PixelFormatBC2_RGBA PixelFormatInvalid
|
||||
#define PixelFormatBC2_RGBA_sRGB PixelFormatInvalid
|
||||
#define PixelFormatBC3_RGBA PixelFormatInvalid
|
||||
#define PixelFormatBC3_RGBA_sRGB PixelFormatInvalid
|
||||
#define PixelFormatBC4_RUnorm PixelFormatInvalid
|
||||
#define PixelFormatBC4_RSnorm PixelFormatInvalid
|
||||
#define PixelFormatBC5_RGUnorm PixelFormatInvalid
|
||||
#define PixelFormatBC5_RGSnorm PixelFormatInvalid
|
||||
#define PixelFormatBC6H_RGBUfloat PixelFormatInvalid
|
||||
#define PixelFormatBC6H_RGBFloat PixelFormatInvalid
|
||||
#define PixelFormatBC7_RGBAUnorm PixelFormatInvalid
|
||||
#define PixelFormatBC7_RGBAUnorm_sRGB PixelFormatInvalid
|
||||
#endif
|
||||
|
||||
#define MTLPixelFormatDepth16Unorm_Stencil8 MTLPixelFormatDepth32Float_Stencil8
|
||||
#define MTLPixelFormatDepth24Unorm_Stencil8 MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatX24_Stencil8 MTLPixelFormatInvalid
|
||||
#define PixelFormatDepth16Unorm_Stencil8 PixelFormatDepth32Float_Stencil8
|
||||
#define PixelFormatDepth24Unorm_Stencil8 PixelFormatInvalid
|
||||
#define PixelFormatX24_Stencil8 PixelFormatInvalid
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_TV
|
||||
#define MTLPixelFormatASTC_4x4_HDR MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatASTC_5x4_HDR MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatASTC_5x5_HDR MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatASTC_6x5_HDR MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatASTC_6x6_HDR MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatASTC_8x5_HDR MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatASTC_8x6_HDR MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatASTC_8x8_HDR MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatASTC_10x5_HDR MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatASTC_10x6_HDR MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatASTC_10x8_HDR MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatASTC_10x10_HDR MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatASTC_12x10_HDR MTLPixelFormatInvalid
|
||||
#define MTLPixelFormatASTC_12x12_HDR MTLPixelFormatInvalid
|
||||
#define PixelFormatASTC_4x4_HDR PixelFormatInvalid
|
||||
#define PixelFormatASTC_5x4_HDR PixelFormatInvalid
|
||||
#define PixelFormatASTC_5x5_HDR PixelFormatInvalid
|
||||
#define PixelFormatASTC_6x5_HDR PixelFormatInvalid
|
||||
#define PixelFormatASTC_6x6_HDR PixelFormatInvalid
|
||||
#define PixelFormatASTC_8x5_HDR PixelFormatInvalid
|
||||
#define PixelFormatASTC_8x6_HDR PixelFormatInvalid
|
||||
#define PixelFormatASTC_8x8_HDR PixelFormatInvalid
|
||||
#define PixelFormatASTC_10x5_HDR PixelFormatInvalid
|
||||
#define PixelFormatASTC_10x6_HDR PixelFormatInvalid
|
||||
#define PixelFormatASTC_10x8_HDR PixelFormatInvalid
|
||||
#define PixelFormatASTC_10x10_HDR PixelFormatInvalid
|
||||
#define PixelFormatASTC_12x10_HDR PixelFormatInvalid
|
||||
#define PixelFormatASTC_12x12_HDR PixelFormatInvalid
|
||||
#endif
|
||||
|
||||
#if !((__MAC_OS_X_VERSION_MAX_ALLOWED >= 140000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 170000)) // Xcode 15
|
||||
#define MTLVertexFormatFloatRG11B10 MTLVertexFormatInvalid
|
||||
#define MTLVertexFormatFloatRGB9E5 MTLVertexFormatInvalid
|
||||
#define VertexFormatFloatRG11B10 VertexFormatInvalid
|
||||
#define VertexFormatFloatRGB9E5 VertexFormatInvalid
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
@@ -113,21 +113,21 @@ bool PixelFormats::isSupportedOrSubstitutable(DataFormat p_format) {
|
||||
return getDataFormatDesc(p_format).isSupportedOrSubstitutable();
|
||||
}
|
||||
|
||||
bool PixelFormats::isPVRTCFormat(MTLPixelFormat p_format) {
|
||||
bool PixelFormats::isPVRTCFormat(MTL::PixelFormat p_format) {
|
||||
#if defined(VISIONOS_ENABLED)
|
||||
return false;
|
||||
#else
|
||||
// Deprecated in SDK 26.0
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations")
|
||||
switch (p_format) {
|
||||
case MTLPixelFormatPVRTC_RGBA_2BPP:
|
||||
case MTLPixelFormatPVRTC_RGBA_2BPP_sRGB:
|
||||
case MTLPixelFormatPVRTC_RGBA_4BPP:
|
||||
case MTLPixelFormatPVRTC_RGBA_4BPP_sRGB:
|
||||
case MTLPixelFormatPVRTC_RGB_2BPP:
|
||||
case MTLPixelFormatPVRTC_RGB_2BPP_sRGB:
|
||||
case MTLPixelFormatPVRTC_RGB_4BPP:
|
||||
case MTLPixelFormatPVRTC_RGB_4BPP_sRGB:
|
||||
case MTL::PixelFormatPVRTC_RGBA_2BPP:
|
||||
case MTL::PixelFormatPVRTC_RGBA_2BPP_sRGB:
|
||||
case MTL::PixelFormatPVRTC_RGBA_4BPP:
|
||||
case MTL::PixelFormatPVRTC_RGBA_4BPP_sRGB:
|
||||
case MTL::PixelFormatPVRTC_RGB_2BPP:
|
||||
case MTL::PixelFormatPVRTC_RGB_2BPP_sRGB:
|
||||
case MTL::PixelFormatPVRTC_RGB_4BPP:
|
||||
case MTL::PixelFormatPVRTC_RGB_4BPP_sRGB:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -140,24 +140,24 @@ MTLFormatType PixelFormats::getFormatType(DataFormat p_format) {
|
||||
return getDataFormatDesc(p_format).formatType;
|
||||
}
|
||||
|
||||
MTLFormatType PixelFormats::getFormatType(MTLPixelFormat p_format) {
|
||||
MTLFormatType PixelFormats::getFormatType(MTL::PixelFormat p_format) {
|
||||
return getDataFormatDesc(p_format).formatType;
|
||||
}
|
||||
|
||||
MTLPixelFormat PixelFormats::getMTLPixelFormat(DataFormat p_format) {
|
||||
MTL::PixelFormat PixelFormats::getMTLPixelFormat(DataFormat p_format) {
|
||||
DataFormatDesc &dfDesc = getDataFormatDesc(p_format);
|
||||
MTLPixelFormat mtlPixFmt = dfDesc.mtlPixelFormat;
|
||||
MTL::PixelFormat mtlPixFmt = dfDesc.mtlPixelFormat;
|
||||
|
||||
// If the MTLPixelFormat is not supported but DataFormat is valid,
|
||||
// If the MTL::PixelFormat is not supported but DataFormat is valid,
|
||||
// attempt to substitute a different format.
|
||||
if (mtlPixFmt == MTLPixelFormatInvalid && p_format != RD::DATA_FORMAT_MAX && dfDesc.chromaSubsamplingPlaneCount <= 1) {
|
||||
if (mtlPixFmt == MTL::PixelFormatInvalid && p_format != RD::DATA_FORMAT_MAX && dfDesc.chromaSubsamplingPlaneCount <= 1) {
|
||||
mtlPixFmt = dfDesc.mtlPixelFormatSubstitute;
|
||||
}
|
||||
|
||||
return mtlPixFmt;
|
||||
}
|
||||
|
||||
RD::DataFormat PixelFormats::getDataFormat(MTLPixelFormat p_format) {
|
||||
RD::DataFormat PixelFormats::getDataFormat(MTL::PixelFormat p_format) {
|
||||
return getMTLPixelFormatDesc(p_format).dataFormat;
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ uint32_t PixelFormats::getBytesPerBlock(DataFormat p_format) {
|
||||
return getDataFormatDesc(p_format).bytesPerBlock;
|
||||
}
|
||||
|
||||
uint32_t PixelFormats::getBytesPerBlock(MTLPixelFormat p_format) {
|
||||
uint32_t PixelFormats::getBytesPerBlock(MTL::PixelFormat p_format) {
|
||||
return getDataFormatDesc(p_format).bytesPerBlock;
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ float PixelFormats::getBytesPerTexel(DataFormat p_format) {
|
||||
return getDataFormatDesc(p_format).bytesPerTexel();
|
||||
}
|
||||
|
||||
float PixelFormats::getBytesPerTexel(MTLPixelFormat p_format) {
|
||||
float PixelFormats::getBytesPerTexel(MTL::PixelFormat p_format) {
|
||||
return getDataFormatDesc(p_format).bytesPerTexel();
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ size_t PixelFormats::getBytesPerRow(DataFormat p_format, uint32_t p_texels_per_r
|
||||
return Math::division_round_up(p_texels_per_row, dfDesc.blockTexelSize.width) * dfDesc.bytesPerBlock;
|
||||
}
|
||||
|
||||
size_t PixelFormats::getBytesPerRow(MTLPixelFormat p_format, uint32_t p_texels_per_row) {
|
||||
size_t PixelFormats::getBytesPerRow(MTL::PixelFormat p_format, uint32_t p_texels_per_row) {
|
||||
DataFormatDesc &dfDesc = getDataFormatDesc(p_format);
|
||||
return Math::division_round_up(p_texels_per_row, dfDesc.blockTexelSize.width) * dfDesc.bytesPerBlock;
|
||||
}
|
||||
@@ -199,7 +199,7 @@ size_t PixelFormats::getBytesPerLayer(DataFormat p_format, size_t p_bytes_per_ro
|
||||
return Math::division_round_up(p_texel_rows_per_layer, getDataFormatDesc(p_format).blockTexelSize.height) * p_bytes_per_row;
|
||||
}
|
||||
|
||||
size_t PixelFormats::getBytesPerLayer(MTLPixelFormat p_format, size_t p_bytes_per_row, uint32_t p_texel_rows_per_layer) {
|
||||
size_t PixelFormats::getBytesPerLayer(MTL::PixelFormat p_format, size_t p_bytes_per_row, uint32_t p_texel_rows_per_layer) {
|
||||
return Math::division_round_up(p_texel_rows_per_layer, getDataFormatDesc(p_format).blockTexelSize.height) * p_bytes_per_row;
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ MTLFmtCaps PixelFormats::getCapabilities(DataFormat p_format, bool p_extended) {
|
||||
return getCapabilities(getDataFormatDesc(p_format).mtlPixelFormat, p_extended);
|
||||
}
|
||||
|
||||
MTLFmtCaps PixelFormats::getCapabilities(MTLPixelFormat p_format, bool p_extended) {
|
||||
MTLFmtCaps PixelFormats::getCapabilities(MTL::PixelFormat p_format, bool p_extended) {
|
||||
MTLFormatDesc &mtlDesc = getMTLPixelFormatDesc(p_format);
|
||||
MTLFmtCaps caps = mtlDesc.mtlFmtCaps;
|
||||
if (!p_extended || mtlDesc.mtlViewClass == MTLViewClass::None) {
|
||||
@@ -226,11 +226,11 @@ MTLFmtCaps PixelFormats::getCapabilities(MTLPixelFormat p_format, bool p_extende
|
||||
return caps;
|
||||
}
|
||||
|
||||
MTLVertexFormat PixelFormats::getMTLVertexFormat(DataFormat p_format) {
|
||||
MTL::VertexFormat PixelFormats::getMTLVertexFormat(DataFormat p_format) {
|
||||
DataFormatDesc &dfDesc = getDataFormatDesc(p_format);
|
||||
MTLVertexFormat format = dfDesc.mtlVertexFormat;
|
||||
MTL::VertexFormat format = dfDesc.mtlVertexFormat;
|
||||
|
||||
if (format == MTLVertexFormatInvalid) {
|
||||
if (format == MTL::VertexFormatInvalid) {
|
||||
String errMsg;
|
||||
errMsg += "DataFormat ";
|
||||
errMsg += dfDesc.name;
|
||||
@@ -254,22 +254,22 @@ DataFormatDesc &PixelFormats::getDataFormatDesc(DataFormat p_format) {
|
||||
return _data_format_descs[p_format];
|
||||
}
|
||||
|
||||
DataFormatDesc &PixelFormats::getDataFormatDesc(MTLPixelFormat p_format) {
|
||||
DataFormatDesc &PixelFormats::getDataFormatDesc(MTL::PixelFormat p_format) {
|
||||
return getDataFormatDesc(getMTLPixelFormatDesc(p_format).dataFormat);
|
||||
}
|
||||
|
||||
// Return a reference to the Metal format descriptor corresponding to the MTLPixelFormat.
|
||||
MTLFormatDesc &PixelFormats::getMTLPixelFormatDesc(MTLPixelFormat p_format) {
|
||||
// Return a reference to the Metal format descriptor corresponding to the MTL::PixelFormat.
|
||||
MTLFormatDesc &PixelFormats::getMTLPixelFormatDesc(MTL::PixelFormat p_format) {
|
||||
return _mtl_pixel_format_descs[p_format];
|
||||
}
|
||||
|
||||
// Return a reference to the Metal format descriptor corresponding to the MTLVertexFormat.
|
||||
MTLFormatDesc &PixelFormats::getMTLVertexFormatDesc(MTLVertexFormat p_format) {
|
||||
// Return a reference to the Metal format descriptor corresponding to the MTL::VertexFormat.
|
||||
MTLFormatDesc &PixelFormats::getMTLVertexFormatDesc(MTL::VertexFormat p_format) {
|
||||
return _mtl_vertex_format_descs[p_format];
|
||||
}
|
||||
|
||||
PixelFormats::PixelFormats(id<MTLDevice> p_device, const MetalFeatures &p_feat) :
|
||||
device(p_device) {
|
||||
PixelFormats::PixelFormats(MTL::Device *p_device, const MetalFeatures &p_feat) :
|
||||
device(p_device->retain()) {
|
||||
initMTLPixelFormatCapabilities();
|
||||
initMTLVertexFormatCapabilities(p_feat);
|
||||
modifyMTLFormatCapabilities(p_feat);
|
||||
@@ -278,9 +278,13 @@ PixelFormats::PixelFormats(id<MTLDevice> p_device, const MetalFeatures &p_feat)
|
||||
buildDFFormatMaps();
|
||||
}
|
||||
|
||||
PixelFormats::~PixelFormats() {
|
||||
device->release();
|
||||
}
|
||||
|
||||
#define addDataFormatDescFull(DATA_FMT, MTL_FMT, MTL_FMT_ALT, MTL_VTX_FMT, MTL_VTX_FMT_ALT, CSPC, CSCB, BLK_W, BLK_H, BLK_BYTE_CNT, MVK_FMT_TYPE, SWIZ_R, SWIZ_G, SWIZ_B, SWIZ_A) \
|
||||
dfFmt = RD::DATA_FORMAT_##DATA_FMT; \
|
||||
_data_format_descs[dfFmt] = { dfFmt, MTLPixelFormat##MTL_FMT, MTLPixelFormat##MTL_FMT_ALT, MTLVertexFormat##MTL_VTX_FMT, MTLVertexFormat##MTL_VTX_FMT_ALT, \
|
||||
_data_format_descs[dfFmt] = { dfFmt, MTL::PixelFormat##MTL_FMT, MTL::PixelFormat##MTL_FMT_ALT, MTL::VertexFormat##MTL_VTX_FMT, MTL::VertexFormat##MTL_VTX_FMT_ALT, \
|
||||
CSPC, CSCB, { BLK_W, BLK_H }, BLK_BYTE_CNT, MTLFormatType::MVK_FMT_TYPE, \
|
||||
{ RD::TEXTURE_SWIZZLE_##SWIZ_R, RD::TEXTURE_SWIZZLE_##SWIZ_G, RD::TEXTURE_SWIZZLE_##SWIZ_B, RD::TEXTURE_SWIZZLE_##SWIZ_A }, \
|
||||
"DATA_FORMAT_" #DATA_FMT, false }
|
||||
@@ -577,14 +581,14 @@ void PixelFormats::initDataFormatCapabilities() {
|
||||
addDfFormatDescChromaSubsampling(G16_B16_R16_3PLANE_444_UNORM, Invalid, 3, 16, 1, 1, 6);
|
||||
}
|
||||
|
||||
void PixelFormats::addMTLPixelFormatDescImpl(MTLPixelFormat p_pix_fmt, MTLPixelFormat p_pix_fmt_linear,
|
||||
void PixelFormats::addMTLPixelFormatDescImpl(MTL::PixelFormat p_pix_fmt, MTL::PixelFormat p_pix_fmt_linear,
|
||||
MTLViewClass p_view_class, MTLFmtCaps p_fmt_caps, const char *p_name) {
|
||||
_mtl_pixel_format_descs[p_pix_fmt] = { .mtlPixelFormat = p_pix_fmt, DataFormat::DATA_FORMAT_MAX, p_fmt_caps, p_view_class, p_pix_fmt_linear, p_name };
|
||||
}
|
||||
|
||||
#define addMTLPixelFormatDescFull(mtlFmt, mtlFmtLinear, viewClass, appleGPUCaps) \
|
||||
addMTLPixelFormatDescImpl(MTLPixelFormat##mtlFmt, MTLPixelFormat##mtlFmtLinear, MTLViewClass::viewClass, \
|
||||
appleGPUCaps, "MTLPixelFormat" #mtlFmt)
|
||||
addMTLPixelFormatDescImpl(MTL::PixelFormat##mtlFmt, MTL::PixelFormat##mtlFmtLinear, MTLViewClass::viewClass, \
|
||||
appleGPUCaps, "MTL::PixelFormat" #mtlFmt)
|
||||
|
||||
#define addMTLPixelFormatDesc(mtlFmt, viewClass, appleGPUCaps) \
|
||||
addMTLPixelFormatDescFull(mtlFmt, mtlFmt, viewClass, kMTLFmtCaps##appleGPUCaps)
|
||||
@@ -602,8 +606,8 @@ void PixelFormats::addMTLPixelFormatDescImpl(MTLPixelFormat p_pix_fmt, MTLPixelF
|
||||
void PixelFormats::initMTLPixelFormatCapabilities() {
|
||||
_mtl_pixel_format_descs.reserve(1024);
|
||||
|
||||
// MTLPixelFormatInvalid must come first. Use addMTLPixelFormatDescImpl to avoid guard code.
|
||||
addMTLPixelFormatDescImpl(MTLPixelFormatInvalid, MTLPixelFormatInvalid, MTLViewClass::None, kMTLFmtCapsNone, "MTLPixelFormatInvalid");
|
||||
// MTL::PixelFormatInvalid must come first. Use addMTLPixelFormatDescImpl to avoid guard code.
|
||||
addMTLPixelFormatDescImpl(MTL::PixelFormatInvalid, MTL::PixelFormatInvalid, MTLViewClass::None, kMTLFmtCapsNone, "MTL::PixelFormatInvalid");
|
||||
|
||||
// Ordinary 8-bit pixel formats.
|
||||
addMTLPixelFormatDesc(A8Unorm, Color8, All);
|
||||
@@ -779,23 +783,23 @@ void PixelFormats::initMTLPixelFormatCapabilities() {
|
||||
}
|
||||
|
||||
// If necessary, resize vector with empty elements.
|
||||
void PixelFormats::addMTLVertexFormatDescImpl(MTLVertexFormat mtlVtxFmt, MTLFmtCaps vtxCap, const char *name) {
|
||||
void PixelFormats::addMTLVertexFormatDescImpl(MTL::VertexFormat mtlVtxFmt, MTLFmtCaps vtxCap, const char *name) {
|
||||
if (mtlVtxFmt >= _mtl_vertex_format_descs.size()) {
|
||||
_mtl_vertex_format_descs.resize(mtlVtxFmt + 1);
|
||||
}
|
||||
_mtl_vertex_format_descs[mtlVtxFmt] = { .mtlVertexFormat = mtlVtxFmt, RD::DATA_FORMAT_MAX, vtxCap, MTLViewClass::None, MTLPixelFormatInvalid, name };
|
||||
_mtl_vertex_format_descs[mtlVtxFmt] = { .mtlVertexFormat = mtlVtxFmt, RD::DATA_FORMAT_MAX, vtxCap, MTLViewClass::None, MTL::PixelFormatInvalid, name };
|
||||
}
|
||||
|
||||
// Check mtlVtx exists on platform, to avoid overwriting the MTLVertexFormatInvalid entry.
|
||||
// Check mtlVtx exists on platform, to avoid overwriting the MTL::VertexFormatInvalid entry.
|
||||
#define addMTLVertexFormatDesc(mtlVtx) \
|
||||
if (MTLVertexFormat##mtlVtx) { \
|
||||
addMTLVertexFormatDescImpl(MTLVertexFormat##mtlVtx, kMTLFmtCapsVertex, "MTLVertexFormat" #mtlVtx); \
|
||||
if (MTL::VertexFormat##mtlVtx) { \
|
||||
addMTLVertexFormatDescImpl(MTL::VertexFormat##mtlVtx, kMTLFmtCapsVertex, "MTL::VertexFormat" #mtlVtx); \
|
||||
}
|
||||
|
||||
void PixelFormats::initMTLVertexFormatCapabilities(const MetalFeatures &p_feat) {
|
||||
_mtl_vertex_format_descs.resize(MTLVertexFormatHalf + 3);
|
||||
// MTLVertexFormatInvalid must come first. Use addMTLVertexFormatDescImpl to avoid guard code.
|
||||
addMTLVertexFormatDescImpl(MTLVertexFormatInvalid, kMTLFmtCapsNone, "MTLVertexFormatInvalid");
|
||||
_mtl_vertex_format_descs.resize(MTL::VertexFormatHalf + 3);
|
||||
// MTL::VertexFormatInvalid must come first. Use addMTLVertexFormatDescImpl to avoid guard code.
|
||||
addMTLVertexFormatDescImpl(MTL::VertexFormatInvalid, kMTLFmtCapsNone, "MTL::VertexFormatInvalid");
|
||||
|
||||
addMTLVertexFormatDesc(UChar2Normalized);
|
||||
addMTLVertexFormatDesc(Char2Normalized);
|
||||
@@ -862,8 +866,8 @@ void PixelFormats::initMTLVertexFormatCapabilities(const MetalFeatures &p_feat)
|
||||
|
||||
addMTLVertexFormatDesc(UChar4Normalized_BGRA);
|
||||
|
||||
if (@available(macos 14.0, ios 17.0, tvos 17.0, *)) {
|
||||
if (p_feat.highestFamily >= MTLGPUFamilyApple5) {
|
||||
if (__builtin_available(macos 14.0, ios 17.0, tvos 17.0, *)) {
|
||||
if (p_feat.highestFamily >= MTL::GPUFamilyApple5) {
|
||||
addMTLVertexFormatDesc(FloatRG11B10);
|
||||
addMTLVertexFormatDesc(FloatRGB9E5);
|
||||
}
|
||||
@@ -871,9 +875,9 @@ void PixelFormats::initMTLVertexFormatCapabilities(const MetalFeatures &p_feat)
|
||||
}
|
||||
|
||||
// Return a reference to the format capabilities, so the caller can manipulate them.
|
||||
// Check mtlPixFmt exists on platform, to avoid overwriting the MTLPixelFormatInvalid entry.
|
||||
// Check mtlPixFmt exists on platform, to avoid overwriting the MTL::PixelFormatInvalid entry.
|
||||
// When returning the dummy, reset it on each access because it can be written to by caller.
|
||||
MTLFmtCaps &PixelFormats::getMTLPixelFormatCapsIf(MTLPixelFormat mtlPixFmt, bool cond) {
|
||||
MTLFmtCaps &PixelFormats::getMTLPixelFormatCapsIf(MTL::PixelFormat mtlPixFmt, bool cond) {
|
||||
static MTLFmtCaps dummyFmtCaps;
|
||||
if (mtlPixFmt && cond) {
|
||||
return getMTLPixelFormatDesc(mtlPixFmt).mtlFmtCaps;
|
||||
@@ -883,22 +887,22 @@ MTLFmtCaps &PixelFormats::getMTLPixelFormatCapsIf(MTLPixelFormat mtlPixFmt, bool
|
||||
}
|
||||
}
|
||||
|
||||
#define setMTLPixFmtCapsIf(cond, mtlFmt, caps) getMTLPixelFormatCapsIf(MTLPixelFormat##mtlFmt, cond) = kMTLFmtCaps##caps;
|
||||
#define setMTLPixFmtCapsIf(cond, mtlFmt, caps) getMTLPixelFormatCapsIf(MTL::PixelFormat##mtlFmt, cond) = kMTLFmtCaps##caps;
|
||||
#define setMTLPixFmtCapsIfGPU(gpuFam, mtlFmt, caps) setMTLPixFmtCapsIf(gpuCaps.supports##gpuFam, mtlFmt, caps)
|
||||
|
||||
#define enableMTLPixFmtCapsIf(cond, mtlFmt, caps) flags::set(getMTLPixelFormatCapsIf(MTLPixelFormat##mtlFmt, cond), kMTLFmtCaps##caps);
|
||||
#define enableMTLPixFmtCapsIfGPU(gpuFam, mtlFmt, caps) enableMTLPixFmtCapsIf(p_feat.highestFamily >= MTLGPUFamily##gpuFam, mtlFmt, caps)
|
||||
#define enableMTLPixFmtCapsIf(cond, mtlFmt, caps) flags::set(getMTLPixelFormatCapsIf(MTL::PixelFormat##mtlFmt, cond), kMTLFmtCaps##caps);
|
||||
#define enableMTLPixFmtCapsIfGPU(gpuFam, mtlFmt, caps) enableMTLPixFmtCapsIf(p_feat.highestFamily >= MTL::GPUFamily##gpuFam, mtlFmt, caps)
|
||||
|
||||
#define disableMTLPixFmtCapsIf(cond, mtlFmt, caps) flags::clear(getMTLPixelFormatCapsIf(MTLPixelFormat##mtlFmt, cond), kMTLFmtCaps##caps);
|
||||
#define disableMTLPixFmtCapsIf(cond, mtlFmt, caps) flags::clear(getMTLPixelFormatCapsIf(MTL::PixelFormat##mtlFmt, cond), kMTLFmtCaps##caps);
|
||||
|
||||
// Modifies the format capability tables based on the capabilities of the specific MTLDevice.
|
||||
void PixelFormats::modifyMTLFormatCapabilities(const MetalFeatures &p_feat) {
|
||||
bool noVulkanSupport = false; // Indicated supported in Metal but not Vulkan or SPIR-V.
|
||||
bool notMac = !p_feat.supportsMac;
|
||||
bool iosOnly1 = notMac && p_feat.highestFamily < MTLGPUFamilyApple2;
|
||||
bool iosOnly2 = notMac && p_feat.highestFamily < MTLGPUFamilyApple3;
|
||||
bool iosOnly6 = notMac && p_feat.highestFamily < MTLGPUFamilyApple7;
|
||||
bool iosOnly8 = notMac && p_feat.highestFamily < MTLGPUFamilyApple9;
|
||||
bool iosOnly1 = notMac && p_feat.highestFamily < MTL::GPUFamilyApple2;
|
||||
bool iosOnly2 = notMac && p_feat.highestFamily < MTL::GPUFamilyApple3;
|
||||
bool iosOnly6 = notMac && p_feat.highestFamily < MTL::GPUFamilyApple7;
|
||||
bool iosOnly8 = notMac && p_feat.highestFamily < MTL::GPUFamilyApple9;
|
||||
|
||||
setMTLPixFmtCapsIf(iosOnly2, A8Unorm, RF);
|
||||
setMTLPixFmtCapsIf(iosOnly1, R8Unorm_sRGB, RFCMRB);
|
||||
@@ -934,7 +938,7 @@ void PixelFormats::modifyMTLFormatCapabilities(const MetalFeatures &p_feat) {
|
||||
|
||||
// Metal supports reading both R&G into as one 64-bit atomic operation, but Vulkan and SPIR-V do not.
|
||||
// Including this here so we remember to update this if support is added to Vulkan in the future.
|
||||
bool atomic64 = noVulkanSupport && (p_feat.highestFamily >= MTLGPUFamilyApple9 || (p_feat.highestFamily >= MTLGPUFamilyApple8 && p_feat.supportsMac));
|
||||
bool atomic64 = noVulkanSupport && (p_feat.highestFamily >= MTL::GPUFamilyApple9 || (p_feat.highestFamily >= MTL::GPUFamilyApple8 && p_feat.supportsMac));
|
||||
enableMTLPixFmtCapsIf(atomic64, RG32Uint, Atomic);
|
||||
enableMTLPixFmtCapsIf(atomic64, RG32Sint, Atomic);
|
||||
|
||||
@@ -961,7 +965,7 @@ void PixelFormats::modifyMTLFormatCapabilities(const MetalFeatures &p_feat) {
|
||||
enableMTLPixFmtCapsIf(floatFB, RGBA32Float, Filter);
|
||||
enableMTLPixFmtCapsIf(floatFB, RGBA32Float, Blend); // Undocumented by confirmed through testing.
|
||||
|
||||
bool noHDR_ASTC = p_feat.highestFamily < MTLGPUFamilyApple6;
|
||||
bool noHDR_ASTC = p_feat.highestFamily < MTL::GPUFamilyApple6;
|
||||
setMTLPixFmtCapsIf(noHDR_ASTC, ASTC_4x4_HDR, None);
|
||||
setMTLPixFmtCapsIf(noHDR_ASTC, ASTC_5x4_HDR, None);
|
||||
setMTLPixFmtCapsIf(noHDR_ASTC, ASTC_5x5_HDR, None);
|
||||
@@ -1021,13 +1025,13 @@ void PixelFormats::buildDFFormatMaps() {
|
||||
mtlDesc.dataFormat = dfDesc.dataFormat;
|
||||
}
|
||||
if (!mtlDesc.isSupported()) {
|
||||
dfDesc.mtlPixelFormat = MTLPixelFormatInvalid;
|
||||
dfDesc.mtlPixelFormat = MTL::PixelFormatInvalid;
|
||||
}
|
||||
}
|
||||
if (dfDesc.mtlPixelFormatSubstitute) {
|
||||
MTLFormatDesc &mtlDesc = getMTLPixelFormatDesc(dfDesc.mtlPixelFormatSubstitute);
|
||||
if (!mtlDesc.isSupported()) {
|
||||
dfDesc.mtlPixelFormatSubstitute = MTLPixelFormatInvalid;
|
||||
dfDesc.mtlPixelFormatSubstitute = MTL::PixelFormatInvalid;
|
||||
}
|
||||
}
|
||||
if (dfDesc.mtlVertexFormat) {
|
||||
@@ -1036,13 +1040,13 @@ void PixelFormats::buildDFFormatMaps() {
|
||||
mtlDesc.dataFormat = dfDesc.dataFormat;
|
||||
}
|
||||
if (!mtlDesc.isSupported()) {
|
||||
dfDesc.mtlVertexFormat = MTLVertexFormatInvalid;
|
||||
dfDesc.mtlVertexFormat = MTL::VertexFormatInvalid;
|
||||
}
|
||||
}
|
||||
if (dfDesc.mtlVertexFormatSubstitute) {
|
||||
MTLFormatDesc &mtlDesc = getMTLVertexFormatDesc(dfDesc.mtlVertexFormatSubstitute);
|
||||
if (!mtlDesc.isSupported()) {
|
||||
dfDesc.mtlVertexFormatSubstitute = MTLVertexFormatInvalid;
|
||||
dfDesc.mtlVertexFormatSubstitute = MTL::VertexFormatInvalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,12 +54,15 @@
|
||||
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations")
|
||||
|
||||
#import "inflection_map.h"
|
||||
#import "metal_device_properties.h"
|
||||
#include "inflection_map.h"
|
||||
#include "metal_device_properties.h"
|
||||
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#ifdef __OBJC__
|
||||
#include <Metal/Metal.h>
|
||||
#endif
|
||||
#include <Metal/Metal.hpp>
|
||||
#include <iterator>
|
||||
|
||||
#pragma mark -
|
||||
@@ -197,10 +200,10 @@ struct ComponentMapping {
|
||||
/** Describes the properties of a DataFormat, including the corresponding Metal pixel and vertex format. */
|
||||
struct DataFormatDesc {
|
||||
RD::DataFormat dataFormat;
|
||||
MTLPixelFormat mtlPixelFormat;
|
||||
MTLPixelFormat mtlPixelFormatSubstitute;
|
||||
MTLVertexFormat mtlVertexFormat;
|
||||
MTLVertexFormat mtlVertexFormatSubstitute;
|
||||
MTL::PixelFormat mtlPixelFormat;
|
||||
MTL::PixelFormat mtlPixelFormatSubstitute;
|
||||
MTL::VertexFormat mtlVertexFormat;
|
||||
MTL::VertexFormat mtlVertexFormatSubstitute;
|
||||
uint8_t chromaSubsamplingPlaneCount;
|
||||
uint8_t chromaSubsamplingComponentBits;
|
||||
Extent2D blockTexelSize;
|
||||
@@ -212,11 +215,11 @@ struct DataFormatDesc {
|
||||
|
||||
inline double bytesPerTexel() const { return (double)bytesPerBlock / (double)(blockTexelSize.width * blockTexelSize.height); }
|
||||
|
||||
inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid || chromaSubsamplingPlaneCount > 1); }
|
||||
inline bool isSupportedOrSubstitutable() const { return isSupported() || (mtlPixelFormatSubstitute != MTLPixelFormatInvalid); }
|
||||
inline bool isSupported() const { return (mtlPixelFormat != MTL::PixelFormatInvalid || chromaSubsamplingPlaneCount > 1); }
|
||||
inline bool isSupportedOrSubstitutable() const { return isSupported() || (mtlPixelFormatSubstitute != MTL::PixelFormatInvalid); }
|
||||
|
||||
inline bool vertexIsSupported() const { return (mtlVertexFormat != MTLVertexFormatInvalid); }
|
||||
inline bool vertexIsSupportedOrSubstitutable() const { return vertexIsSupported() || (mtlVertexFormatSubstitute != MTLVertexFormatInvalid); }
|
||||
inline bool vertexIsSupported() const { return (mtlVertexFormat != MTL::VertexFormatInvalid); }
|
||||
inline bool vertexIsSupportedOrSubstitutable() const { return vertexIsSupported() || (mtlVertexFormatSubstitute != MTL::VertexFormatInvalid); }
|
||||
|
||||
bool needsSwizzle() const {
|
||||
return (componentMapping.r != RD::TEXTURE_SWIZZLE_IDENTITY ||
|
||||
@@ -226,19 +229,19 @@ struct DataFormatDesc {
|
||||
}
|
||||
};
|
||||
|
||||
/** Describes the properties of a MTLPixelFormat or MTLVertexFormat. */
|
||||
/** Describes the properties of a MTL::PixelFormat or MTL::VertexFormat. */
|
||||
struct MTLFormatDesc {
|
||||
union {
|
||||
MTLPixelFormat mtlPixelFormat;
|
||||
MTLVertexFormat mtlVertexFormat;
|
||||
MTL::PixelFormat mtlPixelFormat;
|
||||
MTL::VertexFormat mtlVertexFormat;
|
||||
};
|
||||
RD::DataFormat dataFormat = RD::DATA_FORMAT_MAX;
|
||||
MTLFmtCaps mtlFmtCaps;
|
||||
MTLViewClass mtlViewClass;
|
||||
MTLPixelFormat mtlPixelFormatLinear;
|
||||
MTL::PixelFormat mtlPixelFormatLinear;
|
||||
const char *name = nullptr;
|
||||
|
||||
inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid) && (mtlFmtCaps != kMTLFmtCapsNone); }
|
||||
inline bool isSupported() const { return (mtlPixelFormat != MTL::PixelFormatInvalid) && (mtlFmtCaps != kMTLFmtCapsNone); }
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) PixelFormats {
|
||||
@@ -251,14 +254,14 @@ public:
|
||||
/** Returns whether the DataFormat is supported by this implementation, or can be substituted by one that is. */
|
||||
bool isSupportedOrSubstitutable(DataFormat p_format);
|
||||
|
||||
/** Returns whether the specified Metal MTLPixelFormat can be used as a depth format. */
|
||||
_FORCE_INLINE_ bool isDepthFormat(MTLPixelFormat p_format) {
|
||||
/** Returns whether the specified Metal MTL::PixelFormat can be used as a depth format. */
|
||||
_FORCE_INLINE_ bool isDepthFormat(MTL::PixelFormat p_format) {
|
||||
switch (p_format) {
|
||||
case MTLPixelFormatDepth32Float:
|
||||
case MTLPixelFormatDepth16Unorm:
|
||||
case MTLPixelFormatDepth32Float_Stencil8:
|
||||
case MTL::PixelFormatDepth32Float:
|
||||
case MTL::PixelFormatDepth16Unorm:
|
||||
case MTL::PixelFormatDepth32Float_Stencil8:
|
||||
#if TARGET_OS_OSX
|
||||
case MTLPixelFormatDepth24Unorm_Stencil8:
|
||||
case MTL::PixelFormatDepth24Unorm_Stencil8:
|
||||
#endif
|
||||
return true;
|
||||
default:
|
||||
@@ -266,42 +269,42 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether the specified Metal MTLPixelFormat can be used as a stencil format. */
|
||||
_FORCE_INLINE_ bool isStencilFormat(MTLPixelFormat p_format) {
|
||||
/** Returns whether the specified Metal MTL::PixelFormat can be used as a stencil format. */
|
||||
_FORCE_INLINE_ bool isStencilFormat(MTL::PixelFormat p_format) {
|
||||
switch (p_format) {
|
||||
case MTLPixelFormatStencil8:
|
||||
case MTL::PixelFormatStencil8:
|
||||
#if TARGET_OS_OSX
|
||||
case MTLPixelFormatDepth24Unorm_Stencil8:
|
||||
case MTLPixelFormatX24_Stencil8:
|
||||
case MTL::PixelFormatDepth24Unorm_Stencil8:
|
||||
case MTL::PixelFormatX24_Stencil8:
|
||||
#endif
|
||||
case MTLPixelFormatDepth32Float_Stencil8:
|
||||
case MTLPixelFormatX32_Stencil8:
|
||||
case MTL::PixelFormatDepth32Float_Stencil8:
|
||||
case MTL::PixelFormatX32_Stencil8:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether the specified Metal MTLPixelFormat is a PVRTC format. */
|
||||
bool isPVRTCFormat(MTLPixelFormat p_format);
|
||||
/** Returns whether the specified Metal MTL::PixelFormat is a PVRTC format. */
|
||||
bool isPVRTCFormat(MTL::PixelFormat p_format);
|
||||
|
||||
/** Returns the format type corresponding to the specified Godot pixel format, */
|
||||
MTLFormatType getFormatType(DataFormat p_format);
|
||||
|
||||
/** Returns the format type corresponding to the specified Metal MTLPixelFormat, */
|
||||
MTLFormatType getFormatType(MTLPixelFormat p_format);
|
||||
/** Returns the format type corresponding to the specified Metal MTL::PixelFormat, */
|
||||
MTLFormatType getFormatType(MTL::PixelFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the Metal MTLPixelFormat corresponding to the specified Godot pixel
|
||||
* or returns MTLPixelFormatInvalid if no corresponding MTLPixelFormat exists.
|
||||
* Returns the Metal MTL::PixelFormat corresponding to the specified Godot pixel
|
||||
* or returns MTL::PixelFormatInvalid if no corresponding MTL::PixelFormat exists.
|
||||
*/
|
||||
MTLPixelFormat getMTLPixelFormat(DataFormat p_format);
|
||||
MTL::PixelFormat getMTLPixelFormat(DataFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the DataFormat corresponding to the specified Metal MTLPixelFormat,
|
||||
* Returns the DataFormat corresponding to the specified Metal MTL::PixelFormat,
|
||||
* or returns DATA_FORMAT_MAX if no corresponding DataFormat exists.
|
||||
*/
|
||||
DataFormat getDataFormat(MTLPixelFormat p_format);
|
||||
DataFormat getDataFormat(MTL::PixelFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texel block of the specified Godot pixel.
|
||||
@@ -313,7 +316,7 @@ public:
|
||||
* Returns the size, in bytes, of a texel block of the specified Metal format.
|
||||
* For uncompressed formats, the returned value corresponds to the size in bytes of a single texel.
|
||||
*/
|
||||
uint32_t getBytesPerBlock(MTLPixelFormat p_format);
|
||||
uint32_t getBytesPerBlock(MTL::PixelFormat p_format);
|
||||
|
||||
/** Returns the number of planes of the specified chroma-subsampling (YCbCr) DataFormat */
|
||||
uint8_t getChromaSubsamplingPlaneCount(DataFormat p_format);
|
||||
@@ -331,7 +334,7 @@ public:
|
||||
* Returns the size, in bytes, of a texel of the specified Metal format.
|
||||
* The returned value may be fractional for certain compressed formats.
|
||||
*/
|
||||
float getBytesPerTexel(MTLPixelFormat p_format);
|
||||
float getBytesPerTexel(MTL::PixelFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a row of texels of the specified Godot pixel format.
|
||||
@@ -349,7 +352,7 @@ public:
|
||||
* and texelsPerRow should specify the width in texels, not blocks. The result is rounded
|
||||
* up if texelsPerRow is not an integer multiple of the compression block width.
|
||||
*/
|
||||
size_t getBytesPerRow(MTLPixelFormat p_format, uint32_t p_texels_per_row);
|
||||
size_t getBytesPerRow(MTL::PixelFormat p_format, uint32_t p_texels_per_row);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texture layer of the specified Godot pixel format.
|
||||
@@ -366,7 +369,7 @@ public:
|
||||
* and p_texel_rows_per_layer should specify the height in texels, not blocks. The result is
|
||||
* rounded up if p_texel_rows_per_layer is not an integer multiple of the compression block height.
|
||||
*/
|
||||
size_t getBytesPerLayer(MTLPixelFormat p_format, size_t p_bytes_per_row, uint32_t p_texel_rows_per_layer);
|
||||
size_t getBytesPerLayer(MTL::PixelFormat p_format, size_t p_bytes_per_row, uint32_t p_texel_rows_per_layer);
|
||||
|
||||
/** Returns whether or not the specified Godot format requires swizzling to use with Metal. */
|
||||
bool needsSwizzle(DataFormat p_format);
|
||||
@@ -375,37 +378,38 @@ public:
|
||||
MTLFmtCaps getCapabilities(DataFormat p_format, bool p_extended = false);
|
||||
|
||||
/** Returns the Metal format capabilities supported by the specified Metal format. */
|
||||
MTLFmtCaps getCapabilities(MTLPixelFormat p_format, bool p_extended = false);
|
||||
MTLFmtCaps getCapabilities(MTL::PixelFormat p_format, bool p_extended = false);
|
||||
|
||||
/**
|
||||
* Returns the Metal MTLVertexFormat corresponding to the specified
|
||||
* Returns the Metal MTL::VertexFormat corresponding to the specified
|
||||
* DataFormat as used as a vertex attribute format.
|
||||
*/
|
||||
MTLVertexFormat getMTLVertexFormat(DataFormat p_format);
|
||||
MTL::VertexFormat getMTLVertexFormat(DataFormat p_format);
|
||||
|
||||
#pragma mark Construction
|
||||
|
||||
explicit PixelFormats(id<MTLDevice> p_device, const MetalFeatures &p_feat);
|
||||
explicit PixelFormats(MTL::Device *p_device, const MetalFeatures &p_feat);
|
||||
~PixelFormats();
|
||||
|
||||
protected:
|
||||
DataFormatDesc &getDataFormatDesc(DataFormat p_format);
|
||||
DataFormatDesc &getDataFormatDesc(MTLPixelFormat p_format);
|
||||
MTLFormatDesc &getMTLPixelFormatDesc(MTLPixelFormat p_format);
|
||||
MTLFmtCaps &getMTLPixelFormatCapsIf(MTLPixelFormat mtlPixFmt, bool cond);
|
||||
MTLFormatDesc &getMTLVertexFormatDesc(MTLVertexFormat p_format);
|
||||
DataFormatDesc &getDataFormatDesc(MTL::PixelFormat p_format);
|
||||
MTLFormatDesc &getMTLPixelFormatDesc(MTL::PixelFormat p_format);
|
||||
MTLFmtCaps &getMTLPixelFormatCapsIf(MTL::PixelFormat mtlPixFmt, bool cond);
|
||||
MTLFormatDesc &getMTLVertexFormatDesc(MTL::VertexFormat p_format);
|
||||
|
||||
void initDataFormatCapabilities();
|
||||
void initMTLPixelFormatCapabilities();
|
||||
void initMTLVertexFormatCapabilities(const MetalFeatures &p_feat);
|
||||
void modifyMTLFormatCapabilities(const MetalFeatures &p_feat);
|
||||
void buildDFFormatMaps();
|
||||
void addMTLPixelFormatDescImpl(MTLPixelFormat p_pix_fmt, MTLPixelFormat p_pix_fmt_linear,
|
||||
void addMTLPixelFormatDescImpl(MTL::PixelFormat p_pix_fmt, MTL::PixelFormat p_pix_fmt_linear,
|
||||
MTLViewClass p_view_class, MTLFmtCaps p_fmt_caps, const char *p_name);
|
||||
void addMTLVertexFormatDescImpl(MTLVertexFormat p_vert_fmt, MTLFmtCaps p_vert_caps, const char *name);
|
||||
void addMTLVertexFormatDescImpl(MTL::VertexFormat p_vert_fmt, MTLFmtCaps p_vert_caps, const char *name);
|
||||
|
||||
id<MTLDevice> device;
|
||||
MTL::Device *device;
|
||||
InflectionMap<DataFormat, DataFormatDesc, RD::DATA_FORMAT_MAX> _data_format_descs;
|
||||
InflectionMap<uint16_t, MTLFormatDesc, MTLPixelFormatX32_Stencil8 + 2> _mtl_pixel_format_descs; // The actual last enum value is not available on iOS.
|
||||
InflectionMap<uint16_t, MTLFormatDesc, MTL::PixelFormatX32_Stencil8 + 2> _mtl_pixel_format_descs; // The actual last enum value is not available on iOS.
|
||||
TightLocalVector<MTLFormatDesc> _mtl_vertex_format_descs;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_context_driver_metal.mm */
|
||||
/* rendering_context_driver_metal.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
@@ -28,14 +28,35 @@
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "rendering_context_driver_metal.h"
|
||||
#include "rendering_context_driver_metal.h"
|
||||
|
||||
#import "rendering_device_driver_metal.h"
|
||||
#include "metal3_objects.h"
|
||||
#include "metal_objects_shared.h"
|
||||
#include "rendering_device_driver_metal3.h"
|
||||
|
||||
#include "core/templates/sort_array.h"
|
||||
|
||||
#import <os/log.h>
|
||||
#import <os/signpost.h>
|
||||
#include <os/log.h>
|
||||
#include <os/signpost.h>
|
||||
|
||||
#include <objc/message.h>
|
||||
|
||||
// Selector helper for calling ObjC methods from C++
|
||||
#define _APPLE_PRIVATE_DEF_SEL(accessor, symbol) static SEL s_k##accessor = sel_registerName(symbol)
|
||||
#define _APPLE_PRIVATE_SEL(accessor) (Private::Selector::s_k##accessor)
|
||||
|
||||
namespace Private::Selector {
|
||||
|
||||
_APPLE_PRIVATE_DEF_SEL(setOpaque_, "setOpaque:");
|
||||
|
||||
template <typename _Ret, typename... _Args>
|
||||
_NS_INLINE _Ret sendMessage(const void *pObj, SEL selector, _Args... args) {
|
||||
using SendMessageProc = _Ret (*)(const void *, SEL, _Args...);
|
||||
const SendMessageProc pProc = reinterpret_cast<SendMessageProc>(&objc_msgSend);
|
||||
return (*pProc)(pObj, selector, args...);
|
||||
}
|
||||
|
||||
} // namespace Private::Selector
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
@@ -48,12 +69,6 @@ __attribute__((constructor)) static void InitializeLogging(void) {
|
||||
LOG_INTERVALS = os_log_create("org.godotengine.godot.metal", "events");
|
||||
}
|
||||
|
||||
@protocol MTLDeviceEx <MTLDevice>
|
||||
#if TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 130300
|
||||
- (void)setShouldMaximizeConcurrentCompilation:(BOOL)v;
|
||||
#endif
|
||||
@end
|
||||
|
||||
RenderingContextDriverMetal::RenderingContextDriverMetal() {
|
||||
}
|
||||
|
||||
@@ -61,14 +76,14 @@ RenderingContextDriverMetal::~RenderingContextDriverMetal() {
|
||||
}
|
||||
|
||||
Error RenderingContextDriverMetal::initialize() {
|
||||
if (OS::get_singleton()->get_environment("MTL_CAPTURE_ENABLED") == "1") {
|
||||
if (OS::get_singleton()->get_environment("MTL_CAPTURE_ENABLED") == "1" || OS::get_singleton()->get_environment("MTLCAPTURE_DESTINATION_DEVELOPER_TOOLS_ENABLE") == "1") {
|
||||
capture_available = true;
|
||||
}
|
||||
|
||||
metal_device = MTLCreateSystemDefaultDevice();
|
||||
metal_device = MTL::CreateSystemDefaultDevice();
|
||||
#if TARGET_OS_OSX
|
||||
if (@available(macOS 13.3, *)) {
|
||||
[id<MTLDeviceEx>(metal_device) setShouldMaximizeConcurrentCompilation:YES];
|
||||
if (__builtin_available(macOS 13.3, *)) {
|
||||
metal_device->setShouldMaximizeConcurrentCompilation(true);
|
||||
}
|
||||
#endif
|
||||
device.type = DEVICE_TYPE_INTEGRATED_GPU;
|
||||
@@ -76,8 +91,8 @@ Error RenderingContextDriverMetal::initialize() {
|
||||
device.workarounds = Workarounds();
|
||||
|
||||
MetalDeviceProperties props(metal_device);
|
||||
int version = (int)props.features.highestFamily - (int)MTLGPUFamilyApple1 + 1;
|
||||
device.name = vformat("%s (Apple%d)", metal_device.name.UTF8String, version);
|
||||
int version = (int)props.features.highestFamily - (int)MTL::GPUFamilyApple1 + 1;
|
||||
device.name = vformat("%s (Apple%d)", metal_device->name()->utf8String(), version);
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -92,7 +107,7 @@ uint32_t RenderingContextDriverMetal::device_get_count() const {
|
||||
}
|
||||
|
||||
RenderingDeviceDriver *RenderingContextDriverMetal::driver_create() {
|
||||
return memnew(RenderingDeviceDriverMetal(this));
|
||||
return memnew(MTL3::RenderingDeviceDriverMetal(this));
|
||||
}
|
||||
|
||||
void RenderingContextDriverMetal::driver_free(RenderingDeviceDriver *p_driver) {
|
||||
@@ -100,25 +115,25 @@ void RenderingContextDriverMetal::driver_free(RenderingDeviceDriver *p_driver) {
|
||||
}
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) SurfaceLayer : public RenderingContextDriverMetal::Surface {
|
||||
CAMetalLayer *__unsafe_unretained layer = nil;
|
||||
CA::MetalLayer *layer = nullptr;
|
||||
LocalVector<MDFrameBuffer> frame_buffers;
|
||||
LocalVector<id<MTLDrawable>> drawables;
|
||||
LocalVector<MTL::Drawable *> drawables;
|
||||
uint32_t rear = -1;
|
||||
uint32_t front = 0;
|
||||
uint32_t count = 0;
|
||||
|
||||
public:
|
||||
SurfaceLayer(CAMetalLayer *p_layer, id<MTLDevice> p_device) :
|
||||
SurfaceLayer(CA::MetalLayer *p_layer, MTL::Device *p_device) :
|
||||
Surface(p_device), layer(p_layer) {
|
||||
layer.allowsNextDrawableTimeout = YES;
|
||||
layer.framebufferOnly = YES;
|
||||
layer.opaque = OS::get_singleton()->is_layered_allowed() ? NO : YES;
|
||||
layer.pixelFormat = get_pixel_format();
|
||||
layer.device = p_device;
|
||||
layer->setAllowsNextDrawableTimeout(true);
|
||||
layer->setFramebufferOnly(true);
|
||||
Private::Selector::sendMessage<void>(layer, _APPLE_PRIVATE_SEL(setOpaque_), !OS::get_singleton()->is_layered_allowed());
|
||||
layer->setPixelFormat(get_pixel_format());
|
||||
layer->setDevice(p_device);
|
||||
}
|
||||
|
||||
~SurfaceLayer() override {
|
||||
layer = nil;
|
||||
layer = nullptr;
|
||||
}
|
||||
|
||||
Error resize(uint32_t p_desired_framebuffer_count) override final {
|
||||
@@ -128,14 +143,14 @@ public:
|
||||
}
|
||||
|
||||
CGSize drawableSize = CGSizeMake(width, height);
|
||||
CGSize current = layer.drawableSize;
|
||||
CGSize current = layer->drawableSize();
|
||||
if (!CGSizeEqualToSize(current, drawableSize)) {
|
||||
layer.drawableSize = drawableSize;
|
||||
layer->setDrawableSize(drawableSize);
|
||||
}
|
||||
|
||||
// Metal supports a maximum of 3 drawables.
|
||||
p_desired_framebuffer_count = MIN(3U, p_desired_framebuffer_count);
|
||||
layer.maximumDrawableCount = p_desired_framebuffer_count;
|
||||
layer->setMaximumDrawableCount(p_desired_framebuffer_count);
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
// Display sync is only supported on macOS.
|
||||
@@ -143,10 +158,10 @@ public:
|
||||
case DisplayServer::VSYNC_MAILBOX:
|
||||
case DisplayServer::VSYNC_ADAPTIVE:
|
||||
case DisplayServer::VSYNC_ENABLED:
|
||||
layer.displaySyncEnabled = YES;
|
||||
layer->setDisplaySyncEnabled(true);
|
||||
break;
|
||||
case DisplayServer::VSYNC_DISABLED:
|
||||
layer.displaySyncEnabled = NO;
|
||||
layer->setDisplaySyncEnabled(false);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@@ -171,56 +186,77 @@ public:
|
||||
MDFrameBuffer &frame_buffer = frame_buffers[rear];
|
||||
frame_buffer.size = Size2i(width, height);
|
||||
|
||||
id<CAMetalDrawable> drawable = layer.nextDrawable;
|
||||
CA::MetalDrawable *drawable = layer->nextDrawable();
|
||||
ERR_FAIL_NULL_V_MSG(drawable, RDD::FramebufferID(), "no drawable available");
|
||||
drawables[rear] = drawable;
|
||||
frame_buffer.set_texture(0, drawable.texture);
|
||||
frame_buffer.set_texture(0, drawable->texture());
|
||||
|
||||
return RDD::FramebufferID(&frame_buffer);
|
||||
}
|
||||
|
||||
void present(MDCommandBuffer *p_cmd_buffer) override final {
|
||||
void present(MTL3::MDCommandBuffer *p_cmd_buffer) override final {
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Release texture and drawable.
|
||||
frame_buffers[front].unset_texture(0);
|
||||
id<MTLDrawable> drawable = drawables[front];
|
||||
drawables[front] = nil;
|
||||
MTL::Drawable *drawable = drawables[front];
|
||||
drawables[front] = nullptr;
|
||||
|
||||
count--;
|
||||
front = (front + 1) % frame_buffers.size();
|
||||
|
||||
if (vsync_mode != DisplayServer::VSYNC_DISABLED) {
|
||||
[p_cmd_buffer->get_command_buffer() presentDrawable:drawable afterMinimumDuration:present_minimum_duration];
|
||||
p_cmd_buffer->get_command_buffer()->presentDrawableAfterMinimumDuration(drawable, present_minimum_duration);
|
||||
} else {
|
||||
[p_cmd_buffer->get_command_buffer() presentDrawable:drawable];
|
||||
p_cmd_buffer->get_command_buffer()->presentDrawable(drawable);
|
||||
}
|
||||
}
|
||||
|
||||
MTL::Drawable *next_drawable() override final {
|
||||
if (count == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Release texture and drawable.
|
||||
frame_buffers[front].unset_texture(0);
|
||||
MTL::Drawable *drawable = drawables[front];
|
||||
drawables[front] = nullptr;
|
||||
|
||||
count--;
|
||||
front = (front + 1) % frame_buffers.size();
|
||||
|
||||
return drawable;
|
||||
}
|
||||
|
||||
API_AVAILABLE(macos(26.0), ios(26.0))
|
||||
MTL::ResidencySet *get_residency_set() const override final {
|
||||
return layer->residencySet();
|
||||
}
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) SurfaceOffscreen : public RenderingContextDriverMetal::Surface {
|
||||
int frame_buffer_size = 3;
|
||||
MDFrameBuffer *frame_buffers;
|
||||
LocalVector<id<MTLTexture>> textures;
|
||||
LocalVector<id<MTLDrawable>> drawables;
|
||||
LocalVector<MTL::Texture *> textures;
|
||||
LocalVector<MTL::Drawable *> drawables;
|
||||
|
||||
int32_t rear = -1;
|
||||
std::atomic_int count;
|
||||
uint64_t target_time = 0;
|
||||
CAMetalLayer *layer;
|
||||
CA::MetalLayer *layer;
|
||||
|
||||
public:
|
||||
SurfaceOffscreen(CAMetalLayer *p_layer, id<MTLDevice> p_device) :
|
||||
SurfaceOffscreen(CA::MetalLayer *p_layer, MTL::Device *p_device) :
|
||||
Surface(p_device), layer(p_layer) {
|
||||
layer.allowsNextDrawableTimeout = YES;
|
||||
layer.framebufferOnly = YES;
|
||||
layer.opaque = OS::get_singleton()->is_layered_allowed() ? NO : YES;
|
||||
layer.pixelFormat = get_pixel_format();
|
||||
layer.device = p_device;
|
||||
layer->setAllowsNextDrawableTimeout(true);
|
||||
layer->setFramebufferOnly(true);
|
||||
Private::Selector::sendMessage<void>(layer, _APPLE_PRIVATE_SEL(setOpaque_), !OS::get_singleton()->is_layered_allowed());
|
||||
layer->setPixelFormat(get_pixel_format());
|
||||
layer->setDevice(p_device);
|
||||
#if TARGET_OS_OSX
|
||||
layer.displaySyncEnabled = NO;
|
||||
layer->setDisplaySyncEnabled(false);
|
||||
#endif
|
||||
target_time = OS::get_singleton()->get_ticks_usec();
|
||||
|
||||
@@ -244,9 +280,9 @@ public:
|
||||
}
|
||||
|
||||
CGSize drawableSize = CGSizeMake(width, height);
|
||||
CGSize current = layer.drawableSize;
|
||||
CGSize current = layer->drawableSize();
|
||||
if (!CGSizeEqualToSize(current, drawableSize)) {
|
||||
layer.drawableSize = drawableSize;
|
||||
layer->setDrawableSize(drawableSize);
|
||||
}
|
||||
|
||||
return OK;
|
||||
@@ -263,22 +299,22 @@ public:
|
||||
|
||||
MDFrameBuffer &frame_buffer = frame_buffers[rear];
|
||||
|
||||
if (textures[rear] == nil || textures[rear].width != width || textures[rear].height != height) {
|
||||
MTLTextureDescriptor *texture_descriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:get_pixel_format() width:width height:height mipmapped:NO];
|
||||
texture_descriptor.usage = MTLTextureUsageRenderTarget;
|
||||
texture_descriptor.hazardTrackingMode = MTLHazardTrackingModeUntracked;
|
||||
texture_descriptor.storageMode = MTLStorageModePrivate;
|
||||
textures[rear] = [device newTextureWithDescriptor:texture_descriptor];
|
||||
if (textures[rear] == nullptr || textures[rear]->width() != width || textures[rear]->height() != height) {
|
||||
MTL::TextureDescriptor *texture_descriptor = MTL::TextureDescriptor::texture2DDescriptor(get_pixel_format(), width, height, false);
|
||||
texture_descriptor->setUsage(MTL::TextureUsageRenderTarget);
|
||||
texture_descriptor->setHazardTrackingMode(MTL::HazardTrackingModeUntracked);
|
||||
texture_descriptor->setStorageMode(MTL::StorageModePrivate);
|
||||
textures[rear] = device->newTexture(texture_descriptor);
|
||||
}
|
||||
|
||||
frame_buffer.size = Size2i(width, height);
|
||||
uint64_t now = OS::get_singleton()->get_ticks_usec();
|
||||
if (now >= target_time) {
|
||||
target_time = now + 1'000'000; // 1 second into the future.
|
||||
id<CAMetalDrawable> drawable = layer.nextDrawable;
|
||||
CA::MetalDrawable *drawable = layer->nextDrawable();
|
||||
ERR_FAIL_NULL_V_MSG(drawable, RDD::FramebufferID(), "no drawable available");
|
||||
drawables[rear] = drawable;
|
||||
frame_buffer.set_texture(0, drawable.texture);
|
||||
frame_buffer.set_texture(0, drawable->texture());
|
||||
} else {
|
||||
frame_buffer.set_texture(0, textures[rear]);
|
||||
}
|
||||
@@ -286,18 +322,39 @@ public:
|
||||
return RDD::FramebufferID(&frame_buffers[rear]);
|
||||
}
|
||||
|
||||
void present(MDCommandBuffer *p_cmd_buffer) override final {
|
||||
void present(MTL3::MDCommandBuffer *p_cmd_buffer) override final {
|
||||
MDFrameBuffer *frame_buffer = &frame_buffers[rear];
|
||||
|
||||
if (drawables[rear] != nil) {
|
||||
[p_cmd_buffer->get_command_buffer() presentDrawable:drawables[rear]];
|
||||
drawables[rear] = nil;
|
||||
if (drawables[rear] != nullptr) {
|
||||
p_cmd_buffer->get_command_buffer()->presentDrawable(drawables[rear]);
|
||||
drawables[rear] = nullptr;
|
||||
}
|
||||
|
||||
[p_cmd_buffer->get_command_buffer() addScheduledHandler:^(id<MTLCommandBuffer> p_command_buffer) {
|
||||
p_cmd_buffer->get_command_buffer()->addScheduledHandler([frame_buffer, this](MTL::CommandBuffer *) {
|
||||
frame_buffer->unset_texture(0);
|
||||
count.fetch_add(-1, std::memory_order_relaxed);
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
MTL::Drawable *next_drawable() override final {
|
||||
if (count == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MDFrameBuffer *frame_buffer = &frame_buffers[rear];
|
||||
|
||||
MTL::Drawable *next = drawables[rear];
|
||||
drawables[rear] = nullptr;
|
||||
|
||||
frame_buffer->unset_texture(0);
|
||||
count--;
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
API_AVAILABLE(macos(26.0), ios(26.0))
|
||||
MTL::ResidencySet *get_residency_set() const override final {
|
||||
return layer->residencySet();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -35,42 +35,18 @@
|
||||
#include "servers/rendering/rendering_context_driver.h"
|
||||
#include "servers/rendering/rendering_device_driver.h"
|
||||
|
||||
#import <CoreGraphics/CGGeometry.h>
|
||||
#include <Metal/Metal.hpp>
|
||||
#include <QuartzCore/QuartzCore.hpp>
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import "metal_objects.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CALayer.h>
|
||||
|
||||
@class CAMetalLayer;
|
||||
@protocol CAMetalDrawable;
|
||||
#else
|
||||
typedef enum MTLPixelFormat {
|
||||
MTLPixelFormatBGRA8Unorm = 80,
|
||||
} MTLPixelFormat;
|
||||
namespace MTL3 {
|
||||
class MDCommandBuffer;
|
||||
#endif
|
||||
|
||||
class PixelFormats;
|
||||
|
||||
#ifdef __OBJC__
|
||||
#define METAL_DEVICE id<MTLDevice>
|
||||
#define METAL_DRAWABLE id<MTLDrawable>
|
||||
#define METAL_LAYER CAMetalLayer *__unsafe_unretained
|
||||
#define METAL_RESIDENCY_SET id<MTLResidencySet>
|
||||
#else
|
||||
#define METAL_DEVICE void *
|
||||
#define METAL_DRAWABLE void *
|
||||
#define METAL_LAYER void *
|
||||
#define METAL_RESIDENCY_SET void *
|
||||
#endif
|
||||
}
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) RenderingContextDriverMetal : public RenderingContextDriver {
|
||||
bool capture_available = false;
|
||||
|
||||
protected:
|
||||
METAL_DEVICE metal_device = nullptr;
|
||||
MTL::Device *metal_device = nullptr;
|
||||
Device device; // There is only one device on Apple Silicon.
|
||||
|
||||
public:
|
||||
@@ -95,12 +71,12 @@ public:
|
||||
|
||||
// Platform-specific data for the Windows embedded in this driver.
|
||||
struct WindowPlatformData {
|
||||
METAL_LAYER layer;
|
||||
CA::MetalLayer *layer;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) Surface {
|
||||
protected:
|
||||
METAL_DEVICE device;
|
||||
MTL::Device *device;
|
||||
|
||||
public:
|
||||
uint32_t width = 0;
|
||||
@@ -109,18 +85,21 @@ public:
|
||||
bool needs_resize = false;
|
||||
double present_minimum_duration = 0.0;
|
||||
|
||||
Surface(METAL_DEVICE p_device) :
|
||||
Surface(MTL::Device *p_device) :
|
||||
device(p_device) {}
|
||||
virtual ~Surface() = default;
|
||||
|
||||
MTLPixelFormat get_pixel_format() const { return MTLPixelFormatBGRA8Unorm; }
|
||||
MTL::PixelFormat get_pixel_format() const { return MTL::PixelFormatBGRA8Unorm; }
|
||||
virtual Error resize(uint32_t p_desired_framebuffer_count) = 0;
|
||||
virtual RDD::FramebufferID acquire_next_frame_buffer() = 0;
|
||||
virtual void present(MDCommandBuffer *p_cmd_buffer) = 0;
|
||||
virtual void present(MTL3::MDCommandBuffer *p_cmd_buffer) = 0;
|
||||
virtual MTL::Drawable *next_drawable() = 0;
|
||||
API_AVAILABLE(macos(26.0), ios(26.0))
|
||||
virtual MTL::ResidencySet *get_residency_set() const = 0;
|
||||
void set_max_fps(int p_max_fps) { present_minimum_duration = p_max_fps ? 1.0 / p_max_fps : 0.0; }
|
||||
};
|
||||
|
||||
METAL_DEVICE get_metal_device() const {
|
||||
MTL::Device *get_metal_device() const {
|
||||
return metal_device;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,13 +30,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#import "metal_device_profile.h"
|
||||
#import "metal_objects.h"
|
||||
#include "metal_device_profile.h"
|
||||
#include "metal_objects_shared.h"
|
||||
|
||||
#include "servers/rendering/rendering_device_driver.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <variant>
|
||||
#include <Metal/Metal.hpp>
|
||||
#include <variant>
|
||||
|
||||
class RenderingShaderContainerFormatMetal;
|
||||
|
||||
@@ -48,18 +48,28 @@ class RenderingShaderContainerFormatMetal;
|
||||
|
||||
class RenderingContextDriverMetal;
|
||||
|
||||
namespace MTL3 {
|
||||
class MDCommandBuffer;
|
||||
}
|
||||
namespace MTL4 {
|
||||
class MDCommandBuffer;
|
||||
}
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) RenderingDeviceDriverMetal : public RenderingDeviceDriver {
|
||||
friend struct ShaderCacheEntry;
|
||||
friend class MDCommandBuffer;
|
||||
friend class MTL3::MDCommandBuffer;
|
||||
friend class MTL4::MDCommandBuffer;
|
||||
friend class MDUniformSet;
|
||||
|
||||
template <typename T>
|
||||
using Result = std::variant<T, Error>;
|
||||
|
||||
#pragma mark - Generic
|
||||
|
||||
protected:
|
||||
RenderingContextDriverMetal *context_driver = nullptr;
|
||||
RenderingContextDriver::Device context_device;
|
||||
id<MTLDevice> device = nil;
|
||||
MTL::Device *device = nullptr;
|
||||
|
||||
uint32_t _frame_count = 1;
|
||||
/// frame_index is a cyclic counter derived from the current frame number modulo frame_count,
|
||||
@@ -78,16 +88,84 @@ class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) RenderingDeviceDriverMet
|
||||
RDD::FragmentShadingRateCapabilities fsr_capabilities;
|
||||
RDD::FragmentDensityMapCapabilities fdm_capabilities;
|
||||
|
||||
id<MTLBinaryArchive> archive = nil;
|
||||
NS::SharedPtr<MTL::BinaryArchive> archive;
|
||||
uint32_t archive_count = 0;
|
||||
// DEV: When true, attempting to create a pipeline will fail if it cannot use the archive.
|
||||
bool archive_fail_on_miss = false;
|
||||
|
||||
id<MTLCommandQueue> device_queue = nil;
|
||||
id<MTLCaptureScope> device_scope = nil;
|
||||
/// Resources to be added to the `main_residency_set`.
|
||||
LocalVector<MTL::Resource *> _residency_add;
|
||||
/// Resources to be removed from the `main_residency_set`.
|
||||
LocalVector<MTL::Resource *> _residency_del;
|
||||
|
||||
#pragma mark - Copy Queue
|
||||
|
||||
Mutex copy_queue_mutex;
|
||||
/// A command queue used for internal copy operations.
|
||||
NS::SharedPtr<MTL::CommandQueue> copy_queue;
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
|
||||
NS::SharedPtr<MTL::ResidencySet> copy_queue_rs;
|
||||
GODOT_CLANG_WARNING_POP
|
||||
// If this is not nullptr, there are pending copy operations.
|
||||
NS::SharedPtr<MTL::CommandBuffer> copy_queue_command_buffer;
|
||||
NS::SharedPtr<MTL::BlitCommandEncoder> copy_queue_blit_encoder;
|
||||
NS::SharedPtr<MTL::Buffer> copy_queue_buffer;
|
||||
NS::UInteger copy_queue_buffer_offset = 0;
|
||||
|
||||
_FORCE_INLINE_ NS::UInteger _copy_queue_buffer_available() const {
|
||||
return copy_queue_buffer.get()->length() - copy_queue_buffer_offset;
|
||||
}
|
||||
|
||||
/// Marks p_size bytes as consumed from the copy queue buffer, aligning the offset to 16 bytes.
|
||||
_FORCE_INLINE_ void _copy_queue_buffer_consume(NS::UInteger p_size) {
|
||||
NS::UInteger aligned_offset = round_up_to_alignment(copy_queue_buffer_offset, 16);
|
||||
copy_queue_buffer_offset = aligned_offset + p_size;
|
||||
}
|
||||
|
||||
/// Returns a pointer to the current position in the copy queue buffer.
|
||||
_FORCE_INLINE_ void *_copy_queue_buffer_ptr() const {
|
||||
return static_cast<uint8_t *>(copy_queue_buffer.get()->contents()) + copy_queue_buffer_offset;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ MTL::CommandBuffer *_copy_queue_command_buffer() {
|
||||
if (!copy_queue_command_buffer) {
|
||||
DEV_ASSERT(!copy_queue_blit_encoder);
|
||||
copy_queue_command_buffer = NS::RetainPtr(copy_queue.get()->commandBufferWithUnretainedReferences());
|
||||
}
|
||||
return copy_queue_command_buffer.get();
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ MTL::BlitCommandEncoder *_copy_queue_blit_encoder() {
|
||||
if (!copy_queue_blit_encoder) {
|
||||
MTL::BlitCommandEncoder *enc = _copy_queue_command_buffer()->blitCommandEncoder();
|
||||
copy_queue_blit_encoder = NS::RetainPtr(enc);
|
||||
}
|
||||
return copy_queue_blit_encoder.get();
|
||||
}
|
||||
|
||||
void _copy_queue_copy_to_buffer(Span<uint8_t> p_src_data, MTL::Buffer *p_dst_buffer, uint64_t p_dst_offset = 0);
|
||||
void _copy_queue_flush();
|
||||
Error _copy_queue_initialize();
|
||||
|
||||
NS::SharedPtr<MTL::CaptureScope> device_scope;
|
||||
|
||||
String pipeline_cache_id;
|
||||
|
||||
Error _create_device();
|
||||
virtual MTL::CommandQueue *get_command_queue() const = 0;
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
|
||||
virtual void add_residency_set_to_main_queue(MTL::ResidencySet *p_set) = 0;
|
||||
virtual void remove_residency_set_to_main_queue(MTL::ResidencySet *p_set) = 0;
|
||||
NS::SharedPtr<MTL::ResidencySet> main_residency_set;
|
||||
GODOT_CLANG_WARNING_POP
|
||||
|
||||
bool use_barriers = false;
|
||||
MTL::ResourceOptions base_hazard_tracking = MTL::ResourceHazardTrackingModeTracked;
|
||||
|
||||
virtual Error _create_device();
|
||||
virtual void _track_resource(MTL::Resource *p_resource);
|
||||
virtual void _untrack_resource(MTL::Resource *p_resource);
|
||||
void _check_capabilities();
|
||||
Error _initialize(uint32_t p_device_index, uint32_t p_frame_count);
|
||||
|
||||
#pragma mark - Shader Cache
|
||||
|
||||
@@ -103,7 +181,7 @@ class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) RenderingDeviceDriverMet
|
||||
void shader_cache_free_entry(const SHA256Digest &key);
|
||||
|
||||
public:
|
||||
Error initialize(uint32_t p_device_index, uint32_t p_frame_count) override final;
|
||||
virtual Error initialize(uint32_t p_device_index, uint32_t p_frame_count) override = 0;
|
||||
|
||||
#pragma mark - Memory
|
||||
|
||||
@@ -111,7 +189,7 @@ public:
|
||||
|
||||
public:
|
||||
struct BufferInfo {
|
||||
id<MTLBuffer> metal_buffer;
|
||||
NS::SharedPtr<MTL::Buffer> metal_buffer;
|
||||
|
||||
_FORCE_INLINE_ bool is_dynamic() const { return _frame_idx != UINT32_MAX; }
|
||||
_FORCE_INLINE_ uint32_t frame_index() const { return _frame_idx; }
|
||||
@@ -131,7 +209,6 @@ public:
|
||||
virtual void buffer_unmap(BufferID p_buffer) override final;
|
||||
virtual uint8_t *buffer_persistent_map_advance(BufferID p_buffer, uint64_t p_frames_drawn) override final;
|
||||
virtual uint64_t buffer_get_dynamic_offsets(Span<BufferID> p_buffers) override final;
|
||||
virtual void buffer_flush(BufferID p_buffer) override final;
|
||||
virtual uint64_t buffer_get_device_address(BufferID p_buffer) override final;
|
||||
|
||||
#pragma mark - Texture
|
||||
@@ -168,6 +245,7 @@ public:
|
||||
|
||||
#pragma mark - Barriers
|
||||
|
||||
public:
|
||||
virtual void command_pipeline_barrier(
|
||||
CommandBufferID p_cmd_buffer,
|
||||
BitField<PipelineStageBits> p_src_stages,
|
||||
@@ -179,78 +257,16 @@ public:
|
||||
|
||||
#pragma mark - Fences
|
||||
|
||||
private:
|
||||
struct Fence {
|
||||
virtual void signal(id<MTLCommandBuffer> p_cmd_buffer) = 0;
|
||||
virtual Error wait(uint32_t p_timeout_ms) = 0;
|
||||
virtual ~Fence() = default;
|
||||
};
|
||||
|
||||
struct FenceEvent : public Fence {
|
||||
id<MTLSharedEvent> event;
|
||||
uint64_t value;
|
||||
FenceEvent(id<MTLSharedEvent> p_event) :
|
||||
event(p_event),
|
||||
value(0) {}
|
||||
|
||||
virtual void signal(id<MTLCommandBuffer> p_cb) override {
|
||||
if (p_cb) {
|
||||
value++;
|
||||
[p_cb encodeSignalEvent:event value:value];
|
||||
}
|
||||
}
|
||||
|
||||
virtual Error wait(uint32_t p_timeout_ms) override {
|
||||
GODOT_CLANG_WARNING_PUSH
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
|
||||
BOOL signaled = [event waitUntilSignaledValue:value timeoutMS:p_timeout_ms];
|
||||
GODOT_CLANG_WARNING_POP
|
||||
if (!signaled) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
ERR_PRINT("timeout waiting for fence");
|
||||
#endif
|
||||
return ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
};
|
||||
|
||||
struct FenceSemaphore : public Fence {
|
||||
dispatch_semaphore_t semaphore;
|
||||
FenceSemaphore() :
|
||||
semaphore(dispatch_semaphore_create(0)) {}
|
||||
|
||||
virtual void signal(id<MTLCommandBuffer> p_cb) override {
|
||||
if (p_cb) {
|
||||
[p_cb addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
} else {
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}
|
||||
}
|
||||
|
||||
virtual Error wait(uint32_t p_timeout_ms) override {
|
||||
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, static_cast<int64_t>(p_timeout_ms) * 1000000);
|
||||
long result = dispatch_semaphore_wait(semaphore, timeout);
|
||||
if (result != 0) {
|
||||
return ERR_TIMEOUT;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
virtual FenceID fence_create() override final;
|
||||
virtual Error fence_wait(FenceID p_fence) override final;
|
||||
virtual void fence_free(FenceID p_fence) override final;
|
||||
virtual FenceID fence_create() override = 0;
|
||||
virtual Error fence_wait(FenceID p_fence) override = 0;
|
||||
virtual void fence_free(FenceID p_fence) override = 0;
|
||||
|
||||
#pragma mark - Semaphores
|
||||
|
||||
public:
|
||||
virtual SemaphoreID semaphore_create() override final;
|
||||
virtual void semaphore_free(SemaphoreID p_semaphore) override final;
|
||||
virtual SemaphoreID semaphore_create() override = 0;
|
||||
virtual void semaphore_free(SemaphoreID p_semaphore) override = 0;
|
||||
|
||||
#pragma mark - Commands
|
||||
// ----- QUEUE FAMILY -----
|
||||
@@ -258,25 +274,22 @@ public:
|
||||
virtual CommandQueueFamilyID command_queue_family_get(BitField<CommandQueueFamilyBits> p_cmd_queue_family_bits, RenderingContextDriver::SurfaceID p_surface = 0) override final;
|
||||
|
||||
// ----- QUEUE -----
|
||||
|
||||
public:
|
||||
virtual CommandQueueID command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue = false) override final;
|
||||
virtual Error command_queue_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) override final;
|
||||
virtual void command_queue_free(CommandQueueID p_cmd_queue) override final;
|
||||
virtual CommandQueueID command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue = false) override = 0;
|
||||
virtual Error command_queue_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) override = 0;
|
||||
virtual void command_queue_free(CommandQueueID p_cmd_queue) override = 0;
|
||||
|
||||
// ----- POOL -----
|
||||
|
||||
virtual CommandPoolID command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) override final;
|
||||
virtual bool command_pool_reset(CommandPoolID p_cmd_pool) override final;
|
||||
virtual void command_pool_free(CommandPoolID p_cmd_pool) override final;
|
||||
virtual CommandPoolID command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) override = 0;
|
||||
virtual bool command_pool_reset(CommandPoolID p_cmd_pool) override = 0;
|
||||
virtual void command_pool_free(CommandPoolID p_cmd_pool) override = 0;
|
||||
|
||||
// ----- BUFFER -----
|
||||
|
||||
private:
|
||||
// Used to maintain references.
|
||||
Vector<MDCommandBuffer *> command_buffers;
|
||||
|
||||
public:
|
||||
virtual CommandBufferID command_buffer_create(CommandPoolID p_cmd_pool) override final;
|
||||
virtual CommandBufferID command_buffer_create(CommandPoolID p_cmd_pool) override = 0;
|
||||
virtual bool command_buffer_begin(CommandBufferID p_cmd_buffer) override final;
|
||||
virtual bool command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) override final;
|
||||
virtual void command_buffer_end(CommandBufferID p_cmd_buffer) override final;
|
||||
@@ -284,7 +297,7 @@ public:
|
||||
|
||||
#pragma mark - Swapchain
|
||||
|
||||
private:
|
||||
protected:
|
||||
struct SwapChain {
|
||||
RenderingContextDriver::SurfaceID surface = RenderingContextDriver::SurfaceID();
|
||||
RenderPassID render_pass;
|
||||
@@ -355,7 +368,7 @@ public:
|
||||
#pragma mark Pipeline
|
||||
|
||||
private:
|
||||
Result<id<MTLFunction>> _create_function(MDLibrary *p_library, NSString *p_name, VectorView<PipelineSpecializationConstant> &p_specialization_constants);
|
||||
Result<NS::SharedPtr<MTL::Function>> _create_function(MDLibrary *p_library, NS::String *p_name, VectorView<PipelineSpecializationConstant> &p_specialization_constants);
|
||||
|
||||
public:
|
||||
virtual void pipeline_free(PipelineID p_pipeline_id) override final;
|
||||
@@ -506,14 +519,13 @@ public:
|
||||
virtual const MultiviewCapabilities &get_multiview_capabilities() override final;
|
||||
virtual const FragmentShadingRateCapabilities &get_fragment_shading_rate_capabilities() override final;
|
||||
virtual const FragmentDensityMapCapabilities &get_fragment_density_map_capabilities() override final;
|
||||
virtual String get_api_name() const override final { return "Metal"; }
|
||||
virtual String get_api_version() const override final;
|
||||
virtual String get_pipeline_cache_uuid() const override final;
|
||||
virtual const Capabilities &get_capabilities() const override final;
|
||||
virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const override final;
|
||||
|
||||
// Metal-specific.
|
||||
id<MTLDevice> get_device() const { return device; }
|
||||
MTL::Device *get_device() const { return device; }
|
||||
PixelFormats &get_pixel_formats() const { return *pixel_formats; }
|
||||
MDResourceCache &get_resource_cache() const { return *resource_cache; }
|
||||
MetalDeviceProperties const &get_device_properties() const { return *device_properties; }
|
||||
@@ -523,7 +535,7 @@ public:
|
||||
}
|
||||
|
||||
size_t get_texel_buffer_alignment_for_format(RDD::DataFormat p_format) const;
|
||||
size_t get_texel_buffer_alignment_for_format(MTLPixelFormat p_format) const;
|
||||
size_t get_texel_buffer_alignment_for_format(MTL::PixelFormat p_format) const;
|
||||
|
||||
_FORCE_INLINE_ uint32_t frame_count() const { return _frame_count; }
|
||||
_FORCE_INLINE_ uint32_t frame_index() const { return _frame_index; }
|
||||
@@ -534,7 +546,7 @@ public:
|
||||
~RenderingDeviceDriverMetal();
|
||||
};
|
||||
|
||||
// Defined outside because we need to forward declare it in metal_objects.h
|
||||
// Defined outside because we need to forward declare it in metal3_objects.h
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalBufferDynamicInfo : public RenderingDeviceDriverMetal::BufferInfo {
|
||||
uint64_t size_bytes; // Contains the real buffer size / frame_count.
|
||||
uint32_t next_frame_index(uint32_t p_frame_count) {
|
||||
|
||||
369
drivers/metal/rendering_device_driver_metal3.cpp
Normal file
369
drivers/metal/rendering_device_driver_metal3.cpp
Normal file
@@ -0,0 +1,369 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_device_driver_metal3.cpp */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "rendering_device_driver_metal3.h"
|
||||
|
||||
#include "pixel_formats.h"
|
||||
#include "rendering_context_driver_metal.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
namespace MTL3 {
|
||||
|
||||
#pragma mark - FenceEvent / FenceSemaphore
|
||||
|
||||
void RenderingDeviceDriverMetal::FenceEvent::signal(MTL::CommandBuffer *p_cb) {
|
||||
if (p_cb) {
|
||||
value++;
|
||||
p_cb->encodeSignalEvent(event.get(), value);
|
||||
}
|
||||
}
|
||||
|
||||
Error RenderingDeviceDriverMetal::FenceEvent::wait(uint32_t p_timeout_ms) {
|
||||
bool signaled = event->waitUntilSignaledValue(value, p_timeout_ms);
|
||||
if (!signaled) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
ERR_PRINT("timeout waiting for fence");
|
||||
#endif
|
||||
return ERR_TIMEOUT;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void RenderingDeviceDriverMetal::FenceSemaphore::signal(MTL::CommandBuffer *p_cb) {
|
||||
if (p_cb) {
|
||||
p_cb->addCompletedHandler([this](MTL::CommandBuffer *) {
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
});
|
||||
} else {
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}
|
||||
}
|
||||
|
||||
Error RenderingDeviceDriverMetal::FenceSemaphore::wait(uint32_t p_timeout_ms) {
|
||||
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, static_cast<int64_t>(p_timeout_ms) * 1000000);
|
||||
long result = dispatch_semaphore_wait(semaphore, timeout);
|
||||
if (result != 0) {
|
||||
return ERR_TIMEOUT;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
#pragma mark - Constructor / Destructor
|
||||
|
||||
RenderingDeviceDriverMetal::RenderingDeviceDriverMetal(RenderingContextDriverMetal *p_context_driver) :
|
||||
::RenderingDeviceDriverMetal(p_context_driver) {
|
||||
}
|
||||
|
||||
RenderingDeviceDriverMetal::~RenderingDeviceDriverMetal() {
|
||||
for (MDCommandBuffer *cb : command_buffers) {
|
||||
memdelete(cb);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
Error RenderingDeviceDriverMetal::_create_device() {
|
||||
Error err = ::RenderingDeviceDriverMetal::_create_device();
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
|
||||
device_queue = NS::TransferPtr(device->newCommandQueue());
|
||||
ERR_FAIL_NULL_V(device_queue.get(), ERR_CANT_CREATE);
|
||||
device_queue->setLabel(MTLSTR("Godot Main Command Queue"));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error RenderingDeviceDriverMetal::initialize(uint32_t p_device_index, uint32_t p_frame_count) {
|
||||
Error err = _initialize(p_device_index, p_frame_count);
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
|
||||
// Barriers are still experimental in Metal 3, so they are disabled by default
|
||||
// and can only be enabled via an environment variable.
|
||||
bool barriers_enabled = OS::get_singleton()->get_environment("GODOT_MTL_FORCE_BARRIERS") == "1";
|
||||
if (__builtin_available(macos 26.0, ios 26.0, tvos 26.0, visionos 26.0, *)) {
|
||||
if (barriers_enabled) {
|
||||
print_line("Metal 3: Resource barriers enabled.");
|
||||
NS::SharedPtr<MTL::ResidencySetDescriptor> rs_desc = NS::TransferPtr(MTL::ResidencySetDescriptor::alloc()->init());
|
||||
rs_desc->setInitialCapacity(250);
|
||||
rs_desc->setLabel(MTLSTR("Main Residency Set"));
|
||||
NS::Error *error = nullptr;
|
||||
NS::SharedPtr<MTL::ResidencySet> mrs = NS::TransferPtr(device->newResidencySet(rs_desc.get(), &error));
|
||||
if (!mrs) {
|
||||
String error_msg = error ? String(error->localizedDescription()->utf8String()) : "Unknown error";
|
||||
print_error(vformat("Resource barriers unavailable. Failed to create main residency set for explicit resource barriers: %s", error_msg));
|
||||
} else {
|
||||
use_barriers = true;
|
||||
base_hazard_tracking = MTL::ResourceHazardTrackingModeUntracked;
|
||||
main_residency_set = mrs;
|
||||
device_queue->addResidencySet(mrs.get());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (barriers_enabled) {
|
||||
// Application or user has requested barriers, but the OS doesn't support them.
|
||||
print_verbose("Metal 3: Resource barriers are not supported on this OS version.");
|
||||
barriers_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#pragma mark - Residency
|
||||
|
||||
void RenderingDeviceDriverMetal::add_residency_set_to_main_queue(MTL::ResidencySet *p_set) {
|
||||
device_queue->addResidencySet(p_set);
|
||||
}
|
||||
|
||||
void RenderingDeviceDriverMetal::remove_residency_set_to_main_queue(MTL::ResidencySet *p_set) {
|
||||
device_queue->removeResidencySet(p_set);
|
||||
}
|
||||
|
||||
#pragma mark - Fences
|
||||
|
||||
RDD::FenceID RenderingDeviceDriverMetal::fence_create() {
|
||||
Fence *fence = memnew(FenceEvent(NS::TransferPtr(device->newSharedEvent())));
|
||||
return FenceID(fence);
|
||||
}
|
||||
|
||||
Error RenderingDeviceDriverMetal::fence_wait(FenceID p_fence) {
|
||||
Fence *fence = (Fence *)(p_fence.id);
|
||||
return fence->wait(1000);
|
||||
}
|
||||
|
||||
void RenderingDeviceDriverMetal::fence_free(FenceID p_fence) {
|
||||
Fence *fence = (Fence *)(p_fence.id);
|
||||
memdelete(fence);
|
||||
}
|
||||
|
||||
#pragma mark - Semaphores
|
||||
|
||||
RDD::SemaphoreID RenderingDeviceDriverMetal::semaphore_create() {
|
||||
if (use_barriers) {
|
||||
Semaphore *sem = memnew(Semaphore(NS::TransferPtr(device->newEvent())));
|
||||
return SemaphoreID(sem);
|
||||
}
|
||||
return SemaphoreID(1);
|
||||
}
|
||||
|
||||
void RenderingDeviceDriverMetal::semaphore_free(SemaphoreID p_semaphore) {
|
||||
if (use_barriers) {
|
||||
Semaphore *sem = (Semaphore *)(p_semaphore.id);
|
||||
memdelete(sem);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Command Queues
|
||||
|
||||
RDD::CommandQueueID RenderingDeviceDriverMetal::command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue) {
|
||||
return CommandQueueID(1);
|
||||
}
|
||||
|
||||
Error RenderingDeviceDriverMetal::_execute_and_present_barriers(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_sem, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_sem, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) {
|
||||
uint32_t size = p_cmd_buffers.size();
|
||||
if (size == 0) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
MTL::ResidencySet *mrs = main_residency_set.get();
|
||||
if (!_residency_add.is_empty()) {
|
||||
mrs->addAllocations(reinterpret_cast<const MTL::Allocation *const *>(_residency_add.ptr()), _residency_add.size());
|
||||
_residency_add.clear();
|
||||
changed = true;
|
||||
}
|
||||
if (!_residency_del.is_empty()) {
|
||||
mrs->removeAllocations(reinterpret_cast<const MTL::Allocation *const *>(_residency_del.ptr()), _residency_del.size());
|
||||
_residency_del.clear();
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
mrs->commit();
|
||||
}
|
||||
|
||||
if (p_wait_sem.size() > 0) {
|
||||
MTL::CommandBuffer *cb = device_queue->commandBuffer();
|
||||
#ifdef DEV_ENABLED
|
||||
cb->setLabel(MTLSTR("Wait Command Buffer"));
|
||||
#endif
|
||||
for (uint32_t i = 0; i < p_wait_sem.size(); i++) {
|
||||
Semaphore *sem = (Semaphore *)p_wait_sem[i].id;
|
||||
cb->encodeWait(sem->event.get(), sem->value);
|
||||
}
|
||||
cb->commit();
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < size - 1; i++) {
|
||||
MDCommandBuffer *cmd_buffer = (MDCommandBuffer *)(p_cmd_buffers[i].id);
|
||||
cmd_buffer->commit();
|
||||
}
|
||||
|
||||
// The last command buffer will signal the fence and semaphores.
|
||||
MDCommandBuffer *cmd_buffer = (MDCommandBuffer *)(p_cmd_buffers[size - 1].id);
|
||||
Fence *fence = (Fence *)(p_cmd_fence.id);
|
||||
if (fence != nullptr) {
|
||||
cmd_buffer->end();
|
||||
MTL::CommandBuffer *cb = cmd_buffer->get_command_buffer();
|
||||
fence->signal(cb);
|
||||
}
|
||||
|
||||
struct DrawRequest {
|
||||
NS::SharedPtr<MTL::Drawable> drawable;
|
||||
DisplayServer::VSyncMode vsync_mode;
|
||||
double duration;
|
||||
};
|
||||
|
||||
if (p_swap_chains.size() > 0) {
|
||||
Vector<DrawRequest> drawables;
|
||||
drawables.reserve(p_swap_chains.size());
|
||||
|
||||
for (uint32_t i = 0; i < p_swap_chains.size(); i++) {
|
||||
SwapChain *swap_chain = (SwapChain *)(p_swap_chains[i].id);
|
||||
RenderingContextDriverMetal::Surface *metal_surface = (RenderingContextDriverMetal::Surface *)(swap_chain->surface);
|
||||
MTL::Drawable *drawable = metal_surface->next_drawable();
|
||||
if (drawable) {
|
||||
drawables.push_back(DrawRequest{
|
||||
.drawable = NS::RetainPtr(drawable),
|
||||
.vsync_mode = metal_surface->vsync_mode,
|
||||
.duration = metal_surface->present_minimum_duration,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
MTL::CommandBuffer *cb = cmd_buffer->get_command_buffer();
|
||||
cb->addCompletedHandler([drawables = std::move(drawables)](MTL::CommandBuffer *) {
|
||||
for (const DrawRequest &dr : drawables) {
|
||||
switch (dr.vsync_mode) {
|
||||
case DisplayServer::VSYNC_DISABLED: {
|
||||
dr.drawable->present();
|
||||
} break;
|
||||
default: {
|
||||
dr.drawable->presentAfterMinimumDuration(dr.duration);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cmd_buffer->commit();
|
||||
|
||||
if (p_cmd_sem.size() > 0) {
|
||||
MTL::CommandBuffer *cb = device_queue->commandBuffer();
|
||||
for (uint32_t i = 0; i < p_cmd_sem.size(); i++) {
|
||||
Semaphore *sem = (Semaphore *)p_cmd_sem[i].id;
|
||||
sem->value++;
|
||||
cb->encodeSignalEvent(sem->event.get(), sem->value);
|
||||
}
|
||||
cb->commit();
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error RenderingDeviceDriverMetal::_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_sem, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_sem, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) {
|
||||
uint32_t size = p_cmd_buffers.size();
|
||||
if (size == 0) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < size - 1; i++) {
|
||||
MDCommandBuffer *cmd_buffer = (MDCommandBuffer *)(p_cmd_buffers[i].id);
|
||||
cmd_buffer->commit();
|
||||
}
|
||||
|
||||
// The last command buffer will signal the fence and semaphores.
|
||||
MDCommandBuffer *cmd_buffer = (MDCommandBuffer *)(p_cmd_buffers[size - 1].id);
|
||||
Fence *fence = (Fence *)(p_cmd_fence.id);
|
||||
if (fence != nullptr) {
|
||||
cmd_buffer->end();
|
||||
MTL::CommandBuffer *cb = cmd_buffer->get_command_buffer();
|
||||
fence->signal(cb);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < p_swap_chains.size(); i++) {
|
||||
SwapChain *swap_chain = (SwapChain *)(p_swap_chains[i].id);
|
||||
RenderingContextDriverMetal::Surface *metal_surface = (RenderingContextDriverMetal::Surface *)(swap_chain->surface);
|
||||
metal_surface->present(cmd_buffer);
|
||||
}
|
||||
|
||||
cmd_buffer->commit();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error RenderingDeviceDriverMetal::command_queue_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_sem, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_sem, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) {
|
||||
Error res;
|
||||
if (use_barriers) {
|
||||
res = _execute_and_present_barriers(p_cmd_queue, p_wait_sem, p_cmd_buffers, p_cmd_sem, p_cmd_fence, p_swap_chains);
|
||||
} else {
|
||||
res = _execute_and_present(p_cmd_queue, p_wait_sem, p_cmd_buffers, p_cmd_sem, p_cmd_fence, p_swap_chains);
|
||||
}
|
||||
ERR_FAIL_COND_V(res != OK, res);
|
||||
|
||||
if (p_swap_chains.size() > 0) {
|
||||
// Used as a signal that we're presenting, so this is the end of a frame.
|
||||
MTL::CaptureScope *scope = device_scope.get();
|
||||
scope->endScope();
|
||||
scope->beginScope();
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void RenderingDeviceDriverMetal::command_queue_free(CommandQueueID p_cmd_queue) {
|
||||
}
|
||||
|
||||
#pragma mark - Command Pools
|
||||
|
||||
RDD::CommandPoolID RenderingDeviceDriverMetal::command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) {
|
||||
DEV_ASSERT(p_cmd_buffer_type == COMMAND_BUFFER_TYPE_PRIMARY);
|
||||
return CommandPoolID(reinterpret_cast<uint64_t>(device_queue.get()));
|
||||
}
|
||||
|
||||
bool RenderingDeviceDriverMetal::command_pool_reset(CommandPoolID p_cmd_pool) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderingDeviceDriverMetal::command_pool_free(CommandPoolID p_cmd_pool) {
|
||||
// Nothing to free - the device_queue is managed by SharedPtr.
|
||||
}
|
||||
|
||||
#pragma mark - Command Buffers
|
||||
|
||||
RDD::CommandBufferID RenderingDeviceDriverMetal::command_buffer_create(CommandPoolID p_cmd_pool) {
|
||||
MTL::CommandQueue *queue = reinterpret_cast<MTL::CommandQueue *>(p_cmd_pool.id);
|
||||
MDCommandBuffer *obj = memnew(MDCommandBuffer(queue, this));
|
||||
command_buffers.push_back(obj);
|
||||
return CommandBufferID(obj);
|
||||
}
|
||||
|
||||
} // namespace MTL3
|
||||
115
drivers/metal/rendering_device_driver_metal3.h
Normal file
115
drivers/metal/rendering_device_driver_metal3.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_device_driver_metal3.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "metal3_objects.h"
|
||||
#include "rendering_device_driver_metal.h"
|
||||
|
||||
#include <Metal/Metal.hpp>
|
||||
|
||||
namespace MTL3 {
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) RenderingDeviceDriverMetal final : public ::RenderingDeviceDriverMetal {
|
||||
friend class MDCommandBuffer;
|
||||
#pragma mark - Generic
|
||||
|
||||
NS::SharedPtr<MTL::CommandQueue> device_queue;
|
||||
|
||||
struct Fence {
|
||||
virtual void signal(MTL::CommandBuffer *p_cmd_buffer) = 0;
|
||||
virtual Error wait(uint32_t p_timeout_ms) = 0;
|
||||
virtual ~Fence() = default;
|
||||
};
|
||||
|
||||
struct FenceEvent : Fence {
|
||||
NS::SharedPtr<MTL::SharedEvent> event;
|
||||
uint64_t value = 0;
|
||||
FenceEvent(NS::SharedPtr<MTL::SharedEvent> p_event) :
|
||||
event(p_event) {}
|
||||
void signal(MTL::CommandBuffer *p_cb) override;
|
||||
Error wait(uint32_t p_timeout_ms) override;
|
||||
};
|
||||
|
||||
struct FenceSemaphore : Fence {
|
||||
dispatch_semaphore_t semaphore;
|
||||
FenceSemaphore() :
|
||||
semaphore(dispatch_semaphore_create(0)) {}
|
||||
void signal(MTL::CommandBuffer *p_cb) override;
|
||||
Error wait(uint32_t p_timeout_ms) override;
|
||||
};
|
||||
|
||||
struct Semaphore {
|
||||
NS::SharedPtr<MTL::Event> event;
|
||||
uint64_t value = 0;
|
||||
Semaphore(NS::SharedPtr<MTL::Event> p_event) :
|
||||
event(p_event) {}
|
||||
};
|
||||
|
||||
Vector<MDCommandBuffer *> command_buffers;
|
||||
|
||||
Error _create_device() override;
|
||||
Error _execute_and_present_barriers(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains);
|
||||
Error _execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains);
|
||||
|
||||
protected:
|
||||
MTL::CommandQueue *get_command_queue() const override { return device_queue.get(); }
|
||||
void add_residency_set_to_main_queue(MTL::ResidencySet *p_set) override;
|
||||
void remove_residency_set_to_main_queue(MTL::ResidencySet *p_set) override;
|
||||
|
||||
public:
|
||||
Error initialize(uint32_t p_device_index, uint32_t p_frame_count) override;
|
||||
|
||||
FenceID fence_create() override;
|
||||
Error fence_wait(FenceID p_fence) override;
|
||||
void fence_free(FenceID p_fence) override;
|
||||
|
||||
SemaphoreID semaphore_create() override;
|
||||
void semaphore_free(SemaphoreID p_semaphore) override;
|
||||
|
||||
CommandQueueID command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue = false) override;
|
||||
Error command_queue_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) override;
|
||||
void command_queue_free(CommandQueueID p_cmd_queue) override;
|
||||
|
||||
CommandPoolID command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) override;
|
||||
bool command_pool_reset(CommandPoolID p_cmd_pool) override;
|
||||
void command_pool_free(CommandPoolID p_cmd_pool) override;
|
||||
|
||||
CommandBufferID command_buffer_create(CommandPoolID p_cmd_pool) override;
|
||||
|
||||
#pragma mark - Miscellaneous
|
||||
|
||||
String get_api_name() const override { return "Metal"; }
|
||||
|
||||
RenderingDeviceDriverMetal(RenderingContextDriverMetal *p_context_driver);
|
||||
~RenderingDeviceDriverMetal();
|
||||
};
|
||||
|
||||
} // namespace MTL3
|
||||
@@ -1,5 +1,5 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_shader_container_metal.mm */
|
||||
/* rendering_shader_container_metal.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
@@ -28,21 +28,21 @@
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "rendering_shader_container_metal.h"
|
||||
#include "rendering_shader_container_metal.h"
|
||||
|
||||
#import "metal_utils.h"
|
||||
#include "metal_utils.h"
|
||||
|
||||
#import "core/io/file_access.h"
|
||||
#import "core/io/marshalls.h"
|
||||
#import "core/templates/fixed_vector.h"
|
||||
#import "servers/rendering/rendering_device.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/templates/fixed_vector.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
|
||||
#include "thirdparty/spirv-reflect/spirv_reflect.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <spirv.hpp>
|
||||
#import <spirv_msl.hpp>
|
||||
#import <spirv_parser.hpp>
|
||||
#include <Metal/Metal.hpp>
|
||||
#include <spirv.hpp>
|
||||
#include <spirv_msl.hpp>
|
||||
#include <spirv_parser.hpp>
|
||||
|
||||
void RenderingShaderContainerMetal::_initialize_toolchain_properties() {
|
||||
if (compiler_props.is_valid()) {
|
||||
@@ -236,6 +236,74 @@ spv::ExecutionModel map_stage(RDD::ShaderStage p_stage) {
|
||||
return SHADER_STAGE_REMAP[p_stage];
|
||||
}
|
||||
|
||||
Error RenderingShaderContainerMetal::reflect_spirv(const ReflectShader &p_shader) {
|
||||
// const LocalVector<ReflectShaderStage> &p_spirv = p_shader.shader_stages;
|
||||
//
|
||||
// using ShaderStage = RenderingDeviceCommons::ShaderStage;
|
||||
//
|
||||
// const uint32_t spirv_size = p_spirv.size();
|
||||
//
|
||||
// HashSet<uint32_t> atomic_spirv_ids;
|
||||
// bool atomics_scanned = false;
|
||||
// auto scan_atomic_accesses = [&atomic_spirv_ids, &p_spirv, spirv_size, &atomics_scanned]() {
|
||||
// if (atomics_scanned) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// for (uint32_t i = 0; i < spirv_size + 0; i++) {
|
||||
// const uint32_t STARTING_WORD_INDEX = 5;
|
||||
// Span<uint32_t> spirv = p_spirv[i].spirv();
|
||||
// const uint32_t *words = spirv.ptr() + STARTING_WORD_INDEX;
|
||||
// while (words < spirv.end()) {
|
||||
// uint32_t instruction = *words;
|
||||
// uint16_t word_count = instruction >> 16;
|
||||
// SpvOp opcode = (SpvOp)(instruction & 0xFFFF);
|
||||
// if (opcode == SpvOpImageTexelPointer) {
|
||||
// uint32_t image_var_id = words[3];
|
||||
// atomic_spirv_ids.insert(image_var_id);
|
||||
// }
|
||||
// words += word_count;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// atomics_scanned = true;
|
||||
// };
|
||||
//
|
||||
// for (uint32_t i = 0; i < spirv_size + 0; i++) {
|
||||
// ShaderStage stage = p_spirv[i].shader_stage;
|
||||
// ShaderStage stage_flag = (ShaderStage)(1 << p_spirv[i].shader_stage);
|
||||
// SpvReflectResult result;
|
||||
//
|
||||
// const SpvReflectShaderModule &module = p_spirv[i].module();
|
||||
//
|
||||
// uint32_t binding_count = 0;
|
||||
// result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, nullptr);
|
||||
// CRASH_COND(result != SPV_REFLECT_RESULT_SUCCESS);
|
||||
//
|
||||
// if (binding_count > 0) {
|
||||
// LocalVector<SpvReflectDescriptorBinding *> bindings;
|
||||
// bindings.resize_uninitialized(binding_count);
|
||||
// result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, bindings.ptr());
|
||||
//
|
||||
// for (uint32_t j = 0; j < binding_count; j++) {
|
||||
// const SpvReflectDescriptorBinding &binding = *bindings[j];
|
||||
//
|
||||
// switch (binding.descriptor_type) {
|
||||
// case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
// case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
// case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
// case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RenderingShaderContainerMetal::_set_code_from_spirv(const ReflectShader &p_shader) {
|
||||
using namespace spirv_cross;
|
||||
using spirv_cross::CompilerMSL;
|
||||
@@ -282,12 +350,7 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const ReflectShader &p_
|
||||
msl_options.ios_support_base_vertex_instance = true;
|
||||
}
|
||||
|
||||
// We don't currently allow argument buffers when using dynamic buffers as
|
||||
// the current implementation does not update the argument buffer each time
|
||||
// the dynamic buffer changes. This is a future TODO.
|
||||
bool argument_buffers_allowed = get_shader_reflection().has_dynamic_buffers == false;
|
||||
|
||||
if (device_profile->features.use_argument_buffers && argument_buffers_allowed) {
|
||||
if (device_profile->features.use_argument_buffers) {
|
||||
msl_options.argument_buffers_tier = CompilerMSL::Options::ArgumentBuffersTier::Tier2;
|
||||
msl_options.argument_buffers = true;
|
||||
mtl_reflection_data.set_uses_argument_buffers(true);
|
||||
@@ -384,9 +447,9 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const ReflectShader &p_
|
||||
case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: {
|
||||
if (!(binding.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE)) {
|
||||
if (!(binding.decoration_flags & SPV_REFLECT_DECORATION_NON_READABLE)) {
|
||||
found->access = MTLBindingAccessReadWrite;
|
||||
found->access = MTL::BindingAccessReadWrite;
|
||||
} else {
|
||||
found->access = MTLBindingAccessWriteOnly;
|
||||
found->access = MTL::BindingAccessWriteOnly;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
@@ -394,9 +457,9 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const ReflectShader &p_
|
||||
case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: {
|
||||
if (!(binding.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE) && !(binding.block.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE)) {
|
||||
if (!(binding.decoration_flags & SPV_REFLECT_DECORATION_NON_READABLE) && !(binding.block.decoration_flags & SPV_REFLECT_DECORATION_NON_READABLE)) {
|
||||
found->access = MTLBindingAccessReadWrite;
|
||||
found->access = MTL::BindingAccessReadWrite;
|
||||
} else {
|
||||
found->access = MTLBindingAccessWriteOnly;
|
||||
found->access = MTL::BindingAccessWriteOnly;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
@@ -405,14 +468,14 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const ReflectShader &p_
|
||||
}
|
||||
|
||||
switch (found->access) {
|
||||
case MTLBindingAccessReadOnly:
|
||||
found->usage = MTLResourceUsageRead;
|
||||
case MTL::BindingAccessReadOnly:
|
||||
found->usage = MTL::ResourceUsageRead;
|
||||
break;
|
||||
case MTLBindingAccessWriteOnly:
|
||||
found->usage = MTLResourceUsageWrite;
|
||||
case MTL::BindingAccessWriteOnly:
|
||||
found->usage = MTL::ResourceUsageWrite;
|
||||
break;
|
||||
case MTLBindingAccessReadWrite:
|
||||
found->usage = MTLResourceUsageRead | MTLResourceUsageWrite;
|
||||
case MTL::BindingAccessReadWrite:
|
||||
found->usage = MTL::ResourceUsageRead | MTL::ResourceUsageWrite;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -424,7 +487,7 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const ReflectShader &p_
|
||||
|
||||
switch (type) {
|
||||
case RDC::UNIFORM_TYPE_SAMPLER: {
|
||||
found->data_type = MTLDataTypeSampler;
|
||||
found->data_type = MTL::DataTypeSampler;
|
||||
found->get_indexes(UniformData::IndexType::SLOT).sampler = next_index(Sampler, binding_stride);
|
||||
found->get_indexes(UniformData::IndexType::ARG).sampler = next_arg_index(binding_stride);
|
||||
|
||||
@@ -433,7 +496,7 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const ReflectShader &p_
|
||||
} break;
|
||||
case RDC::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE:
|
||||
case RDC::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER: {
|
||||
found->data_type = MTLDataTypeTexture;
|
||||
found->data_type = MTL::DataTypeTexture;
|
||||
found->get_indexes(UniformData::IndexType::SLOT).texture = next_index(Texture, binding_stride);
|
||||
found->get_indexes(UniformData::IndexType::SLOT).sampler = next_index(Sampler, binding_stride);
|
||||
found->get_indexes(UniformData::IndexType::ARG).texture = next_arg_index(binding_stride);
|
||||
@@ -443,7 +506,7 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const ReflectShader &p_
|
||||
case RDC::UNIFORM_TYPE_TEXTURE:
|
||||
case RDC::UNIFORM_TYPE_IMAGE:
|
||||
case RDC::UNIFORM_TYPE_TEXTURE_BUFFER: {
|
||||
found->data_type = MTLDataTypeTexture;
|
||||
found->data_type = MTL::DataTypeTexture;
|
||||
found->get_indexes(UniformData::IndexType::SLOT).texture = next_index(Texture, binding_stride);
|
||||
found->get_indexes(UniformData::IndexType::ARG).texture = next_arg_index(binding_stride);
|
||||
rb.basetype = SPIRType::BaseType::Image;
|
||||
@@ -455,13 +518,13 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const ReflectShader &p_
|
||||
case RDC::UNIFORM_TYPE_STORAGE_BUFFER_DYNAMIC:
|
||||
case RDC::UNIFORM_TYPE_UNIFORM_BUFFER:
|
||||
case RDC::UNIFORM_TYPE_STORAGE_BUFFER: {
|
||||
found->data_type = MTLDataTypePointer;
|
||||
found->data_type = MTL::DataTypePointer;
|
||||
found->get_indexes(UniformData::IndexType::SLOT).buffer = next_index(Buffer, binding_stride);
|
||||
found->get_indexes(UniformData::IndexType::ARG).buffer = next_arg_index(binding_stride);
|
||||
rb.basetype = SPIRType::BaseType::Void;
|
||||
} break;
|
||||
case RDC::UNIFORM_TYPE_INPUT_ATTACHMENT: {
|
||||
found->data_type = MTLDataTypeTexture;
|
||||
found->data_type = MTL::DataTypeTexture;
|
||||
found->get_indexes(UniformData::IndexType::SLOT).texture = next_index(Texture, binding_stride);
|
||||
found->get_indexes(UniformData::IndexType::ARG).texture = next_arg_index(binding_stride);
|
||||
rb.basetype = SPIRType::BaseType::Image;
|
||||
@@ -476,44 +539,53 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const ReflectShader &p_
|
||||
rb.msl_texture = found->get_indexes(shader_index_type).texture;
|
||||
rb.msl_sampler = found->get_indexes(shader_index_type).sampler;
|
||||
|
||||
if (found->data_type == MTLDataTypeTexture) {
|
||||
if (found->data_type == MTL::DataTypeTexture) {
|
||||
const SpvReflectImageTraits &image = uniform.get_spv_reflect().image;
|
||||
|
||||
switch (image.dim) {
|
||||
case SpvDim1D: {
|
||||
if (image.arrayed) {
|
||||
found->texture_type = MTLTextureType1DArray;
|
||||
found->texture_type = MTL::TextureType1DArray;
|
||||
} else {
|
||||
found->texture_type = MTLTextureType1D;
|
||||
found->texture_type = MTL::TextureType1D;
|
||||
}
|
||||
} break;
|
||||
case SpvDimSubpassData:
|
||||
case SpvDim2D: {
|
||||
if (image.arrayed && image.ms) {
|
||||
found->texture_type = MTLTextureType2DMultisampleArray;
|
||||
found->texture_type = MTL::TextureType2DMultisampleArray;
|
||||
} else if (image.arrayed) {
|
||||
found->texture_type = MTLTextureType2DArray;
|
||||
found->texture_type = MTL::TextureType2DArray;
|
||||
} else if (image.ms) {
|
||||
found->texture_type = MTLTextureType2DMultisample;
|
||||
found->texture_type = MTL::TextureType2DMultisample;
|
||||
} else {
|
||||
found->texture_type = MTLTextureType2D;
|
||||
found->texture_type = MTL::TextureType2D;
|
||||
}
|
||||
} break;
|
||||
case SpvDim3D: {
|
||||
found->texture_type = MTLTextureType3D;
|
||||
found->texture_type = MTL::TextureType3D;
|
||||
} break;
|
||||
case SpvDimCube: {
|
||||
if (image.arrayed) {
|
||||
found->texture_type = MTLTextureTypeCubeArray;
|
||||
found->texture_type = MTL::TextureTypeCubeArray;
|
||||
} else {
|
||||
found->texture_type = MTLTextureTypeCube;
|
||||
found->texture_type = MTL::TextureTypeCube;
|
||||
}
|
||||
} break;
|
||||
case SpvDimRect: {
|
||||
// Ignored.
|
||||
} break;
|
||||
case SpvDimBuffer: {
|
||||
found->texture_type = MTLTextureTypeTextureBuffer;
|
||||
found->texture_type = MTL::TextureTypeTextureBuffer;
|
||||
// If this is used with atomics, we need to use a read-write texture.
|
||||
// scan_atomic_accesses();
|
||||
// if (atomic_spirv_ids.find(uniform.spirv_id) != atomic_spirv_ids.end()) {
|
||||
// rb.access = MTLBindingAccessReadWrite;
|
||||
// found->access = MTLBindingAccessReadWrite;
|
||||
// } else {
|
||||
// rb.access = MTLBindingAccessReadOnly;
|
||||
// found->access = MTLBindingAccessReadOnly;
|
||||
// }
|
||||
} break;
|
||||
case SpvDimTileImageDataEXT: {
|
||||
// Godot does not use this extension.
|
||||
@@ -30,11 +30,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#import "metal_device_profile.h"
|
||||
#import "sha256_digest.h"
|
||||
#include "metal_device_profile.h"
|
||||
#include "sha256_digest.h"
|
||||
|
||||
#import "servers/rendering/rendering_device_driver.h"
|
||||
#import "servers/rendering/rendering_shader_container.h"
|
||||
#include "servers/rendering/rendering_device_driver.h"
|
||||
#include "servers/rendering/rendering_shader_container.h"
|
||||
|
||||
constexpr uint32_t R32UI_ALIGNMENT_CONSTANT_ID = 65535;
|
||||
/// Metal buffer index for the view mask when rendering multi-view.
|
||||
@@ -177,6 +177,8 @@ private:
|
||||
|
||||
Error compile_metal_source(const char *p_source, const StageData &p_stage_data, Vector<uint8_t> &r_binary_data);
|
||||
|
||||
Error reflect_spirv(const ReflectShader &p_shader);
|
||||
|
||||
public:
|
||||
static constexpr uint32_t FORMAT_VERSION = 2;
|
||||
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
#import <simd/simd.h>
|
||||
#import <zlib.h>
|
||||
#include <CommonCrypto/CommonDigest.h>
|
||||
#include <simd/simd.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "core/templates/hashfuncs.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
|
||||
@@ -160,7 +160,7 @@ DisplayServerEmbedded::DisplayServerEmbedded(const String &p_rendering_driver, W
|
||||
#endif
|
||||
#ifdef METAL_ENABLED
|
||||
if (rendering_driver == "metal") {
|
||||
wpd.metal.layer = (CAMetalLayer *)layer;
|
||||
wpd.metal.layer = (__bridge CA::MetalLayer *)layer;
|
||||
}
|
||||
#endif
|
||||
Error err = rendering_context->window_create(window_id_counter, &wpd);
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
#endif
|
||||
|
||||
#if defined(ACCESSKIT_ENABLED)
|
||||
@@ -185,7 +186,7 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod
|
||||
#endif
|
||||
#ifdef METAL_ENABLED
|
||||
if (rendering_driver == "metal") {
|
||||
wpd.metal.layer = (CAMetalLayer *)layer;
|
||||
wpd.metal.layer = (__bridge CA::MetalLayer *)layer;
|
||||
}
|
||||
#endif
|
||||
Error err = rendering_context->window_create(window_id_counter, &wpd);
|
||||
|
||||
@@ -7,6 +7,10 @@ Import("env")
|
||||
|
||||
env_effects = env.Clone()
|
||||
|
||||
# metal-cpp headers for Metal FX
|
||||
if env["metal"]:
|
||||
env_effects.Prepend(CPPPATH=["#thirdparty/metal-cpp"])
|
||||
|
||||
# Thirdparty source files
|
||||
|
||||
thirdparty_obj = []
|
||||
@@ -69,8 +73,6 @@ env.servers_sources += thirdparty_obj
|
||||
module_obj = []
|
||||
|
||||
env_effects.add_source_files(module_obj, "*.cpp")
|
||||
if env["metal"]:
|
||||
env_effects.add_source_files(module_obj, "metal_fx.mm")
|
||||
env.servers_sources += module_obj
|
||||
|
||||
# Needed to force rebuilding the module files when the thirdparty library is updated.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**************************************************************************/
|
||||
/* metal_fx.mm */
|
||||
/* metal_fx.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
@@ -28,20 +28,24 @@
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "metal_fx.h"
|
||||
#ifdef METAL_ENABLED
|
||||
|
||||
#import "../storage_rd/render_scene_buffers_rd.h"
|
||||
#import "drivers/metal/pixel_formats.h"
|
||||
#import "drivers/metal/rendering_device_driver_metal.h"
|
||||
#include "metal_fx.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <MetalFX/MetalFX.h>
|
||||
#include "../storage_rd/render_scene_buffers_rd.h"
|
||||
#include "drivers/metal/pixel_formats.h"
|
||||
#include "drivers/metal/rendering_device_driver_metal3.h"
|
||||
|
||||
#include <MetalFX/MetalFX.hpp>
|
||||
|
||||
using namespace RendererRD;
|
||||
|
||||
#pragma mark - Spatial Scaler
|
||||
|
||||
MFXSpatialContext::~MFXSpatialContext() {
|
||||
if (scaler) {
|
||||
scaler->release();
|
||||
}
|
||||
}
|
||||
|
||||
MFXSpatialEffect::MFXSpatialEffect() {
|
||||
@@ -51,28 +55,21 @@ MFXSpatialEffect::~MFXSpatialEffect() {
|
||||
}
|
||||
|
||||
void MFXSpatialEffect::callback(RDD *p_driver, RDD::CommandBufferID p_command_buffer, CallbackArgs *p_userdata) {
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
|
||||
|
||||
MDCommandBuffer *obj = (MDCommandBuffer *)(p_command_buffer.id);
|
||||
MDCommandBufferBase *obj = (MDCommandBufferBase *)(p_command_buffer.id);
|
||||
obj->end();
|
||||
|
||||
id<MTLTexture> src_texture = rid::get(p_userdata->src);
|
||||
id<MTLTexture> dst_texture = rid::get(p_userdata->dst);
|
||||
MTL::Texture *src_texture = reinterpret_cast<MTL::Texture *>(p_userdata->src.id);
|
||||
MTL::Texture *dst_texture = reinterpret_cast<MTL::Texture *>(p_userdata->dst.id);
|
||||
|
||||
__block id<MTLFXSpatialScaler> scaler = p_userdata->ctx.scaler;
|
||||
scaler.colorTexture = src_texture;
|
||||
scaler.outputTexture = dst_texture;
|
||||
[scaler encodeToCommandBuffer:obj->get_command_buffer()];
|
||||
// TODO(sgc): add API to retain objects until the command buffer completes
|
||||
[obj->get_command_buffer() addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
|
||||
// This block retains a reference to the scaler until the command buffer.
|
||||
// completes.
|
||||
scaler = nil;
|
||||
}];
|
||||
MTLFX::SpatialScalerBase *scaler = p_userdata->scaler;
|
||||
scaler->setColorTexture(src_texture);
|
||||
scaler->setOutputTexture(dst_texture);
|
||||
MTLFX::SpatialScaler *s = static_cast<MTLFX::SpatialScaler *>(scaler);
|
||||
MTL3::MDCommandBuffer *cmd = (MTL3::MDCommandBuffer *)(p_command_buffer.id);
|
||||
s->encodeToCommandBuffer(cmd->get_command_buffer());
|
||||
obj->retain_resource(scaler);
|
||||
|
||||
CallbackArgs::free(&p_userdata);
|
||||
|
||||
GODOT_CLANG_WARNING_POP
|
||||
}
|
||||
|
||||
void MFXSpatialEffect::ensure_context(Ref<RenderSceneBuffersRD> p_render_buffers) {
|
||||
@@ -98,27 +95,23 @@ void MFXSpatialEffect::process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p
|
||||
MFXSpatialContext *MFXSpatialEffect::create_context(CreateParams p_params) const {
|
||||
DEV_ASSERT(RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_SPATIAL));
|
||||
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
|
||||
|
||||
RenderingDeviceDriverMetal *rdd = (RenderingDeviceDriverMetal *)RD::get_singleton()->get_device_driver();
|
||||
PixelFormats &pf = rdd->get_pixel_formats();
|
||||
id<MTLDevice> dev = rdd->get_device();
|
||||
MTL::Device *dev = rdd->get_device();
|
||||
|
||||
MTLFXSpatialScalerDescriptor *desc = [MTLFXSpatialScalerDescriptor new];
|
||||
desc.inputWidth = (NSUInteger)p_params.input_size.width;
|
||||
desc.inputHeight = (NSUInteger)p_params.input_size.height;
|
||||
NS::SharedPtr<MTLFX::SpatialScalerDescriptor> desc = NS::TransferPtr(MTLFX::SpatialScalerDescriptor::alloc()->init());
|
||||
desc->setInputWidth((NS::UInteger)p_params.input_size.width);
|
||||
desc->setInputHeight((NS::UInteger)p_params.input_size.height);
|
||||
|
||||
desc.outputWidth = (NSUInteger)p_params.output_size.width;
|
||||
desc.outputHeight = (NSUInteger)p_params.output_size.height;
|
||||
desc->setOutputWidth((NS::UInteger)p_params.output_size.width);
|
||||
desc->setOutputHeight((NS::UInteger)p_params.output_size.height);
|
||||
|
||||
desc->setColorTextureFormat((MTL::PixelFormat)pf.getMTLPixelFormat(p_params.input_format));
|
||||
desc->setOutputTextureFormat((MTL::PixelFormat)pf.getMTLPixelFormat(p_params.output_format));
|
||||
desc->setColorProcessingMode(MTLFX::SpatialScalerColorProcessingModeLinear);
|
||||
|
||||
desc.colorTextureFormat = pf.getMTLPixelFormat(p_params.input_format);
|
||||
desc.outputTextureFormat = pf.getMTLPixelFormat(p_params.output_format);
|
||||
desc.colorProcessingMode = MTLFXSpatialScalerColorProcessingModeLinear;
|
||||
id<MTLFXSpatialScaler> scaler = [desc newSpatialScalerWithDevice:dev];
|
||||
MFXSpatialContext *context = memnew(MFXSpatialContext);
|
||||
context->scaler = scaler;
|
||||
|
||||
GODOT_CLANG_WARNING_POP
|
||||
context->scaler = desc->newSpatialScaler(dev);
|
||||
|
||||
return context;
|
||||
}
|
||||
@@ -127,7 +120,11 @@ MFXSpatialContext *MFXSpatialEffect::create_context(CreateParams p_params) const
|
||||
|
||||
#pragma mark - Temporal Scaler
|
||||
|
||||
MFXTemporalContext::~MFXTemporalContext() {}
|
||||
MFXTemporalContext::~MFXTemporalContext() {
|
||||
if (scaler) {
|
||||
scaler->release();
|
||||
}
|
||||
}
|
||||
|
||||
MFXTemporalEffect::MFXTemporalEffect() {}
|
||||
MFXTemporalEffect::~MFXTemporalEffect() {}
|
||||
@@ -135,35 +132,29 @@ MFXTemporalEffect::~MFXTemporalEffect() {}
|
||||
MFXTemporalContext *MFXTemporalEffect::create_context(CreateParams p_params) const {
|
||||
DEV_ASSERT(RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_TEMPORAL));
|
||||
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
|
||||
|
||||
RenderingDeviceDriverMetal *rdd = (RenderingDeviceDriverMetal *)RD::get_singleton()->get_device_driver();
|
||||
PixelFormats &pf = rdd->get_pixel_formats();
|
||||
id<MTLDevice> dev = rdd->get_device();
|
||||
MTL::Device *dev = rdd->get_device();
|
||||
|
||||
MTLFXTemporalScalerDescriptor *desc = [MTLFXTemporalScalerDescriptor new];
|
||||
desc.inputWidth = (NSUInteger)p_params.input_size.width;
|
||||
desc.inputHeight = (NSUInteger)p_params.input_size.height;
|
||||
NS::SharedPtr<MTLFX::TemporalScalerDescriptor> desc = NS::TransferPtr(MTLFX::TemporalScalerDescriptor::alloc()->init());
|
||||
desc->setInputWidth((NS::UInteger)p_params.input_size.width);
|
||||
desc->setInputHeight((NS::UInteger)p_params.input_size.height);
|
||||
|
||||
desc.outputWidth = (NSUInteger)p_params.output_size.width;
|
||||
desc.outputHeight = (NSUInteger)p_params.output_size.height;
|
||||
desc->setOutputWidth((NS::UInteger)p_params.output_size.width);
|
||||
desc->setOutputHeight((NS::UInteger)p_params.output_size.height);
|
||||
|
||||
desc.colorTextureFormat = pf.getMTLPixelFormat(p_params.input_format);
|
||||
desc.depthTextureFormat = pf.getMTLPixelFormat(p_params.depth_format);
|
||||
desc.motionTextureFormat = pf.getMTLPixelFormat(p_params.motion_format);
|
||||
desc.autoExposureEnabled = NO;
|
||||
desc->setColorTextureFormat((MTL::PixelFormat)pf.getMTLPixelFormat(p_params.input_format));
|
||||
desc->setDepthTextureFormat((MTL::PixelFormat)pf.getMTLPixelFormat(p_params.depth_format));
|
||||
desc->setMotionTextureFormat((MTL::PixelFormat)pf.getMTLPixelFormat(p_params.motion_format));
|
||||
desc->setAutoExposureEnabled(false);
|
||||
|
||||
desc.outputTextureFormat = pf.getMTLPixelFormat(p_params.output_format);
|
||||
desc->setOutputTextureFormat((MTL::PixelFormat)pf.getMTLPixelFormat(p_params.output_format));
|
||||
|
||||
id<MTLFXTemporalScaler> scaler = [desc newTemporalScalerWithDevice:dev];
|
||||
MFXTemporalContext *context = memnew(MFXTemporalContext);
|
||||
context->scaler = scaler;
|
||||
|
||||
scaler.motionVectorScaleX = p_params.motion_vector_scale.x;
|
||||
scaler.motionVectorScaleY = p_params.motion_vector_scale.y;
|
||||
scaler.depthReversed = true; // Godot uses reverse Z per https://github.com/godotengine/godot/pull/88328
|
||||
|
||||
GODOT_CLANG_WARNING_POP
|
||||
context->scaler = desc->newTemporalScaler(dev);
|
||||
context->scaler->setMotionVectorScaleX(p_params.motion_vector_scale.x);
|
||||
context->scaler->setMotionVectorScaleY(p_params.motion_vector_scale.y);
|
||||
context->scaler->setDepthReversed(true); // Godot uses reverse Z per https://github.com/godotengine/godot/pull/88328
|
||||
|
||||
return context;
|
||||
}
|
||||
@@ -188,38 +179,33 @@ void MFXTemporalEffect::process(RendererRD::MFXTemporalContext *p_ctx, RendererR
|
||||
}
|
||||
|
||||
void MFXTemporalEffect::callback(RDD *p_driver, RDD::CommandBufferID p_command_buffer, CallbackArgs *p_userdata) {
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
|
||||
|
||||
MDCommandBuffer *obj = (MDCommandBuffer *)(p_command_buffer.id);
|
||||
MDCommandBufferBase *obj = (MDCommandBufferBase *)(p_command_buffer.id);
|
||||
obj->end();
|
||||
|
||||
id<MTLTexture> src_texture = rid::get(p_userdata->src);
|
||||
id<MTLTexture> depth = rid::get(p_userdata->depth);
|
||||
id<MTLTexture> motion = rid::get(p_userdata->motion);
|
||||
id<MTLTexture> exposure = rid::get(p_userdata->exposure);
|
||||
MTL::Texture *src_texture = reinterpret_cast<MTL::Texture *>(p_userdata->src.id);
|
||||
MTL::Texture *depth = reinterpret_cast<MTL::Texture *>(p_userdata->depth.id);
|
||||
MTL::Texture *motion = reinterpret_cast<MTL::Texture *>(p_userdata->motion.id);
|
||||
MTL::Texture *exposure = reinterpret_cast<MTL::Texture *>(p_userdata->exposure.id);
|
||||
|
||||
id<MTLTexture> dst_texture = rid::get(p_userdata->dst);
|
||||
MTL::Texture *dst_texture = reinterpret_cast<MTL::Texture *>(p_userdata->dst.id);
|
||||
|
||||
__block id<MTLFXTemporalScaler> scaler = p_userdata->ctx.scaler;
|
||||
scaler.reset = p_userdata->reset;
|
||||
scaler.colorTexture = src_texture;
|
||||
scaler.depthTexture = depth;
|
||||
scaler.motionTexture = motion;
|
||||
scaler.exposureTexture = exposure;
|
||||
scaler.jitterOffsetX = p_userdata->jitter_offset.x;
|
||||
scaler.jitterOffsetY = p_userdata->jitter_offset.y;
|
||||
scaler.outputTexture = dst_texture;
|
||||
[scaler encodeToCommandBuffer:obj->get_command_buffer()];
|
||||
// TODO(sgc): add API to retain objects until the command buffer completes
|
||||
[obj->get_command_buffer() addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
|
||||
// This block retains a reference to the scaler until the command buffer.
|
||||
// completes.
|
||||
scaler = nil;
|
||||
}];
|
||||
MTLFX::TemporalScalerBase *scaler = p_userdata->scaler;
|
||||
scaler->setReset(p_userdata->reset);
|
||||
scaler->setColorTexture(src_texture);
|
||||
scaler->setDepthTexture(depth);
|
||||
scaler->setMotionTexture(motion);
|
||||
scaler->setExposureTexture(exposure);
|
||||
scaler->setJitterOffsetX(p_userdata->jitter_offset.x);
|
||||
scaler->setJitterOffsetY(p_userdata->jitter_offset.y);
|
||||
scaler->setOutputTexture(dst_texture);
|
||||
MTLFX::TemporalScaler *s = static_cast<MTLFX::TemporalScaler *>(scaler);
|
||||
MTL3::MDCommandBuffer *cmd = (MTL3::MDCommandBuffer *)(p_command_buffer.id);
|
||||
s->encodeToCommandBuffer(cmd->get_command_buffer());
|
||||
obj->retain_resource(scaler);
|
||||
|
||||
CallbackArgs::free(&p_userdata);
|
||||
|
||||
GODOT_CLANG_WARNING_POP
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -41,32 +41,28 @@
|
||||
#include "core/templates/paged_allocator.h"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
|
||||
#ifdef __OBJC__
|
||||
@protocol MTLFXSpatialScaler;
|
||||
@protocol MTLFXTemporalScaler;
|
||||
#endif
|
||||
namespace MTLFX {
|
||||
class SpatialScalerBase;
|
||||
class TemporalScalerBase;
|
||||
} //namespace MTLFX
|
||||
|
||||
namespace RendererRD {
|
||||
|
||||
struct MFXSpatialContext {
|
||||
#ifdef __OBJC__
|
||||
id<MTLFXSpatialScaler> scaler = nullptr;
|
||||
#else
|
||||
void *scaler = nullptr;
|
||||
#endif
|
||||
MTLFX::SpatialScalerBase *scaler = nullptr;
|
||||
MFXSpatialContext() = default;
|
||||
~MFXSpatialContext();
|
||||
};
|
||||
|
||||
class MFXSpatialEffect : public SpatialUpscaler {
|
||||
struct CallbackArgs {
|
||||
MFXSpatialEffect *owner;
|
||||
MFXSpatialEffect *owner = nullptr;
|
||||
MTLFX::SpatialScalerBase *scaler = nullptr;
|
||||
RDD::TextureID src;
|
||||
RDD::TextureID dst;
|
||||
MFXSpatialContext ctx;
|
||||
|
||||
CallbackArgs(MFXSpatialEffect *p_owner, RDD::TextureID p_src, RDD::TextureID p_dst, MFXSpatialContext p_ctx) :
|
||||
owner(p_owner), src(p_src), dst(p_dst), ctx(p_ctx) {}
|
||||
CallbackArgs(MFXSpatialEffect *p_owner, RDD::TextureID p_src, RDD::TextureID p_dst, const MFXSpatialContext &p_ctx) :
|
||||
owner(p_owner), scaler(p_ctx.scaler), src(p_src), dst(p_dst) {}
|
||||
|
||||
static void free(CallbackArgs **p_args) {
|
||||
(*p_args)->owner->args_allocator.free(*p_args);
|
||||
@@ -98,25 +94,21 @@ public:
|
||||
#ifdef METAL_MFXTEMPORAL_ENABLED
|
||||
|
||||
struct MFXTemporalContext {
|
||||
#ifdef __OBJC__
|
||||
id<MTLFXTemporalScaler> scaler = nullptr;
|
||||
#else
|
||||
void *scaler = nullptr;
|
||||
#endif
|
||||
MTLFX::TemporalScalerBase *scaler = nullptr;
|
||||
MFXTemporalContext() = default;
|
||||
~MFXTemporalContext();
|
||||
};
|
||||
|
||||
class MFXTemporalEffect {
|
||||
struct CallbackArgs {
|
||||
MFXTemporalEffect *owner;
|
||||
MFXTemporalEffect *owner = nullptr;
|
||||
MTLFX::TemporalScalerBase *scaler = nullptr;
|
||||
RDD::TextureID src;
|
||||
RDD::TextureID depth;
|
||||
RDD::TextureID motion;
|
||||
RDD::TextureID exposure;
|
||||
Vector2 jitter_offset;
|
||||
RDD::TextureID dst;
|
||||
MFXTemporalContext ctx;
|
||||
bool reset = false;
|
||||
|
||||
CallbackArgs(
|
||||
@@ -127,16 +119,16 @@ class MFXTemporalEffect {
|
||||
RDD::TextureID p_exposure,
|
||||
Vector2 p_jitter_offset,
|
||||
RDD::TextureID p_dst,
|
||||
MFXTemporalContext p_ctx,
|
||||
const MFXTemporalContext &p_ctx,
|
||||
bool p_reset) :
|
||||
owner(p_owner),
|
||||
scaler(p_ctx.scaler),
|
||||
src(p_src),
|
||||
depth(p_depth),
|
||||
motion(p_motion),
|
||||
exposure(p_exposure),
|
||||
jitter_offset(p_jitter_offset),
|
||||
dst(p_dst),
|
||||
ctx(p_ctx),
|
||||
reset(p_reset) {}
|
||||
|
||||
static void free(CallbackArgs **p_args) {
|
||||
|
||||
12
thirdparty/README.md
vendored
12
thirdparty/README.md
vendored
@@ -694,6 +694,18 @@ Patches:
|
||||
- `0001-msvc-2019-psa-redeclaration.patch` ([GH-90535](https://github.com/godotengine/godot/pull/90535))
|
||||
|
||||
|
||||
## metal-cpp
|
||||
|
||||
- Upstream: https://developer.apple.com/metal/cpp/
|
||||
- Version: 26.0 (2025)
|
||||
- License: Apache 2.0
|
||||
|
||||
Update instructions:
|
||||
|
||||
- Download latest metal-cpp ZIP from https://developer.apple.com/metal/cpp/:
|
||||
- Run `update-metal-cpp.sh <path to the downloaded zip>` to extract the relevant files and apply patches.
|
||||
|
||||
|
||||
## meshoptimizer
|
||||
|
||||
- Upstream: https://github.com/zeux/meshoptimizer
|
||||
|
||||
6
thirdparty/metal-cpp/Foundation/NSData.hpp
vendored
6
thirdparty/metal-cpp/Foundation/NSData.hpp
vendored
@@ -33,6 +33,7 @@ class Data : public Copying<Data>
|
||||
{
|
||||
public:
|
||||
void* mutableBytes() const;
|
||||
void* bytes() const;
|
||||
UInteger length() const;
|
||||
};
|
||||
}
|
||||
@@ -44,6 +45,11 @@ _NS_INLINE void* NS::Data::mutableBytes() const
|
||||
return Object::sendMessage<void*>(this, _NS_PRIVATE_SEL(mutableBytes));
|
||||
}
|
||||
|
||||
_NS_INLINE void* NS::Data::bytes() const
|
||||
{
|
||||
return Object::sendMessage<void*>(this, _NS_PRIVATE_SEL(bytes));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
_NS_INLINE NS::UInteger NS::Data::length() const
|
||||
|
||||
@@ -272,6 +272,8 @@ namespace Private
|
||||
"initWithBytes:objCType:");
|
||||
_NS_PRIVATE_DEF_SEL(initWithBytesNoCopy_length_encoding_freeWhenDone_,
|
||||
"initWithBytesNoCopy:length:encoding:freeWhenDone:");
|
||||
_NS_PRIVATE_DEF_SEL(initWithBytes_length_encoding_,
|
||||
"initWithBytes:length:encoding:");
|
||||
_NS_PRIVATE_DEF_SEL(initWithChar_,
|
||||
"initWithChar:");
|
||||
_NS_PRIVATE_DEF_SEL(initWithCoder_,
|
||||
@@ -372,6 +374,8 @@ namespace Private
|
||||
"methodSignatureForSelector:");
|
||||
_NS_PRIVATE_DEF_SEL(mutableBytes,
|
||||
"mutableBytes");
|
||||
_NS_PRIVATE_DEF_SEL(bytes,
|
||||
"bytes");
|
||||
_NS_PRIVATE_DEF_SEL(name,
|
||||
"name");
|
||||
_NS_PRIVATE_DEF_SEL(nextObject,
|
||||
|
||||
7
thirdparty/metal-cpp/Foundation/NSString.hpp
vendored
7
thirdparty/metal-cpp/Foundation/NSString.hpp
vendored
@@ -87,6 +87,7 @@ public:
|
||||
String* init();
|
||||
String* init(const String* pString);
|
||||
String* init(const char* pString, StringEncoding encoding);
|
||||
String* init(void* pBytes, UInteger len, StringEncoding encoding);
|
||||
String* init(void* pBytes, UInteger len, StringEncoding encoding, bool freeBuffer);
|
||||
|
||||
unichar character(UInteger index) const;
|
||||
@@ -168,6 +169,12 @@ _NS_INLINE NS::String* NS::String::init(const char* pString, StringEncoding enco
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
_NS_INLINE NS::String* NS::String::init(void* pBytes, UInteger len, StringEncoding encoding)
|
||||
{
|
||||
return Object::sendMessage<String*>(this, _NS_PRIVATE_SEL(initWithBytes_length_encoding_), pBytes, len, encoding);
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
_NS_INLINE NS::String* NS::String::init(void* pBytes, UInteger len, StringEncoding encoding, bool freeBuffer)
|
||||
{
|
||||
return Object::sendMessage<String*>(this, _NS_PRIVATE_SEL(initWithBytesNoCopy_length_encoding_freeWhenDone_), pBytes, len, encoding, freeBuffer);
|
||||
|
||||
313
thirdparty/metal-cpp/README.md
vendored
313
thirdparty/metal-cpp/README.md
vendored
@@ -1,313 +0,0 @@
|
||||
## About
|
||||
|
||||
**metal-cpp** is a low overhead and header only C++ interface for Metal that helps developers add Metal functionality to graphics applications that are written in C++ (such as game engines). **metal-cpp** removes the need to create a shim and allows developers to call Metal functions directly from anywhere in their existing C++ code.
|
||||
|
||||
|
||||
## Highlights
|
||||
|
||||
- Drop in C++ alternative interface to the Metal Objective-C headers.
|
||||
- Direct mapping of all Metal Objective-C classes, constants and enums to C++ in the MTL C++ namespace.
|
||||
- No measurable overhead compared to calling Metal Objective-C headers, due to inlining of C++ function calls.
|
||||
- No usage of wrapper containers that require additional allocations.
|
||||
- Requires C++17 due to the usage of `constexpr` in `NS::Object`.
|
||||
- Identical header files and function/constant/enum availability for iOS, macOS and tvOS.
|
||||
- Backwards compatibility: All `bool MTL::Device::supports...()` functions check if their required selectors exist and automatically return `false` if not.
|
||||
- String (`ErrorDomain`) constants are weak linked and automatically set to `nullptr` if not available.
|
||||
|
||||
## Changelog
|
||||
|
||||
| Version | Changes |
|
||||
|-|-|
|
||||
| macOS 26, iOS 26 | Add all the Metal APIs in macOS 26, iOS 26, including support for the **Apple10** GPU family. <br/>Add support for Metal 4 and new denoiser and temporal scalers in MetalFX.|
|
||||
| macOS 15, iOS 18 | Add all the Metal APIs in macOS 15 and iOS 18. |
|
||||
| macOS 14, iOS 17 | Add support for the **MetalFX** framework. <br/>Add all the APIs in macOS 14 and iOS 17. |
|
||||
| macOS 13.3, iOS 16.4 | Add all the APIs in macOS 13.3 and iOS 16.4. |
|
||||
| macOS 13, iOS 16| Add all the APIs in macOS 13 and iOS 16.<br />New optional `NS::SharedPtr<T>` type to assist with memory management.<br/>New convenience function to create a `CA::MetalLayer`.<br/>New `MTLSTR(str)` macro allows faster string creation from literals.<br/>Fix a problem with the signature of functions that take an array of pointers as input.<br/>Fix a problem with the signature of the `setGroups()` function in `MTL::LinkedFunctions`.|
|
||||
| macOS 12, iOS 15 | Initial release. |
|
||||
|
||||
## Memory Allocation Policy
|
||||
|
||||
**metal-cpp** follows the object allocation policies of Cocoa, Cocoa Touch, and CoreFoundation. Understanding these rules is especially important when using metal-cpp, as C++ objects are not eligible for automatic reference counting (ARC).
|
||||
|
||||
**metal-cpp** objects are reference counted. To help convey and manage object lifecycles, the following conventions are observed:
|
||||
|
||||
1. *You own any object returned by methods whose name begins with* `alloc` *,* `new` *,* `copy` *,* `mutableCopy` *, or* `Create`. The method returns these objects with `retainCount` equals to `1`.
|
||||
2. *You can take ownership of an object by calling its* ```retain()``` *method*. A received object is normally guaranteed to remain valid within the method it was received in. You use `retain` in two situations: (1) In the implementation of an accessor method (a setter) or to take ownership of an object; and (2) To prevent an object from being deallocated as a side-effect of some other operation.
|
||||
3. *When you no longer need it, you must relinquish ownership of an object you own*. You relinquish ownership by calling its `release()` or `autorelease()` method.
|
||||
4. *You must not relinquish ownership of an object you do not own*.
|
||||
|
||||
When an object's `retainCount` reaches `0`, the object is immediately deallocated. It is illegal to call methods on a deallocated object and it may lead to an application crash.
|
||||
|
||||
### AutoreleasePools and Objects
|
||||
|
||||
Several methods that create temporary objects in **metal-cpp** add them to an `AutoreleasePool` to help manage their lifetimes. In these situations, after **metal-cpp** creates the object, it adds it to an `AutoreleasePool`, which will release its objects when you release (or drain) it.
|
||||
|
||||
By adding temporary objects to an AutoreleasePool, you do not need to explicitly call `release()` to deallocate them. Instead, you can rely on the `AutoreleasePool` to implicitly manage those lifetimes.
|
||||
|
||||
If you create an object with a method that does not begin with `alloc`, `new`, `copy`, `mutableCopy`, or `Create`, the creating method adds the object to an autorelease pool.
|
||||
|
||||
The typical scope of an `AutoreleasePool` is one frame of rendering for the main thread of the program. When the thread returns control to the RunLoop (an object responsible for receiving input and events from the windowing system), the pool is *drained*, releasing its objects.
|
||||
|
||||
You can create and manage additional `AutoreleasePool`s at smaller scopes to reduce your program's working set, and you are required to do so for any additional threads your program creates.
|
||||
|
||||
If an object's lifecycle needs to be extended beyond the scope of an `AutoreleasePool` instance, you can claim ownership of it by calling its `retain()` method before the pool is drained. In these cases, you are responsible for making the appropriate `release()` call on the object after you no longer need it.
|
||||
|
||||
You can find a more-detailed introduction to the memory management rules here: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html, and here: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/Concepts/Ownership.html
|
||||
|
||||
For more details about the application's RunLoop, please find its documentation here: https://developer.apple.com/documentation/foundation/nsrunloop
|
||||
|
||||
### Use and debug AutoreleasePools
|
||||
|
||||
When you create an autoreleased object and there is no enclosing `AutoreleasePool`, the object is leaked.
|
||||
|
||||
To prevent this, you normally create an `AutoreleasePool` in your program's `main` function, and in the entry function for every thread you create. You may also create additional `AutoreleasePool`s to avoid growing your program's high memory watermark when you create several autoreleased objects, such as when rendering.
|
||||
|
||||
Use the Environment Variable `OBJC_DEBUG_MISSING_POOLS=YES` to print a runtime warning when an autoreleased object is leaked because no enclosing `AutoreleasePool` is available for its thread.
|
||||
|
||||
You can also run `leaks --autoreleasePools` on a memgraph file or a process ID (macOS only) to view a listing of your program's `AutoreleasePool`s and all objects they contain.
|
||||
|
||||
### NS::SharedPtr
|
||||
|
||||
The **metal-cpp** headers include an optional `NS::SharedPtr<>` (shared pointer) template that can help you manually manage memory in your apps.
|
||||
|
||||
Shared pointers in **metal-cpp** are different from `std::shared_ptr<>` in that they implement specific optimizations for its memory model. For example, **metal-cpp**'s shared pointers avoid the overhead of the standard library's version by leveraging the reference counting implementation of the `NS::Object` type.
|
||||
|
||||
#### Note
|
||||
|
||||
The **metal-cpp** shared pointer’s destructor method always calls the `release()` method of the pointer that it wraps.
|
||||
|
||||
You can create an `NS::SharedPtr<>` by calling the metal-cpp's factory method that's appropriate for your application's intent:
|
||||
|
||||
* You can **transfer** ownership of a pointer to a new shared pointer instance by calling the `NS::TransferPtr()` factory function, which is the correct function for Resource Acquisition is Initialization (RAII) implementations because it doesn't increase the pointee's retain count.
|
||||
|
||||
* You can **share** ownership of a pointer with another entity by calling the `NS::RetainPtr()` factory function. This function can also extend an object's lifecycle beyond an `AutoreleasePool` instance's scope because it creates a strong reference to the pointee and increases its retain count.
|
||||
|
||||
Usage of `NS::SharedPtr<>` is optional.
|
||||
|
||||
### nullptr
|
||||
|
||||
Similar to Objective-C, it is legal to call any method, including `retain()` and `release()`, on `nullptr` "objects". While calling methods on `nullptr` still does incur in function call overhead, the effective result is equivalent of a NOP.
|
||||
|
||||
Conversely, do not assume that because calling a method on a pointer did not result in a crash, that the pointed-to object is valid.
|
||||
|
||||
## Adding metal-cpp to a Project
|
||||
|
||||
Simply include `Metal/Metal.hpp`. To ensure that the selector and class symbols are linked, add to one of your cpp files:
|
||||
|
||||
```cpp
|
||||
#define NS_PRIVATE_IMPLEMENTATION
|
||||
#define MTL_PRIVATE_IMPLEMENTATION
|
||||
|
||||
#include "Metal/Metal.hpp"
|
||||
```
|
||||
|
||||
If you want to use the QuartzCore wrapper, add:
|
||||
|
||||
```cpp
|
||||
#define CA_PRIVATE_IMPLEMENTATION
|
||||
|
||||
#include "QuartzCore/QuartzCore.hpp"
|
||||
```
|
||||
|
||||
## Generating a Single Header File
|
||||
|
||||
Purely optional: You can generate a single header file that contains all **metal-cpp** headers via:
|
||||
|
||||
```shell
|
||||
./SingleHeader/MakeSingleHeader.py Foundation/Foundation.hpp QuartzCore/QuartzCore.hpp Metal/Metal.hpp MetalFX/MetalFX.hpp
|
||||
```
|
||||
|
||||
By default the generator script writes its output to `./SingleHeader/Metal.hpp`. Use the `-o` option to customize output filename.
|
||||
|
||||
## Global Symbol Visibility
|
||||
|
||||
metal-cpp marks all its symbols with `default` visibility. Define the macro: `METALCPP_SYMBOL_VISIBILITY_HIDDEN` to override this behavior and hide its symbols.
|
||||
|
||||
## Examples
|
||||
|
||||
#### Creating the device
|
||||
|
||||
###### Objective-C (with automatic reference counting)
|
||||
|
||||
```objc
|
||||
id< MTLDevice > device = MTLCreateSystemDefaultDevice();
|
||||
|
||||
// ...
|
||||
```
|
||||
|
||||
###### Objective-C
|
||||
|
||||
```objc
|
||||
id< MTLDevice > device = MTLCreateSystemDefaultDevice();
|
||||
|
||||
// ...
|
||||
|
||||
[device release];
|
||||
```
|
||||
|
||||
###### C++
|
||||
|
||||
```cpp
|
||||
MTL::Device* pDevice = MTL::CreateSystemDefaultDevice();
|
||||
|
||||
// ...
|
||||
|
||||
pDevice->release();
|
||||
```
|
||||
|
||||
###### C++ (using NS::SharedPtr)
|
||||
|
||||
```cpp
|
||||
NS::SharedPtr< MTL::Device > pDevice = NS::TransferPtr( MTL::CreateSystemDefaultDevice() );
|
||||
|
||||
// ...
|
||||
```
|
||||
|
||||
#### Metal function calls map directly to C++
|
||||
|
||||
###### Objective-C (with automatic reference counting)
|
||||
|
||||
```objc
|
||||
MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];
|
||||
|
||||
[samplerDescriptor setSAddressMode: MTLSamplerAddressModeRepeat];
|
||||
[samplerDescriptor setTAddressMode: MTLSamplerAddressModeRepeat];
|
||||
[samplerDescriptor setRAddressMode: MTLSamplerAddressModeRepeat];
|
||||
[samplerDescriptor setMagFilter: MTLSamplerMinMagFilterLinear];
|
||||
[samplerDescriptor setMinFilter: MTLSamplerMinMagFilterLinear];
|
||||
[samplerDescriptor setMipFilter: MTLSamplerMipFilterLinear];
|
||||
[samplerDescriptor setSupportArgumentBuffers: YES];
|
||||
|
||||
id< MTLSamplerState > samplerState = [device newSamplerStateWithDescriptor:samplerDescriptor];
|
||||
```
|
||||
|
||||
###### Objective-C
|
||||
|
||||
```objc
|
||||
MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];
|
||||
|
||||
[samplerDescriptor setSAddressMode: MTLSamplerAddressModeRepeat];
|
||||
[samplerDescriptor setTAddressMode: MTLSamplerAddressModeRepeat];
|
||||
[samplerDescriptor setRAddressMode: MTLSamplerAddressModeRepeat];
|
||||
[samplerDescriptor setMagFilter: MTLSamplerMinMagFilterLinear];
|
||||
[samplerDescriptor setMinFilter: MTLSamplerMinMagFilterLinear];
|
||||
[samplerDescriptor setMipFilter: MTLSamplerMipFilterLinear];
|
||||
[samplerDescriptor setSupportArgumentBuffers: YES];
|
||||
|
||||
id< MTLSamplerState > samplerState = [device newSamplerStateWithDescriptor:samplerDescriptor];
|
||||
|
||||
[samplerDescriptor release];
|
||||
|
||||
// ...
|
||||
|
||||
[samplerState release];
|
||||
```
|
||||
|
||||
###### C++
|
||||
|
||||
```cpp
|
||||
MTL::SamplerDescriptor* pSamplerDescriptor = MTL::SamplerDescriptor::alloc()->init();
|
||||
|
||||
pSamplerDescriptor->setSAddressMode( MTL::SamplerAddressModeRepeat );
|
||||
pSamplerDescriptor->setTAddressMode( MTL::SamplerAddressModeRepeat );
|
||||
pSamplerDescriptor->setRAddressMode( MTL::SamplerAddressModeRepeat );
|
||||
pSamplerDescriptor->setMagFilter( MTL::SamplerMinMagFilterLinear );
|
||||
pSamplerDescriptor->setMinFilter( MTL::SamplerMinMagFilterLinear );
|
||||
pSamplerDescriptor->setMipFilter( MTL::SamplerMipFilterLinear );
|
||||
pSamplerDescriptor->setSupportArgumentBuffers( true );
|
||||
|
||||
MTL::SamplerState* pSamplerState = pDevice->newSamplerState( pSamplerDescriptor );
|
||||
|
||||
pSamplerDescriptor->release();
|
||||
|
||||
// ...
|
||||
|
||||
pSamplerState->release();
|
||||
```
|
||||
|
||||
###### C++ (using NS::SharedPtr)
|
||||
|
||||
```cpp
|
||||
NS::SharedPtr< MTL::SamplerDescriptor > pSamplerDescriptor = NS::TransferPtr( MTL::SamplerDescriptor::alloc()->init() );
|
||||
|
||||
pSamplerDescriptor->setSAddressMode( MTL::SamplerAddressModeRepeat );
|
||||
pSamplerDescriptor->setTAddressMode( MTL::SamplerAddressModeRepeat );
|
||||
pSamplerDescriptor->setRAddressMode( MTL::SamplerAddressModeRepeat );
|
||||
pSamplerDescriptor->setMagFilter( MTL::SamplerMinMagFilterLinear );
|
||||
pSamplerDescriptor->setMinFilter( MTL::SamplerMinMagFilterLinear );
|
||||
pSamplerDescriptor->setMipFilter( MTL::SamplerMipFilterLinear );
|
||||
pSamplerDescriptor->setSupportArgumentBuffers( true );
|
||||
|
||||
NS::SharedPtr< MTL::SamplerState > pSamplerState( pDevice->newSamplerState( pSamplerDescriptor ) );
|
||||
```
|
||||
|
||||
#### A subset of bindings for Foundation classes is provided for seamless integration
|
||||
|
||||
###### Objective-C (with automatic reference counting)
|
||||
|
||||
```objc
|
||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
NSString* string = [NSString stringWithCString: "Hello World" encoding: NSASCIIStringEncoding];
|
||||
|
||||
printf( "string = \"%s\"\n", [string cStringUsingEncoding: NSASCIIStringEncoding] );
|
||||
```
|
||||
|
||||
###### Objective-C
|
||||
|
||||
```objc
|
||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||
NSString* string = [NSString stringWithCString: "Hello World" encoding: NSASCIIStringEncoding];
|
||||
|
||||
printf( "string = \"%s\"\n", [string cStringUsingEncoding: NSASCIIStringEncoding] );
|
||||
|
||||
[pool release];
|
||||
```
|
||||
|
||||
###### C++
|
||||
|
||||
```cpp
|
||||
NS::AutoreleasePool* pPool = NS::AutoreleasePool::alloc()->init();
|
||||
NS::String* pString = NS::String::string( "Hello World", NS::ASCIIStringEncoding );
|
||||
|
||||
printf( "pString = \"%s\"\n", pString->cString( NS::ASCIIStringEncoding ) );
|
||||
|
||||
pPool->release();
|
||||
```
|
||||
|
||||
###### C++ (using NS::SharedPtr)
|
||||
|
||||
```cpp
|
||||
NS::SharedPtr< NS::AutoreleasePool > pPool = NS::TransferPtr( NS::AutoreleasePool::alloc()->init() );
|
||||
NS::String* pString = NS::String::string( "Hello World", NS::ASCIIStringEncoding );
|
||||
|
||||
printf( "pString = \"%s\"\n", pString->cString( NS::ASCIIStringEncoding ) );
|
||||
```
|
||||
|
||||
#### Containers
|
||||
|
||||
Use the CoreFoundation framework to create `NS::Array` and `NS::Dictionary` instances.
|
||||
|
||||
```cpp
|
||||
MTL::AccelerationStructureTriangleGeometryDescriptor* pGeoDescriptor = MTL::AccelerationStructureTriangleGeometryDescriptor::alloc()->init();
|
||||
CFTypeRef descriptors[] = { ( CFTypeRef )( pGeoDescriptor ) };
|
||||
NS::Array* pGeoDescriptors = ( NS::Array* )( CFArrayCreate( kCFAllocatorDefault, descriptors, SIZEOF_ARRAY( descriptors), &kCFTypeArrayCallBacks ) );
|
||||
|
||||
// ...
|
||||
|
||||
pGeoDescriptors->release();
|
||||
```
|
||||
|
||||
Containers, such as `NS::Array` and `NS::Dictionary`, retain the objects they hold and release them when the container is deallocated.
|
||||
|
||||
#### Accessing the Metal Drawable
|
||||
|
||||
```cpp
|
||||
#import <QuartzCore/QuartzCore.hpp>
|
||||
|
||||
// ...
|
||||
|
||||
CA::MetalLayer* pMetalLayer = /* layer associated with the view */;
|
||||
CA::MetalDrawable* pMetalDrawable = pMetalLayer->nextDrawable();
|
||||
|
||||
// ...
|
||||
```
|
||||
@@ -1,271 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
#--------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
#
|
||||
# SingleHeader/MakeSingleHeader.py
|
||||
#
|
||||
# Copyright 2020-2024 Apple Inc.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#--------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
#--------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
class HeaderPrefix( object ):
|
||||
__template = ( '//\n'
|
||||
'// {file}\n'
|
||||
'//\n'
|
||||
'// {meta_data}\n'
|
||||
'//\n'
|
||||
'// Copyright 2020-2024 Apple Inc.\n'
|
||||
'//\n'
|
||||
'// Licensed under the Apache License, Version 2.0 (the "License");\n'
|
||||
'// you may not use this file except in compliance with the License.\n'
|
||||
'// You may obtain a copy of the License at\n'
|
||||
'//\n'
|
||||
'// http://www.apache.org/licenses/LICENSE-2.0\n'
|
||||
'//\n'
|
||||
'// Unless required by applicable law or agreed to in writing, software\n'
|
||||
'// distributed under the License is distributed on an "AS IS" BASIS,\n'
|
||||
'// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
|
||||
'// See the License for the specific language governing permissions and\n'
|
||||
'// limitations under the License.\n'
|
||||
'//\n'
|
||||
'\n' )
|
||||
|
||||
__template_commit = 'Autogenerated from commit {commit}.'
|
||||
__template_date = 'Autogenerated on %B %d, %Y.'
|
||||
|
||||
def __init__( self, file ):
|
||||
self.__file = file
|
||||
|
||||
def __str__( self ):
|
||||
return self.__template.format( file = self.__file, meta_data = self.__meta_data_string() )
|
||||
|
||||
def __get_commit_hash( self ):
|
||||
git_commit_hash = None
|
||||
|
||||
try:
|
||||
git_dir = os.path.dirname( os.path.realpath( __file__ ) )
|
||||
proc = subprocess.Popen( [ 'git', 'rev-parse', 'HEAD' ], cwd = git_dir, stdout = subprocess.PIPE, stderr = subprocess.PIPE )
|
||||
git_commit_hash = proc.stdout.read().decode( 'utf-8', 'replace' ).strip()
|
||||
except:
|
||||
logging.error( 'Failed to determine git commit hash!' )
|
||||
pass
|
||||
|
||||
return git_commit_hash
|
||||
|
||||
def __get_commit_string( self ):
|
||||
meta_data = None
|
||||
git_commit_hash = self.__get_commit_hash()
|
||||
|
||||
if git_commit_hash:
|
||||
meta_data = self.__template_commit.format( commit = git_commit_hash )
|
||||
|
||||
return meta_data
|
||||
|
||||
def __get_date_string( self ):
|
||||
today = datetime.date.today()
|
||||
|
||||
return today.strftime( self.__template_date )
|
||||
|
||||
def __meta_data_string( self ):
|
||||
meta_data = self.__get_commit_string()
|
||||
|
||||
if not meta_data:
|
||||
meta_data = self.__get_date_string()
|
||||
|
||||
return meta_data
|
||||
|
||||
#--------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
class SingleHeader( object ):
|
||||
__pragma_once = '#pragma once\n\n'
|
||||
|
||||
def __init__( self ):
|
||||
self.__header_paths = list()
|
||||
|
||||
def __str__( self ):
|
||||
return self.process()
|
||||
|
||||
def append( self, header_path ):
|
||||
self.__header_paths.append( header_path )
|
||||
|
||||
def process( self ):
|
||||
out_header = self.__pragma_once
|
||||
|
||||
self.__included_headers = set()
|
||||
self.__base_path = list()
|
||||
|
||||
for header_path in self.__header_paths:
|
||||
out_header += self.__process_header( header_path )
|
||||
|
||||
return self.__strip_empty_lines( out_header )
|
||||
|
||||
def __read_header( self, path ):
|
||||
path = os.path.realpath( path )
|
||||
|
||||
try:
|
||||
f = open( path, 'r' )
|
||||
except:
|
||||
raise RuntimeError( 'Failed to open file \"' + path + '\" for read!' )
|
||||
|
||||
return f.read()
|
||||
|
||||
def __strip_pragma_once( self, header ):
|
||||
return re.sub( '\\s*#pragma once\s*\\/\\/-*\\n', '', header )
|
||||
|
||||
def __strip_comments( self, header ):
|
||||
return re.sub( '^//.*\\n', '', header, flags = re.MULTILINE )
|
||||
|
||||
def __strip_empty_lines( self, header ):
|
||||
return re.sub( '\\n\\n+', '\\n\\n', header, flags = re.MULTILINE )
|
||||
|
||||
def __substitute_include_directive( self, match ):
|
||||
header_path = match.group( 'HEADER_PATH' )
|
||||
|
||||
logging.info( '\tSubstituting \"' + header_path + '\"...' )
|
||||
|
||||
return self.__process_header( os.path.join( self.__base_path[-1], header_path ) )
|
||||
|
||||
def __process_include_directives( self, header ):
|
||||
return re.sub( '^\\s*#include\\s\\"(?P<HEADER_PATH>\\S*)\\"', self.__substitute_include_directive, header, flags = re.MULTILINE )
|
||||
|
||||
def __process_foundation_directives( self, header ):
|
||||
if header.find("#include <Foundation/Foundation.hpp>") != -1:
|
||||
logging.info( '\tSubstituting <Foundation/Foundation.hpp>...' )
|
||||
return header.replace("#include <Foundation/Foundation.hpp>", self.__process_header( os.path.join( self.__base_path[-1], "../Foundation/Foundation.hpp" ) ) )
|
||||
return header
|
||||
|
||||
|
||||
def __process_header( self, header_path ):
|
||||
out_header = ''
|
||||
|
||||
header_path = os.path.realpath( header_path )
|
||||
|
||||
if not header_path in self.__included_headers:
|
||||
logging.info( 'Processing \"' + header_path + '\"...' )
|
||||
|
||||
self.__base_path.append( os.path.dirname( header_path ) )
|
||||
self.__included_headers.add( header_path )
|
||||
|
||||
out_header = self.__read_header( header_path )
|
||||
out_header = self.__strip_pragma_once( out_header )
|
||||
out_header = self.__strip_comments( out_header )
|
||||
out_header = self.__process_include_directives( out_header )
|
||||
out_header = self.__process_foundation_directives( out_header )
|
||||
|
||||
self.__base_path.pop()
|
||||
else:
|
||||
logging.info( '\tSkipping \"' + header_path + '\"...' )
|
||||
|
||||
return out_header
|
||||
|
||||
#--------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def create_argument_parser():
|
||||
parser = argparse.ArgumentParser()
|
||||
base_path = os.path.dirname( os.path.realpath( __file__ ) )
|
||||
output_path = os.path.join( base_path, 'Metal.hpp' )
|
||||
|
||||
parser.add_argument( '-o', '--output', dest = 'output_path', metavar = 'PATH', default = output_path, help = 'Output path for the single header file.' )
|
||||
parser.add_argument( '-v', '--verbose', action = 'store_true', help = 'Show verbose output.' )
|
||||
parser.add_argument( dest = 'header_paths', metavar = 'HEADER_FILE', nargs='+', help = 'Input header file.' )
|
||||
|
||||
return parser
|
||||
|
||||
#--------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def parse_arguments():
|
||||
parser = create_argument_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
logging.getLogger().setLevel( logging.INFO )
|
||||
else:
|
||||
logging.getLogger().setLevel( logging.ERROR )
|
||||
|
||||
return args
|
||||
|
||||
#--------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def make_header( args ):
|
||||
prefix = HeaderPrefix( os.path.basename( args.output_path ) )
|
||||
header = SingleHeader()
|
||||
|
||||
for header_path in args.header_paths:
|
||||
header.append( header_path )
|
||||
|
||||
return str( prefix ) + str( header )
|
||||
|
||||
#--------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def make_dir( path ):
|
||||
try:
|
||||
if not os.path.exists( path ):
|
||||
os.makedirs( path )
|
||||
except os.error:
|
||||
pass
|
||||
except:
|
||||
raise
|
||||
|
||||
#--------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def write_header( args, content ):
|
||||
path = os.path.realpath( args.output_path )
|
||||
|
||||
logging.info( 'Writing \"' + path + '\"...' )
|
||||
|
||||
make_dir( os.path.dirname( path ) )
|
||||
|
||||
try:
|
||||
f = open( path, 'w' )
|
||||
except:
|
||||
raise RuntimeError( 'Failed to open file \"' + path + '\" for write!' )
|
||||
|
||||
f.write( content )
|
||||
|
||||
#--------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
if __name__ == '__main__':
|
||||
result = -1
|
||||
|
||||
try:
|
||||
if sys.getdefaultencoding().lower() == 'ascii':
|
||||
reload( sys )
|
||||
sys.setdefaultencoding( 'utf-8' )
|
||||
|
||||
args = parse_arguments()
|
||||
header = make_header( args )
|
||||
|
||||
write_header( args, header )
|
||||
|
||||
result = 0
|
||||
|
||||
except ( KeyboardInterrupt, SystemExit ):
|
||||
pass
|
||||
except:
|
||||
raise
|
||||
|
||||
sys.exit( result )
|
||||
|
||||
#--------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
76
thirdparty/metal-cpp/patches/0001-add-missing-apis.patch
vendored
Normal file
76
thirdparty/metal-cpp/patches/0001-add-missing-apis.patch
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
thirdparty/metal-cpp/Foundation/NSData.hpp | 6 ++++++
|
||||
thirdparty/metal-cpp/Foundation/NSPrivate.hpp | 4 ++++
|
||||
thirdparty/metal-cpp/Foundation/NSString.hpp | 7 +++++++
|
||||
3 files changed, 17 insertions(+)
|
||||
|
||||
diff --git a/thirdparty/metal-cpp/Foundation/NSData.hpp b/thirdparty/metal-cpp/Foundation/NSData.hpp
|
||||
index 3ad360609f..fbf3f20343 100644
|
||||
--- a/thirdparty/metal-cpp/Foundation/NSData.hpp
|
||||
+++ b/thirdparty/metal-cpp/Foundation/NSData.hpp
|
||||
@@ -33,6 +33,7 @@ class Data : public Copying<Data>
|
||||
{
|
||||
public:
|
||||
void* mutableBytes() const;
|
||||
+ void* bytes() const;
|
||||
UInteger length() const;
|
||||
};
|
||||
}
|
||||
@@ -44,6 +45,11 @@ _NS_INLINE void* NS::Data::mutableBytes() const
|
||||
return Object::sendMessage<void*>(this, _NS_PRIVATE_SEL(mutableBytes));
|
||||
}
|
||||
|
||||
+_NS_INLINE void* NS::Data::bytes() const
|
||||
+{
|
||||
+ return Object::sendMessage<void*>(this, _NS_PRIVATE_SEL(bytes));
|
||||
+}
|
||||
+
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
_NS_INLINE NS::UInteger NS::Data::length() const
|
||||
diff --git a/thirdparty/metal-cpp/Foundation/NSPrivate.hpp b/thirdparty/metal-cpp/Foundation/NSPrivate.hpp
|
||||
index f8d87004f3..17909fbd2a 100644
|
||||
--- a/thirdparty/metal-cpp/Foundation/NSPrivate.hpp
|
||||
+++ b/thirdparty/metal-cpp/Foundation/NSPrivate.hpp
|
||||
@@ -272,6 +272,8 @@ namespace Private
|
||||
"initWithBytes:objCType:");
|
||||
_NS_PRIVATE_DEF_SEL(initWithBytesNoCopy_length_encoding_freeWhenDone_,
|
||||
"initWithBytesNoCopy:length:encoding:freeWhenDone:");
|
||||
+ _NS_PRIVATE_DEF_SEL(initWithBytes_length_encoding_,
|
||||
+ "initWithBytes:length:encoding:");
|
||||
_NS_PRIVATE_DEF_SEL(initWithChar_,
|
||||
"initWithChar:");
|
||||
_NS_PRIVATE_DEF_SEL(initWithCoder_,
|
||||
@@ -372,6 +374,8 @@ namespace Private
|
||||
"methodSignatureForSelector:");
|
||||
_NS_PRIVATE_DEF_SEL(mutableBytes,
|
||||
"mutableBytes");
|
||||
+ _NS_PRIVATE_DEF_SEL(bytes,
|
||||
+ "bytes");
|
||||
_NS_PRIVATE_DEF_SEL(name,
|
||||
"name");
|
||||
_NS_PRIVATE_DEF_SEL(nextObject,
|
||||
diff --git a/thirdparty/metal-cpp/Foundation/NSString.hpp b/thirdparty/metal-cpp/Foundation/NSString.hpp
|
||||
index 07ba3f8d39..d4d0c52ec2 100644
|
||||
--- a/thirdparty/metal-cpp/Foundation/NSString.hpp
|
||||
+++ b/thirdparty/metal-cpp/Foundation/NSString.hpp
|
||||
@@ -87,6 +87,7 @@ public:
|
||||
String* init();
|
||||
String* init(const String* pString);
|
||||
String* init(const char* pString, StringEncoding encoding);
|
||||
+ String* init(void* pBytes, UInteger len, StringEncoding encoding);
|
||||
String* init(void* pBytes, UInteger len, StringEncoding encoding, bool freeBuffer);
|
||||
|
||||
unichar character(UInteger index) const;
|
||||
@@ -168,6 +169,12 @@ _NS_INLINE NS::String* NS::String::init(const char* pString, StringEncoding enco
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
+_NS_INLINE NS::String* NS::String::init(void* pBytes, UInteger len, StringEncoding encoding)
|
||||
+{
|
||||
+ return Object::sendMessage<String*>(this, _NS_PRIVATE_SEL(initWithBytes_length_encoding_), pBytes, len, encoding);
|
||||
+}
|
||||
+//-------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
+
|
||||
_NS_INLINE NS::String* NS::String::init(void* pBytes, UInteger len, StringEncoding encoding, bool freeBuffer)
|
||||
{
|
||||
return Object::sendMessage<String*>(this, _NS_PRIVATE_SEL(initWithBytesNoCopy_length_encoding_freeWhenDone_), pBytes, len, encoding, freeBuffer);
|
||||
79
thirdparty/metal-cpp/update-metal-cpp.sh
vendored
79
thirdparty/metal-cpp/update-metal-cpp.sh
vendored
@@ -7,58 +7,24 @@
|
||||
|
||||
VERSION="macOS26-iOS26"
|
||||
|
||||
pushd "$(dirname "$0")" > /dev/null
|
||||
SCRIPT_DIR="$(pwd)"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
# If a tarball/zip is provided as argument, extract it
|
||||
# If a zip is provided as argument, extract it
|
||||
if [ -n "$1" ]; then
|
||||
echo "Updating metal-cpp from: $1"
|
||||
|
||||
# Create temp directory for extraction and backup
|
||||
TMPDIR=$(mktemp -d)
|
||||
trap "rm -rf '$TMPDIR'" EXIT
|
||||
rm -rf \
|
||||
"$SCRIPT_DIR/Foundation" \
|
||||
"$SCRIPT_DIR/Metal" \
|
||||
"$SCRIPT_DIR/MetalFX" \
|
||||
"$SCRIPT_DIR/QuartzCore" \
|
||||
"$SCRIPT_DIR/SingleHeader"
|
||||
|
||||
# Preserve this script
|
||||
cp "$SCRIPT_DIR/update-metal-cpp.sh" "$TMPDIR/update-metal-cpp.sh.bak"
|
||||
|
||||
# Clean existing files (keep this script) - use absolute path for safety
|
||||
find "$SCRIPT_DIR" -mindepth 1 -maxdepth 1 ! -name 'update-metal-cpp.sh' -exec rm -rf {} +
|
||||
|
||||
# Extract archive
|
||||
pushd "$TMPDIR" > /dev/null
|
||||
if [[ "$1" == *.zip ]]; then
|
||||
unzip -q "$1"
|
||||
else
|
||||
tar --strip-components=1 -xf "$1"
|
||||
fi
|
||||
|
||||
# Copy contents (handle both flat and nested archives)
|
||||
if [ -d "metal-cpp" ]; then
|
||||
cp -r metal-cpp/* "$SCRIPT_DIR/"
|
||||
elif [ -d "Metal" ]; then
|
||||
cp -r . "$SCRIPT_DIR/"
|
||||
else
|
||||
# Try to find the metal-cpp directory
|
||||
METAL_DIR=$(find . -type d -name "Metal" -print -quit | xargs dirname)
|
||||
if [ -n "$METAL_DIR" ]; then
|
||||
cp -r "$METAL_DIR"/* "$SCRIPT_DIR/"
|
||||
else
|
||||
echo "Error: Could not find metal-cpp files in archive"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
popd > /dev/null
|
||||
|
||||
# Restore this script
|
||||
mv "$TMPDIR/update-metal-cpp.sh.bak" "$SCRIPT_DIR/update-metal-cpp.sh"
|
||||
unzip -q "$1" -d "$SCRIPT_DIR"
|
||||
|
||||
echo "Extracted metal-cpp $VERSION"
|
||||
else
|
||||
echo "Usage: $0 <path-to-metal-cpp-archive>"
|
||||
echo ""
|
||||
echo "Download metal-cpp from: https://developer.apple.com/metal/cpp/"
|
||||
echo "Then run: $0 /path/to/metal-cpp.zip"
|
||||
echo ""
|
||||
echo "Applying patches only..."
|
||||
fi
|
||||
|
||||
@@ -68,6 +34,30 @@ fi
|
||||
|
||||
echo "Applying Godot compatibility patches..."
|
||||
|
||||
# Apply patch files (idempotent)
|
||||
PATCH_DIR="$SCRIPT_DIR/patches"
|
||||
if [ -d "$PATCH_DIR" ]; then
|
||||
for PATCH in "$PATCH_DIR"/*.patch; do
|
||||
if [ ! -e "$PATCH" ]; then
|
||||
echo " No patches found in $PATCH_DIR"
|
||||
break
|
||||
fi
|
||||
|
||||
PATCH_NAME="$(basename "$PATCH")"
|
||||
if git -C "$REPO_ROOT" apply --check "$PATCH" > /dev/null 2>&1; then
|
||||
git -C "$REPO_ROOT" apply "$PATCH"
|
||||
echo " $PATCH_NAME: applied"
|
||||
elif git -C "$REPO_ROOT" apply --reverse --check "$PATCH" > /dev/null 2>&1; then
|
||||
echo " $PATCH_NAME: already applied"
|
||||
else
|
||||
echo " $PATCH_NAME: failed to apply"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo " Warning: $PATCH_DIR not found"
|
||||
fi
|
||||
|
||||
# Patch 1: Add forward declarations to NSDefines.hpp to avoid conflicts with
|
||||
# Godot's global types (String, Object, Error).
|
||||
#
|
||||
@@ -104,4 +94,3 @@ else
|
||||
fi
|
||||
|
||||
echo "Done."
|
||||
popd > /dev/null
|
||||
|
||||
Reference in New Issue
Block a user