Merge pull request #116371 from stuartcarnie/metal_clamp_to_border

Metal: Various fixes
This commit is contained in:
Thaddeus Crews 2026-02-17 09:28:30 -06:00
commit a8a582e28b
No known key found for this signature in database
GPG key ID: 8C6E5FEB5FC03CCC
16 changed files with 445 additions and 160 deletions

View file

@ -149,6 +149,7 @@ void MetalDeviceProperties::init_features(MTL::Device *p_device) {
features.quadPermute = p_device->supportsFamily(MTL::GPUFamilyApple4);
features.simdPermute = p_device->supportsFamily(MTL::GPUFamilyApple6);
features.simdReduction = p_device->supportsFamily(MTL::GPUFamilyApple7);
features.supports_border_color = 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));

View file

@ -118,6 +118,7 @@ struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalFeatures {
bool supports_image_atomic_32_bit = false; /**< If true, 32-bit atomic operations on images are supported by the GPU. */
bool supports_image_atomic_64_bit = false; /**< If true, 64-bit atomic operations on images are supported by the GPU. */
bool supports_native_image_atomics = false; /**< If true, native image atomic operations are supported by the OS. */
bool supports_border_color = false; /**< If true, sampler border color (clamp-to-border) is supported. Requires Apple7+. */
bool supports_residency_sets = false; /**< If true, residency sets (MTLResidencySet) are supported by the OS. */
/*!

View file

@ -748,14 +748,19 @@ class MDImmediateLibrary final : public MDLibrary {
NS::Error *_error = nullptr;
std::mutex _cv_mutex;
std::condition_variable _cv;
std::atomic<bool> _complete{ false };
bool _ready = false;
bool _complete = false;
void _check_and_wait() {
std::unique_lock<std::mutex> lock(_cv_mutex);
_cv.wait(lock, [this] { return _complete; });
}
public:
MDImmediateLibrary(ShaderCacheEntry *p_entry,
MTL::Device *p_device,
NS::String *p_source,
MTL::CompileOptions *p_options);
~MDImmediateLibrary() override;
MTL::Library *get_library() override;
NS::Error *get_error() override;
@ -787,28 +792,24 @@ MDImmediateLibrary::MDImmediateLibrary(ShaderCacheEntry *p_entry,
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();
std::lock_guard<std::mutex> lock(_cv_mutex);
_complete = true;
_cv.notify_all();
});
}
MDImmediateLibrary::~MDImmediateLibrary() {
// Wait for the async compilation callback to complete before destroying to avoid segfault.
_check_and_wait();
}
MTL::Library *MDImmediateLibrary::get_library() {
if (!_complete) {
std::unique_lock<std::mutex> lock(_cv_mutex);
_cv.wait(lock, [this] { return _ready; });
}
_check_and_wait();
return _library.get();
}
NS::Error *MDImmediateLibrary::get_error() {
if (!_complete) {
std::unique_lock<std::mutex> lock(_cv_mutex);
_cv.wait(lock, [this] { return _ready; });
}
_check_and_wait();
return _error;
}

View file

@ -41,23 +41,6 @@
#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
os_log_t LOG_DRIVER;
@ -127,7 +110,7 @@ public:
Surface(p_device), layer(p_layer) {
layer->setAllowsNextDrawableTimeout(true);
layer->setFramebufferOnly(true);
Private::Selector::sendMessage<void>(layer, _APPLE_PRIVATE_SEL(setOpaque_), !OS::get_singleton()->is_layered_allowed());
layer->setOpaque(!OS::get_singleton()->is_layered_allowed());
layer->setPixelFormat(get_pixel_format());
layer->setDevice(p_device);
}
@ -252,7 +235,7 @@ public:
Surface(p_device), layer(p_layer) {
layer->setAllowsNextDrawableTimeout(true);
layer->setFramebufferOnly(true);
Private::Selector::sendMessage<void>(layer, _APPLE_PRIVATE_SEL(setOpaque_), !OS::get_singleton()->is_layered_allowed());
layer->setOpaque(!OS::get_singleton()->is_layered_allowed());
layer->setPixelFormat(get_pixel_format());
layer->setDevice(p_device);
#if TARGET_OS_OSX

View file

@ -530,7 +530,7 @@ void RenderingDeviceDriverMetal::texture_free(TextureID p_texture) {
uint64_t RenderingDeviceDriverMetal::texture_get_allocation_size(TextureID p_texture) {
MTL::Texture *obj = reinterpret_cast<MTL::Texture *>(p_texture.id);
return obj->allocatedSize();
return NS::Object::sendMessageSafe<NS::UInteger>(obj, _MTL_PRIVATE_SEL(allocatedSize));
}
void RenderingDeviceDriverMetal::texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) {
@ -721,9 +721,26 @@ RDD::SamplerID RenderingDeviceDriverMetal::sampler_create(const SamplerState &p_
desc->setMinFilter(p_state.min_filter == SAMPLER_FILTER_LINEAR ? MTL::SamplerMinMagFilterLinear : MTL::SamplerMinMagFilterNearest);
desc->setMipFilter(p_state.mip_filter == SAMPLER_FILTER_LINEAR ? MTL::SamplerMipFilterLinear : MTL::SamplerMipFilterNearest);
desc->setSAddressMode(ADDRESS_MODES[p_state.repeat_u]);
desc->setTAddressMode(ADDRESS_MODES[p_state.repeat_v]);
desc->setRAddressMode(ADDRESS_MODES[p_state.repeat_w]);
MTL::SamplerAddressMode address_u = ADDRESS_MODES[p_state.repeat_u];
MTL::SamplerAddressMode address_v = ADDRESS_MODES[p_state.repeat_v];
MTL::SamplerAddressMode address_w = ADDRESS_MODES[p_state.repeat_w];
if (!device_properties->features.supports_border_color) {
// Default to clamp to edge if border color is not supported.
if (address_u == MTL::SamplerAddressModeClampToBorderColor) {
address_u = MTL::SamplerAddressModeClampToEdge;
}
if (address_v == MTL::SamplerAddressModeClampToBorderColor) {
address_v = MTL::SamplerAddressModeClampToEdge;
}
if (address_w == MTL::SamplerAddressModeClampToBorderColor) {
address_w = MTL::SamplerAddressModeClampToEdge;
}
}
desc->setSAddressMode(address_u);
desc->setTAddressMode(address_v);
desc->setRAddressMode(address_w);
if (p_state.use_anisotropy) {
desc->setMaxAnisotropy(p_state.anisotropy_max);
@ -734,7 +751,9 @@ RDD::SamplerID RenderingDeviceDriverMetal::sampler_create(const SamplerState &p_
desc->setLodMinClamp(p_state.min_lod);
desc->setLodMaxClamp(p_state.max_lod);
desc->setBorderColor(SAMPLER_BORDER_COLORS[p_state.border_color]);
if (device_properties->features.supports_border_color) {
desc->setBorderColor(SAMPLER_BORDER_COLORS[p_state.border_color]);
}
desc->setNormalizedCoordinates(!p_state.unnormalized_uvw);

View file

@ -151,7 +151,12 @@ void RenderingDeviceDriverMetal::remove_residency_set_to_main_queue(MTL::Residen
#pragma mark - Fences
RDD::FenceID RenderingDeviceDriverMetal::fence_create() {
Fence *fence = memnew(FenceEvent(NS::TransferPtr(device->newSharedEvent())));
Fence *fence = nullptr;
if (__builtin_available(macOS 12.0, iOS 15.0, tvOS 15.0, visionOS 1.0, *)) {
fence = memnew(FenceEvent(NS::TransferPtr(device->newSharedEvent())));
} else {
fence = memnew(FenceSemaphore());
}
return FenceID(fence);
}

View file

@ -33,8 +33,8 @@ class Data : public Copying<Data>
{
public:
void* mutableBytes() const;
void* bytes() const;
UInteger length() const;
const void * bytes() const;
};
}
@ -45,11 +45,6 @@ _NS_INLINE void* NS::Data::mutableBytes() const
return Object::sendMessage<void*>(this, _NS_PRIVATE_SEL(mutableBytes));
}
_NS_INLINE void* NS::Data::bytes() const
{
return Object::sendMessage<void*>(this, _NS_PRIVATE_SEL(bytes));
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
_NS_INLINE NS::UInteger NS::Data::length() const
@ -57,4 +52,9 @@ _NS_INLINE NS::UInteger NS::Data::length() const
return Object::sendMessage<UInteger>(this, _NS_PRIVATE_SEL(length));
}
_NS_INLINE const void * NS::Data::bytes() const
{
return Object::sendMessage<const void *>(this, _NS_PRIVATE_SEL(bytes));
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------

View file

@ -35,8 +35,6 @@
namespace NS
{
class String;
template <class _Class, class _Base = class Object>
class _NS_EXPORT Referencing : public _Base
{
@ -70,6 +68,12 @@ public:
class String* description() const;
class String* debugDescription() const;
template <typename _Ret, typename... _Args>
static _Ret sendMessage(const void* pObj, SEL selector, _Args... args);
template <typename _Ret, typename... _Args>
static _Ret sendMessageSafe(const void* pObj, SEL selector, _Args... args);
protected:
friend class Referencing<Object, objc_object>;
@ -86,10 +90,6 @@ protected:
static bool respondsToSelector(const void* pObj, SEL selector);
template <typename _Type>
static constexpr bool doesRequireMsgSendStret();
template <typename _Ret, typename... _Args>
static _Ret sendMessage(const void* pObj, SEL selector, _Args... args);
template <typename _Ret, typename... _Args>
static _Ret sendMessageSafe(const void* pObj, SEL selector, _Args... args);
private:
Object() = delete;

View file

@ -184,6 +184,8 @@ namespace Private
"bundleWithPath:");
_NS_PRIVATE_DEF_SEL(bundleWithURL_,
"bundleWithURL:");
_NS_PRIVATE_DEF_SEL(bytes,
"bytes");
_NS_PRIVATE_DEF_SEL(caseInsensitiveCompare_,
"caseInsensitiveCompare:");
_NS_PRIVATE_DEF_SEL(characterAtIndex_,
@ -268,12 +270,12 @@ namespace Private
"initFileURLWithPath:");
_NS_PRIVATE_DEF_SEL(initWithBool_,
"initWithBool:");
_NS_PRIVATE_DEF_SEL(initWithBytes_length_encoding_,
"initWithBytes:length:encoding:");
_NS_PRIVATE_DEF_SEL(initWithBytes_objCType_,
"initWithBytes:objCType:");
_NS_PRIVATE_DEF_SEL(initWithBytesNoCopy_length_encoding_freeWhenDone_,
"initWithBytesNoCopy:length:encoding:freeWhenDone:");
_NS_PRIVATE_DEF_SEL(initWithBytes_length_encoding_,
"initWithBytes:length:encoding:");
_NS_PRIVATE_DEF_SEL(initWithChar_,
"initWithChar:");
_NS_PRIVATE_DEF_SEL(initWithCoder_,
@ -374,8 +376,6 @@ namespace Private
"methodSignatureForSelector:");
_NS_PRIVATE_DEF_SEL(mutableBytes,
"mutableBytes");
_NS_PRIVATE_DEF_SEL(bytes,
"bytes");
_NS_PRIVATE_DEF_SEL(name,
"name");
_NS_PRIVATE_DEF_SEL(nextObject,

View file

@ -87,7 +87,6 @@ public:
String* init();
String* init(const String* pString);
String* init(const char* pString, StringEncoding encoding);
String* init(void* pBytes, UInteger len, StringEncoding encoding);
String* init(void* pBytes, UInteger len, StringEncoding encoding, bool freeBuffer);
unichar character(UInteger index) const;
@ -105,6 +104,7 @@ public:
String* stringByAppendingString(const String* pString) const;
ComparisonResult caseInsensitiveCompare(const String* pString) const;
NS::String* init(const void * bytes, NS::UInteger len, NS::StringEncoding encoding);
};
/// Create an NS::String* from a string literal.
@ -122,7 +122,7 @@ template <std::size_t _StringLen>
_NS_INLINE NS::String* NS::String::string()
{
return sendMessage<String*>(_NS_PRIVATE_CLS(NSString), _NS_PRIVATE_SEL(string));
return Object::sendMessage<String*>(_NS_PRIVATE_CLS(NSString), _NS_PRIVATE_SEL(string));
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -169,12 +169,6 @@ _NS_INLINE NS::String* NS::String::init(const char* pString, StringEncoding enco
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
_NS_INLINE NS::String* NS::String::init(void* pBytes, UInteger len, StringEncoding encoding)
{
return Object::sendMessage<String*>(this, _NS_PRIVATE_SEL(initWithBytes_length_encoding_), pBytes, len, encoding);
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
_NS_INLINE NS::String* NS::String::init(void* pBytes, UInteger len, StringEncoding encoding, bool freeBuffer)
{
return Object::sendMessage<String*>(this, _NS_PRIVATE_SEL(initWithBytesNoCopy_length_encoding_freeWhenDone_), pBytes, len, encoding, freeBuffer);
@ -259,4 +253,9 @@ _NS_INLINE NS::ComparisonResult NS::String::caseInsensitiveCompare(const String*
return Object::sendMessage<NS::ComparisonResult>(this, _NS_PRIVATE_SEL(caseInsensitiveCompare_), pString);
}
_NS_INLINE NS::String* NS::String::init(const void * bytes, NS::UInteger len, NS::StringEncoding encoding)
{
return Object::sendMessage<NS::String*>(this, _NS_PRIVATE_SEL(initWithBytes_length_encoding_), bytes, len, encoding);
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------

View file

@ -0,0 +1,79 @@
#pragma once
#include "CADefines.hpp"
#include "CAPrivate.hpp"
#include "../Foundation/NSObject.hpp"
#include <CoreGraphics/CoreGraphics.h>
namespace CA
{
using DynamicRange = NS::String*;
_CA_CONST(DynamicRange, DynamicRangeAutomatic);
_CA_CONST(DynamicRange, DynamicRangeStandard);
_CA_CONST(DynamicRange, DynamicRangeConstrainedHigh);
_CA_CONST(DynamicRange, DynamicRangeHigh);
class Layer : public NS::Referencing<Layer>
{
public:
bool wantsExtendedDynamicRangeContent() const;
void setWantsExtendedDynamicRangeContent(bool wantsExtendedDynamicRangeContent);
CA::DynamicRange preferredDynamicRange() const;
void setPreferredDynamicRange(CA::DynamicRange preferredDynamicRange);
CGFloat contentsHeadroom() const;
void setContentsHeadroom(CGFloat contentsHeadroom);
bool opaque() const;
void setOpaque(bool opaque);
};
} // namespace CA
// --- Inline implementations ---
_CA_INLINE bool CA::Layer::wantsExtendedDynamicRangeContent() const
{
return Object::sendMessage<bool>(this, _CA_PRIVATE_SEL(wantsExtendedDynamicRangeContent));
}
_CA_INLINE void CA::Layer::setWantsExtendedDynamicRangeContent(bool wantsExtendedDynamicRangeContent)
{
Object::sendMessage<void>(this, _CA_PRIVATE_SEL(setWantsExtendedDynamicRangeContent_), wantsExtendedDynamicRangeContent);
}
_CA_INLINE CA::DynamicRange CA::Layer::preferredDynamicRange() const
{
return Object::sendMessage<CA::DynamicRange>(this, _CA_PRIVATE_SEL(preferredDynamicRange));
}
_CA_INLINE void CA::Layer::setPreferredDynamicRange(CA::DynamicRange preferredDynamicRange)
{
Object::sendMessage<void>(this, _CA_PRIVATE_SEL(setPreferredDynamicRange_), preferredDynamicRange);
}
_CA_INLINE CGFloat CA::Layer::contentsHeadroom() const
{
return Object::sendMessage<CGFloat>(this, _CA_PRIVATE_SEL(contentsHeadroom));
}
_CA_INLINE void CA::Layer::setContentsHeadroom(CGFloat contentsHeadroom)
{
Object::sendMessage<void>(this, _CA_PRIVATE_SEL(setContentsHeadroom_), contentsHeadroom);
}
_CA_INLINE bool CA::Layer::opaque() const
{
return Object::sendMessage<bool>(this, _CA_PRIVATE_SEL(opaque));
}
_CA_INLINE void CA::Layer::setOpaque(bool opaque)
{
Object::sendMessage<void>(this, _CA_PRIVATE_SEL(setOpaque_), opaque);
}
_CA_PRIVATE_DEF_STR(CA::DynamicRange, DynamicRangeAutomatic);
_CA_PRIVATE_DEF_STR(CA::DynamicRange, DynamicRangeStandard);
_CA_PRIVATE_DEF_STR(CA::DynamicRange, DynamicRangeConstrainedHigh);
_CA_PRIVATE_DEF_STR(CA::DynamicRange, DynamicRangeHigh);

View file

@ -32,13 +32,14 @@
#include "CADefines.hpp"
#include "CAMetalDrawable.hpp"
#include "CAPrivate.hpp"
#include "CALayer.hpp"
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
namespace CA
{
class MetalLayer : public NS::Referencing<MetalLayer>
class MetalLayer : public NS::Referencing<MetalLayer, Layer>
{
public:
static class MetalLayer* layer();

View file

@ -76,6 +76,7 @@ namespace Private
namespace Class
{
_CA_PRIVATE_DEF_CLS(CAMetalLayer);
_CA_PRIVATE_DEF_CLS(CALayer);
} // Class
} // Private
} // CA
@ -107,6 +108,8 @@ namespace Private
"allowsNextDrawableTimeout");
_CA_PRIVATE_DEF_SEL(colorspace,
"colorspace");
_CA_PRIVATE_DEF_SEL(contentsHeadroom,
"contentsHeadroom");
_CA_PRIVATE_DEF_SEL(device,
"device");
_CA_PRIVATE_DEF_SEL(displaySyncEnabled,
@ -121,14 +124,20 @@ namespace Private
"maximumDrawableCount");
_CA_PRIVATE_DEF_SEL(nextDrawable,
"nextDrawable");
_CA_PRIVATE_DEF_SEL(opaque,
"opaque");
_CA_PRIVATE_DEF_SEL(pixelFormat,
"pixelFormat");
_CA_PRIVATE_DEF_SEL(preferredDynamicRange,
"preferredDynamicRange");
_CA_PRIVATE_DEF_SEL(residencySet,
"residencySet");
_CA_PRIVATE_DEF_SEL(setAllowsNextDrawableTimeout_,
"setAllowsNextDrawableTimeout:");
_CA_PRIVATE_DEF_SEL(setColorspace_,
"setColorspace:");
_CA_PRIVATE_DEF_SEL(setContentsHeadroom_,
"setContentsHeadroom:");
_CA_PRIVATE_DEF_SEL(setDevice_,
"setDevice:");
_CA_PRIVATE_DEF_SEL(setDisplaySyncEnabled_,
@ -139,10 +148,19 @@ namespace Private
"setFramebufferOnly:");
_CA_PRIVATE_DEF_SEL(setMaximumDrawableCount_,
"setMaximumDrawableCount:");
_CA_PRIVATE_DEF_SEL(setOpaque_,
"setOpaque:");
_CA_PRIVATE_DEF_SEL(setPixelFormat_,
"setPixelFormat:");
_CA_PRIVATE_DEF_SEL(setPreferredDynamicRange_,
"setPreferredDynamicRange:");
_CA_PRIVATE_DEF_SEL(setWantsExtendedDynamicRangeContent_,
"setWantsExtendedDynamicRangeContent:");
_CA_PRIVATE_DEF_SEL(texture,
"texture");
_CA_PRIVATE_DEF_SEL(wantsExtendedDynamicRangeContent,
"wantsExtendedDynamicRangeContent");
} // Class
} // Private
} // CA

View file

@ -22,6 +22,7 @@
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
#include "CALayer.hpp"
#include "CAMetalDrawable.hpp"
#include "CAMetalLayer.hpp"

View file

@ -1,76 +1,288 @@
thirdparty/metal-cpp/Foundation/NSData.hpp | 6 ++++++
thirdparty/metal-cpp/Foundation/NSPrivate.hpp | 4 ++++
thirdparty/metal-cpp/Foundation/NSString.hpp | 7 +++++++
3 files changed, 17 insertions(+)
diff --git a/thirdparty/metal-cpp/Foundation/NSData.hpp b/thirdparty/metal-cpp/Foundation/NSData.hpp
index 3ad360609f..fbf3f20343 100644
--- a/thirdparty/metal-cpp/Foundation/NSData.hpp
+++ b/thirdparty/metal-cpp/Foundation/NSData.hpp
@@ -33,6 +33,7 @@ class Data : public Copying<Data>
{
diff -ruN original/Foundation/NSData.hpp patched/Foundation/NSData.hpp
--- original/Foundation/NSData.hpp 2026-02-16 15:06:52
+++ patched/Foundation/NSData.hpp 2026-02-16 15:06:52
@@ -34,6 +34,7 @@
public:
void* mutableBytes() const;
+ void* bytes() const;
UInteger length() const;
+ const void * bytes() const;
};
}
@@ -44,6 +45,11 @@ _NS_INLINE void* NS::Data::mutableBytes() const
return Object::sendMessage<void*>(this, _NS_PRIVATE_SEL(mutableBytes));
@@ -49,6 +50,11 @@
_NS_INLINE NS::UInteger NS::Data::length() const
{
return Object::sendMessage<UInteger>(this, _NS_PRIVATE_SEL(length));
+}
+
+_NS_INLINE const void * NS::Data::bytes() const
+{
+ return Object::sendMessage<const void *>(this, _NS_PRIVATE_SEL(bytes));
}
+_NS_INLINE void* NS::Data::bytes() const
+{
+ return Object::sendMessage<void*>(this, _NS_PRIVATE_SEL(bytes));
+}
+
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
diff -ruN original/Foundation/NSDefines.hpp patched/Foundation/NSDefines.hpp
--- original/Foundation/NSDefines.hpp 2026-02-16 15:06:52
+++ patched/Foundation/NSDefines.hpp 2026-02-16 15:06:52
@@ -22,6 +22,16 @@
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
_NS_INLINE NS::UInteger NS::Data::length() const
diff --git a/thirdparty/metal-cpp/Foundation/NSPrivate.hpp b/thirdparty/metal-cpp/Foundation/NSPrivate.hpp
index f8d87004f3..17909fbd2a 100644
--- a/thirdparty/metal-cpp/Foundation/NSPrivate.hpp
+++ b/thirdparty/metal-cpp/Foundation/NSPrivate.hpp
@@ -272,6 +272,8 @@ namespace Private
"initWithBytes:objCType:");
_NS_PRIVATE_DEF_SEL(initWithBytesNoCopy_length_encoding_freeWhenDone_,
"initWithBytesNoCopy:length:encoding:freeWhenDone:");
+ _NS_PRIVATE_DEF_SEL(initWithBytes_length_encoding_,
+ "initWithBytes:length:encoding:");
_NS_PRIVATE_DEF_SEL(initWithChar_,
"initWithChar:");
_NS_PRIVATE_DEF_SEL(initWithCoder_,
@@ -372,6 +374,8 @@ namespace Private
"methodSignatureForSelector:");
_NS_PRIVATE_DEF_SEL(mutableBytes,
"mutableBytes");
+// Forward declarations to avoid conflicts with Godot types (String, Object, Error)
+namespace NS {
+class Array;
+class Dictionary;
+class Error;
+class Object;
+class String;
+class URL;
+} // namespace NS
+
#define _NS_WEAK_IMPORT __attribute__((weak_import))
#ifdef METALCPP_SYMBOL_VISIBILITY_HIDDEN
#define _NS_EXPORT __attribute__((visibility("hidden")))
diff -ruN original/Foundation/NSObject.hpp patched/Foundation/NSObject.hpp
--- original/Foundation/NSObject.hpp 2026-02-16 15:06:52
+++ patched/Foundation/NSObject.hpp 2026-02-16 15:06:52
@@ -68,6 +68,12 @@
class String* description() const;
class String* debugDescription() const;
+ template <typename _Ret, typename... _Args>
+ static _Ret sendMessage(const void* pObj, SEL selector, _Args... args);
+
+ template <typename _Ret, typename... _Args>
+ static _Ret sendMessageSafe(const void* pObj, SEL selector, _Args... args);
+
protected:
friend class Referencing<Object, objc_object>;
@@ -84,10 +90,6 @@
static bool respondsToSelector(const void* pObj, SEL selector);
template <typename _Type>
static constexpr bool doesRequireMsgSendStret();
- template <typename _Ret, typename... _Args>
- static _Ret sendMessage(const void* pObj, SEL selector, _Args... args);
- template <typename _Ret, typename... _Args>
- static _Ret sendMessageSafe(const void* pObj, SEL selector, _Args... args);
private:
Object() = delete;
diff -ruN original/Foundation/NSPrivate.hpp patched/Foundation/NSPrivate.hpp
--- original/Foundation/NSPrivate.hpp 2026-02-16 15:06:52
+++ patched/Foundation/NSPrivate.hpp 2026-02-16 15:06:52
@@ -184,6 +184,8 @@
"bundleWithPath:");
_NS_PRIVATE_DEF_SEL(bundleWithURL_,
"bundleWithURL:");
+ _NS_PRIVATE_DEF_SEL(bytes,
+ "bytes");
_NS_PRIVATE_DEF_SEL(name,
"name");
_NS_PRIVATE_DEF_SEL(nextObject,
diff --git a/thirdparty/metal-cpp/Foundation/NSString.hpp b/thirdparty/metal-cpp/Foundation/NSString.hpp
index 07ba3f8d39..d4d0c52ec2 100644
--- a/thirdparty/metal-cpp/Foundation/NSString.hpp
+++ b/thirdparty/metal-cpp/Foundation/NSString.hpp
@@ -87,6 +87,7 @@ public:
String* init();
String* init(const String* pString);
String* init(const char* pString, StringEncoding encoding);
+ String* init(void* pBytes, UInteger len, StringEncoding encoding);
String* init(void* pBytes, UInteger len, StringEncoding encoding, bool freeBuffer);
_NS_PRIVATE_DEF_SEL(caseInsensitiveCompare_,
"caseInsensitiveCompare:");
_NS_PRIVATE_DEF_SEL(characterAtIndex_,
@@ -268,6 +270,8 @@
"initFileURLWithPath:");
_NS_PRIVATE_DEF_SEL(initWithBool_,
"initWithBool:");
+ _NS_PRIVATE_DEF_SEL(initWithBytes_length_encoding_,
+ "initWithBytes:length:encoding:");
_NS_PRIVATE_DEF_SEL(initWithBytes_objCType_,
"initWithBytes:objCType:");
_NS_PRIVATE_DEF_SEL(initWithBytesNoCopy_length_encoding_freeWhenDone_,
diff -ruN original/Foundation/NSString.hpp patched/Foundation/NSString.hpp
--- original/Foundation/NSString.hpp 2026-02-16 15:06:52
+++ patched/Foundation/NSString.hpp 2026-02-16 15:06:52
@@ -104,6 +104,7 @@
unichar character(UInteger index) const;
@@ -168,6 +169,12 @@ _NS_INLINE NS::String* NS::String::init(const char* pString, StringEncoding enco
String* stringByAppendingString(const String* pString) const;
ComparisonResult caseInsensitiveCompare(const String* pString) const;
+ NS::String* init(const void * bytes, NS::UInteger len, NS::StringEncoding encoding);
};
/// Create an NS::String* from a string literal.
@@ -250,6 +251,11 @@
_NS_INLINE NS::ComparisonResult NS::String::caseInsensitiveCompare(const String* pString) const
{
return Object::sendMessage<NS::ComparisonResult>(this, _NS_PRIVATE_SEL(caseInsensitiveCompare_), pString);
+}
+
+_NS_INLINE NS::String* NS::String::init(const void * bytes, NS::UInteger len, NS::StringEncoding encoding)
+{
+ return Object::sendMessage<NS::String*>(this, _NS_PRIVATE_SEL(initWithBytes_length_encoding_), bytes, len, encoding);
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
diff -ruN original/QuartzCore/CALayer.hpp patched/QuartzCore/CALayer.hpp
--- original/QuartzCore/CALayer.hpp 1970-01-01 11:00:00
+++ patched/QuartzCore/CALayer.hpp 2026-02-16 15:06:52
@@ -0,0 +1,79 @@
+#pragma once
+
+#include "CADefines.hpp"
+#include "CAPrivate.hpp"
+#include "../Foundation/NSObject.hpp"
+#include <CoreGraphics/CoreGraphics.h>
+
+namespace CA
+{
+using DynamicRange = NS::String*;
+
+_CA_CONST(DynamicRange, DynamicRangeAutomatic);
+_CA_CONST(DynamicRange, DynamicRangeStandard);
+_CA_CONST(DynamicRange, DynamicRangeConstrainedHigh);
+_CA_CONST(DynamicRange, DynamicRangeHigh);
+
+
+class Layer : public NS::Referencing<Layer>
+{
+public:
+ bool wantsExtendedDynamicRangeContent() const;
+ void setWantsExtendedDynamicRangeContent(bool wantsExtendedDynamicRangeContent);
+ CA::DynamicRange preferredDynamicRange() const;
+ void setPreferredDynamicRange(CA::DynamicRange preferredDynamicRange);
+ CGFloat contentsHeadroom() const;
+ void setContentsHeadroom(CGFloat contentsHeadroom);
+ bool opaque() const;
+ void setOpaque(bool opaque);
+
+};
+
+} // namespace CA
+
+// --- Inline implementations ---
+
+_CA_INLINE bool CA::Layer::wantsExtendedDynamicRangeContent() const
+{
+ return Object::sendMessage<bool>(this, _CA_PRIVATE_SEL(wantsExtendedDynamicRangeContent));
+}
+
+_CA_INLINE void CA::Layer::setWantsExtendedDynamicRangeContent(bool wantsExtendedDynamicRangeContent)
+{
+ Object::sendMessage<void>(this, _CA_PRIVATE_SEL(setWantsExtendedDynamicRangeContent_), wantsExtendedDynamicRangeContent);
+}
+
+_CA_INLINE CA::DynamicRange CA::Layer::preferredDynamicRange() const
+{
+ return Object::sendMessage<CA::DynamicRange>(this, _CA_PRIVATE_SEL(preferredDynamicRange));
+}
+
+_CA_INLINE void CA::Layer::setPreferredDynamicRange(CA::DynamicRange preferredDynamicRange)
+{
+ Object::sendMessage<void>(this, _CA_PRIVATE_SEL(setPreferredDynamicRange_), preferredDynamicRange);
+}
+
+_CA_INLINE CGFloat CA::Layer::contentsHeadroom() const
+{
+ return Object::sendMessage<CGFloat>(this, _CA_PRIVATE_SEL(contentsHeadroom));
+}
+
+_CA_INLINE void CA::Layer::setContentsHeadroom(CGFloat contentsHeadroom)
+{
+ Object::sendMessage<void>(this, _CA_PRIVATE_SEL(setContentsHeadroom_), contentsHeadroom);
+}
+
+_CA_INLINE bool CA::Layer::opaque() const
+{
+ return Object::sendMessage<bool>(this, _CA_PRIVATE_SEL(opaque));
+}
+
+_CA_INLINE void CA::Layer::setOpaque(bool opaque)
+{
+ Object::sendMessage<void>(this, _CA_PRIVATE_SEL(setOpaque_), opaque);
+}
+
+_CA_PRIVATE_DEF_STR(CA::DynamicRange, DynamicRangeAutomatic);
+_CA_PRIVATE_DEF_STR(CA::DynamicRange, DynamicRangeStandard);
+_CA_PRIVATE_DEF_STR(CA::DynamicRange, DynamicRangeConstrainedHigh);
+_CA_PRIVATE_DEF_STR(CA::DynamicRange, DynamicRangeHigh);
diff -ruN original/QuartzCore/CAMetalLayer.hpp patched/QuartzCore/CAMetalLayer.hpp
--- original/QuartzCore/CAMetalLayer.hpp 2026-02-16 15:06:52
+++ patched/QuartzCore/CAMetalLayer.hpp 2026-02-16 15:06:52
@@ -32,13 +32,14 @@
#include "CADefines.hpp"
#include "CAMetalDrawable.hpp"
#include "CAPrivate.hpp"
+#include "CALayer.hpp"
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
+_NS_INLINE NS::String* NS::String::init(void* pBytes, UInteger len, StringEncoding encoding)
+{
+ return Object::sendMessage<String*>(this, _NS_PRIVATE_SEL(initWithBytes_length_encoding_), pBytes, len, encoding);
+}
+//-------------------------------------------------------------------------------------------------------------------------------------------------------------
+
_NS_INLINE NS::String* NS::String::init(void* pBytes, UInteger len, StringEncoding encoding, bool freeBuffer)
namespace CA
{
return Object::sendMessage<String*>(this, _NS_PRIVATE_SEL(initWithBytesNoCopy_length_encoding_freeWhenDone_), pBytes, len, encoding, freeBuffer);
-class MetalLayer : public NS::Referencing<MetalLayer>
+class MetalLayer : public NS::Referencing<MetalLayer, Layer>
{
public:
static class MetalLayer* layer();
diff -ruN original/QuartzCore/CAPrivate.hpp patched/QuartzCore/CAPrivate.hpp
--- original/QuartzCore/CAPrivate.hpp 2026-02-16 15:06:52
+++ patched/QuartzCore/CAPrivate.hpp 2026-02-16 15:06:52
@@ -76,6 +76,7 @@
namespace Class
{
_CA_PRIVATE_DEF_CLS(CAMetalLayer);
+ _CA_PRIVATE_DEF_CLS(CALayer);
} // Class
} // Private
} // CA
@@ -107,6 +108,8 @@
"allowsNextDrawableTimeout");
_CA_PRIVATE_DEF_SEL(colorspace,
"colorspace");
+ _CA_PRIVATE_DEF_SEL(contentsHeadroom,
+ "contentsHeadroom");
_CA_PRIVATE_DEF_SEL(device,
"device");
_CA_PRIVATE_DEF_SEL(displaySyncEnabled,
@@ -121,14 +124,20 @@
"maximumDrawableCount");
_CA_PRIVATE_DEF_SEL(nextDrawable,
"nextDrawable");
+ _CA_PRIVATE_DEF_SEL(opaque,
+ "opaque");
_CA_PRIVATE_DEF_SEL(pixelFormat,
"pixelFormat");
+ _CA_PRIVATE_DEF_SEL(preferredDynamicRange,
+ "preferredDynamicRange");
_CA_PRIVATE_DEF_SEL(residencySet,
"residencySet");
_CA_PRIVATE_DEF_SEL(setAllowsNextDrawableTimeout_,
"setAllowsNextDrawableTimeout:");
_CA_PRIVATE_DEF_SEL(setColorspace_,
"setColorspace:");
+ _CA_PRIVATE_DEF_SEL(setContentsHeadroom_,
+ "setContentsHeadroom:");
_CA_PRIVATE_DEF_SEL(setDevice_,
"setDevice:");
_CA_PRIVATE_DEF_SEL(setDisplaySyncEnabled_,
@@ -139,10 +148,19 @@
"setFramebufferOnly:");
_CA_PRIVATE_DEF_SEL(setMaximumDrawableCount_,
"setMaximumDrawableCount:");
+ _CA_PRIVATE_DEF_SEL(setOpaque_,
+ "setOpaque:");
_CA_PRIVATE_DEF_SEL(setPixelFormat_,
"setPixelFormat:");
+ _CA_PRIVATE_DEF_SEL(setPreferredDynamicRange_,
+ "setPreferredDynamicRange:");
+ _CA_PRIVATE_DEF_SEL(setWantsExtendedDynamicRangeContent_,
+ "setWantsExtendedDynamicRangeContent:");
_CA_PRIVATE_DEF_SEL(texture,
"texture");
+ _CA_PRIVATE_DEF_SEL(wantsExtendedDynamicRangeContent,
+ "wantsExtendedDynamicRangeContent");
+
} // Class
} // Private
} // CA
diff -ruN original/QuartzCore/QuartzCore.hpp patched/QuartzCore/QuartzCore.hpp
--- original/QuartzCore/QuartzCore.hpp 2026-02-16 15:06:52
+++ patched/QuartzCore/QuartzCore.hpp 2026-02-16 15:06:52
@@ -22,6 +22,7 @@
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
+#include "CALayer.hpp"
#include "CAMetalDrawable.hpp"
#include "CAMetalLayer.hpp"

View file

@ -58,39 +58,4 @@ else
echo " Warning: $PATCH_DIR not found"
fi
# Patch 1: Add forward declarations to NSDefines.hpp to avoid conflicts with
# Godot's global types (String, Object, Error).
#
# Without this patch, metal-cpp's "class String*" forward declarations conflict
# with Godot's ::String, ::Object, and ::Error types.
NSDEFINES="$SCRIPT_DIR/Foundation/NSDefines.hpp"
if [ -f "$NSDEFINES" ]; then
# Check if patch already applied
if grep -q "Forward declarations to avoid conflicts with Godot" "$NSDEFINES"; then
echo " NSDefines.hpp: already patched"
else
# Find the line with #pragma once and insert after the next separator
sed -i '' '/^#pragma once$/,/^\/\/---/{
/^\/\/---/ {
a\
\
// Forward declarations to avoid conflicts with Godot types (String, Object, Error)\
namespace NS {\
class Array;\
class Dictionary;\
class Error;\
class Object;\
class String;\
class URL;\
} // namespace NS
}
}' "$NSDEFINES"
echo " NSDefines.hpp: patched"
fi
else
echo " Warning: $NSDEFINES not found"
fi
echo "Done."