mirror of
https://github.com/godotengine/godot.git
synced 2026-02-07 11:21:49 +00:00
Metal: Refactor for future Metal 4, switch to C++; fix dynamic uniforms
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user