X Tutup
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/classes/DisplayServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2751,7 +2751,7 @@
Display server supports interaction with screen reader or Braille display. [b]Linux (X11/Wayland), macOS, Windows[/b]
</constant>
<constant name="FEATURE_HDR_OUTPUT" value="35" enum="Feature">
Display server supports HDR output. [b]Windows[/b]
Display server supports HDR output. [b]macOS, iOS, visionOS, Windows[/b]
</constant>
<constant name="ROLE_UNKNOWN" value="0" enum="AccessibilityRole">
Unknown or custom role.
Expand Down
28 changes: 28 additions & 0 deletions drivers/apple_embedded/display_server_apple_embedded.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
class InputEvent;
class NativeMenu;

/// "Embedded" as in "Embedded Device".
class DisplayServerAppleEmbedded : public DisplayServer {
GDSOFTCLASS(DisplayServerAppleEmbedded, DisplayServer);

Expand Down Expand Up @@ -87,7 +88,16 @@ class DisplayServerAppleEmbedded : public DisplayServer {

void initialize_tts() const;

bool edr_requested = false;
void _update_hdr_output();
float _calculate_current_reference_luminance() const;

protected:
virtual bool _screen_hdr_is_supported() const { return false; }
virtual float _screen_potential_edr_headroom() const { return 1.0f; }
virtual float _screen_current_edr_headroom() const { return 1.0f; }
float hardware_reference_luminance_nits = 100.0f;

DisplayServerAppleEmbedded(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
~DisplayServerAppleEmbedded();

Expand Down Expand Up @@ -230,6 +240,24 @@ class DisplayServerAppleEmbedded : public DisplayServer {
virtual void screen_set_keep_on(bool p_enable) override;
virtual bool screen_is_kept_on() const override;

// MARK: - HDR / EDR

void current_edr_headroom_changed();
virtual bool window_is_hdr_output_supported(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_request_hdr_output(const bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
virtual bool window_is_hdr_output_requested(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual bool window_is_hdr_output_enabled(WindowID p_window = MAIN_WINDOW_ID) const override;

virtual void window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual float window_get_hdr_output_reference_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual float window_get_hdr_output_current_reference_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;

virtual void window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual float window_get_hdr_output_max_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual float window_get_hdr_output_current_max_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;

virtual float window_get_output_max_linear_value(WindowID p_window = MAIN_WINDOW_ID) const override;

void resize_window(CGSize size);
virtual void swap_buffers() override {}

Expand Down
101 changes: 101 additions & 0 deletions drivers/apple_embedded/display_server_apple_embedded.mm
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@
// case FEATURE_NATIVE_ICON:
// case FEATURE_WINDOW_TRANSPARENCY:
case FEATURE_CLIPBOARD:
case FEATURE_HDR_OUTPUT:
case FEATURE_KEEP_SCREEN_ON:
case FEATURE_ORIENTATION:
case FEATURE_TOUCHSCREEN:
Expand Down Expand Up @@ -820,6 +821,106 @@ _FORCE_INLINE_ int _convert_utf32_offset_to_utf16(const String &p_existing_text,
return DisplayServer::VSYNC_ENABLED;
}

// MARK: - HDR / EDR

void DisplayServerAppleEmbedded::_update_hdr_output() {
#ifdef RD_ENABLED
if (!rendering_context) {
return;
}

bool desired = edr_requested && _screen_hdr_is_supported();
if (rendering_context->window_get_hdr_output_enabled(MAIN_WINDOW_ID) != desired) {
rendering_context->window_set_hdr_output_enabled(MAIN_WINDOW_ID, desired);
}

float reference_luminance = _calculate_current_reference_luminance();
rendering_context->window_set_hdr_output_reference_luminance(MAIN_WINDOW_ID, reference_luminance);

float max_luminance = _screen_potential_edr_headroom() * hardware_reference_luminance_nits;
rendering_context->window_set_hdr_output_max_luminance(MAIN_WINDOW_ID, max_luminance);
#endif
}

void DisplayServerAppleEmbedded::current_edr_headroom_changed() {
_update_hdr_output();
}

bool DisplayServerAppleEmbedded::window_is_hdr_output_supported(WindowID p_window) const {
Copy link
Contributor

@allenwp allenwp Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that the functions in this file do not have _THREAD_SAFE_METHOD_, but those in display_server_macos_base do. Is there a reason for this difference? (I know basically nothing about Godot multithreading, so I can't comment on whether either is correct.)

This comment applies to all of the other window_... functions as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, as I didn't write this code originally. @bruvzg may be able to comment on this. I'm not sure what the expectation is for the various APIs on the DisplayServer class and whether it is expected this class can be called from multiple threads (it would appear so).

#if defined(RD_ENABLED)
if (rendering_device && !rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) {
return false;
}
#endif
return _screen_hdr_is_supported();
}

void DisplayServerAppleEmbedded::window_request_hdr_output(const bool p_enabled, WindowID p_window) {
#if defined(RD_ENABLED)
ERR_FAIL_COND_MSG(p_enabled && rendering_device && !rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT), "HDR output is not supported by the rendering device.");
#endif

edr_requested = p_enabled;
_update_hdr_output();
}

bool DisplayServerAppleEmbedded::window_is_hdr_output_requested(WindowID p_window) const {
return edr_requested;
}

bool DisplayServerAppleEmbedded::window_is_hdr_output_enabled(WindowID p_window) const {
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_enabled(p_window);
}
#endif
return false;
}

void DisplayServerAppleEmbedded::window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window) {
ERR_PRINT_ONCE("Manually setting reference white luminance is not supported on Apple devices, as they provide a user-facing brightness setting that directly controls reference white luminance.");
}

float DisplayServerAppleEmbedded::window_get_hdr_output_reference_luminance(WindowID p_window) const {
return -1.0f; // Always auto-adjusted by the OS on Apple platforms.
}

float DisplayServerAppleEmbedded::_calculate_current_reference_luminance() const {
float potential = _screen_potential_edr_headroom();
float current = _screen_current_edr_headroom();
return potential * hardware_reference_luminance_nits / current;
}

float DisplayServerAppleEmbedded::window_get_hdr_output_current_reference_luminance(WindowID p_window) const {
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_reference_luminance(p_window);
}
#endif
return 200.0f;
}

void DisplayServerAppleEmbedded::window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window) {
ERR_PRINT_ONCE("Manually setting max luminance is not supported on Apple embedded devices as they provide accurate max luminance values for their built-in screens.");
}

float DisplayServerAppleEmbedded::window_get_hdr_output_max_luminance(WindowID p_window) const {
return -1.0f;
}

float DisplayServerAppleEmbedded::window_get_hdr_output_current_max_luminance(WindowID p_window) const {
return _screen_potential_edr_headroom() * hardware_reference_luminance_nits;
}

float DisplayServerAppleEmbedded::window_get_output_max_linear_value(WindowID p_window) const {
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_output_max_linear_value(p_window);
}
#endif
return 1.0f;
}

void DisplayServerAppleEmbedded::set_native_icon(const String &p_filename) {
// Not supported on Apple embedded platforms.
}
Expand Down
16 changes: 16 additions & 0 deletions drivers/apple_embedded/godot_view_apple_embedded.mm
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@

@interface GDTView () {
UITouch *godot_touches[max_touches];
CGFloat last_edr_headroom;
}

@property(assign, nonatomic) BOOL isActive;
Expand Down Expand Up @@ -121,6 +122,7 @@ - (void)dealloc {
- (void)godot_commonInit {
self.preferredFrameRate = 60;
self.useCADisplayLink = bool(GLOBAL_DEF("display.AppleEmbedded/use_cadisplaylink", true)) ? YES : NO;
last_edr_headroom = 0.0;

#if !defined(VISIONOS_ENABLED)
self.contentScaleFactor = [UIScreen mainScreen].scale;
Expand Down Expand Up @@ -249,6 +251,20 @@ - (void)drawView {
}

[self handleMotion];

#if !defined(VISIONOS_ENABLED)
if (@available(iOS 16.0, *)) {
CGFloat edr_headroom = UIScreen.mainScreen.currentEDRHeadroom;
if (last_edr_headroom != edr_headroom) {
last_edr_headroom = edr_headroom;
if (DisplayServerAppleEmbedded::get_singleton()) {
DisplayServerAppleEmbedded::get_singleton()->current_edr_headroom_changed();
}
}
}

#endif

[self.renderer renderOnView:self];

[self.renderingLayer stopRenderDisplayLayer];
Expand Down
39 changes: 29 additions & 10 deletions drivers/metal/rendering_context_driver_metal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) SurfaceLayer : public Re
layer = nullptr;
}

Error resize(uint32_t p_desired_framebuffer_count) override final {
Error resize(uint32_t p_desired_framebuffer_count, RDD::DataFormat &r_format, RDD::ColorSpace &r_color_space) override final {
if (width == 0 || height == 0) {
// Very likely the window is minimized, don't create a swap chain.
return ERR_SKIP;
Expand Down Expand Up @@ -155,6 +155,28 @@ class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) SurfaceLayer : public Re
frame_buffers[i].set_texture_count(1);
}

if (hdr_output) {
layer->setWantsExtendedDynamicRangeContent(true);
CGColorSpaceRef color_space = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
layer->setColorspace(color_space);
CGColorSpaceRelease(color_space);
layer->setPixelFormat(MTL::PixelFormatRGBA16Float);

r_color_space = RDD::COLOR_SPACE_REC709_LINEAR;
r_format = RDD::DATA_FORMAT_R16G16B16A16_SFLOAT;
pixel_format = MTL::PixelFormatRGBA16Float;
} else {
layer->setWantsExtendedDynamicRangeContent(false);
CGColorSpaceRef color_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
layer->setColorspace(color_space);
CGColorSpaceRelease(color_space);
layer->setPixelFormat(MTL::PixelFormatBGRA8Unorm);

r_color_space = RDD::COLOR_SPACE_REC709_NONLINEAR_SRGB;
r_format = RDD::DATA_FORMAT_B8G8R8A8_UNORM;
pixel_format = MTL::PixelFormatBGRA8Unorm;
}

return OK;
}

Expand Down Expand Up @@ -256,7 +278,7 @@ class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) SurfaceOffscreen : publi
memdelete_arr(frame_buffers);
}

Error resize(uint32_t p_desired_framebuffer_count) override final {
Error resize(uint32_t p_desired_framebuffer_count, RDD::DataFormat &r_format, RDD::ColorSpace &r_color_space) override final {
if (width == 0 || height == 0) {
// Very likely the window is minimized, don't create a swap chain.
return ERR_SKIP;
Expand Down Expand Up @@ -379,13 +401,12 @@ DisplayServer::VSyncMode RenderingContextDriverMetal::surface_get_vsync_mode(Sur

void RenderingContextDriverMetal::surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_output = p_enabled;
surface->needs_resize = true;
surface->set_hdr_output_enabled(p_enabled);
}

bool RenderingContextDriverMetal::surface_get_hdr_output_enabled(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_output;
return surface->is_hdr_output_enabled();
}

void RenderingContextDriverMetal::surface_set_hdr_output_reference_luminance(SurfaceID p_surface, float p_reference_luminance) {
Expand All @@ -394,7 +415,7 @@ void RenderingContextDriverMetal::surface_set_hdr_output_reference_luminance(Sur
}

float RenderingContextDriverMetal::surface_get_hdr_output_reference_luminance(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
const Surface *surface = (Surface *)(p_surface);
return surface->hdr_reference_luminance;
}

Expand All @@ -404,18 +425,16 @@ void RenderingContextDriverMetal::surface_set_hdr_output_max_luminance(SurfaceID
}

float RenderingContextDriverMetal::surface_get_hdr_output_max_luminance(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
const Surface *surface = (Surface *)(p_surface);
return surface->hdr_max_luminance;
}

void RenderingContextDriverMetal::surface_set_hdr_output_linear_luminance_scale(SurfaceID p_surface, float p_linear_luminance_scale) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_linear_luminance_scale = p_linear_luminance_scale;
}

float RenderingContextDriverMetal::surface_get_hdr_output_linear_luminance_scale(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_linear_luminance_scale;
return surface->hdr_reference_luminance;
}

float RenderingContextDriverMetal::surface_get_hdr_output_max_value(SurfaceID p_surface) const {
Expand Down
20 changes: 15 additions & 5 deletions drivers/metal/rendering_context_driver_metal.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,22 +91,32 @@ class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) RenderingContextDriverMe
uint32_t width = 0;
uint32_t height = 0;
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
bool needs_resize = false;
double present_minimum_duration = 0.0;
MTL::PixelFormat pixel_format = MTL::PixelFormatBGRA8Unorm;

bool hdr_output = false;
// BT.2408 recommendation of 203 nits for HDR Reference White, rounded to 200
// to be a more pleasant player-facing value.
float hdr_reference_luminance = 200.0f;
float hdr_max_luminance = 1000.0f;
float hdr_linear_luminance_scale = 100.0f;
bool needs_resize = false;
bool hdr_output = false;

Surface(MTL::Device *p_device) :
device(p_device) {}
virtual ~Surface() = default;

MTL::PixelFormat get_pixel_format() const { return MTL::PixelFormatBGRA8Unorm; }
virtual Error resize(uint32_t p_desired_framebuffer_count) = 0;
MTL::PixelFormat get_pixel_format() const { return pixel_format; }
void set_hdr_output_enabled(bool p_enabled) {
if (hdr_output != p_enabled) {
hdr_output = p_enabled;
needs_resize = true;
}
}

bool is_hdr_output_enabled() const {
return hdr_output;
}
virtual Error resize(uint32_t p_desired_framebuffer_count, RDD::DataFormat &r_format, RDD::ColorSpace &r_color_space) = 0;
virtual RDD::FramebufferID acquire_next_frame_buffer() = 0;
virtual void present(MTL3::MDCommandBuffer *p_cmd_buffer) = 0;
virtual MTL::Drawable *next_drawable() = 0;
Expand Down
Loading
X Tutup