diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 37a2608c102c..31edf487c5d8 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1467,6 +1467,8 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("display/window/size/transparent", false);
GLOBAL_DEF("display/window/size/extend_to_title", false);
GLOBAL_DEF("display/window/size/no_focus", false);
+ GLOBAL_DEF("display/window/hdr/enabled", false);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "display/window/hdr/max_luminance", PROPERTY_HINT_RANGE, "0,1500,1,or_greater"), 1000.0f);
GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_width_override", PROPERTY_HINT_RANGE, "0,7680,1,or_greater"), 0); // 8K resolution
GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 0); // 8K resolution
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 0bed5288bdb6..b8c944240c25 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -1448,6 +1448,20 @@
Returns the current value of the given window's [param flag].
+
+
+
+
+ Returns whether HDR output is requested for the given window.
+
+
+
+
+
+
+ Returns the maximum luminance in nits (cd/m²) set for HDR output for the given window.
+
+
@@ -1615,6 +1629,24 @@
Enables or disables the given window's given [param flag]. See [enum WindowFlags] for possible values and their behavior.
+
+
+
+
+
+ Sets whether HDR output should be enabled for the window specified by [param window_id].
+ Only available on platforms that support HDR output, have HDR enabled in the system settings, and have a compatible display connected.
+
+
+
+
+
+
+
+ Sets the maximum luminance of the display in nits (cd/m²) when HDR is enabled.
+ This is used to scale the HDR effect to avoid clipping.
+
+
@@ -1883,6 +1915,9 @@
Display server supports spawning dialogs for selecting files or directories using the operating system's native look-and-feel. See [method file_dialog_show] and [method file_dialog_with_options_show]. [b]Windows, macOS, Linux (X11/Wayland)[/b]
+
+ Display server supports HDR output. [b]Windows[/b]
+
Makes the mouse cursor visible if it is hidden.
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 22c25dd5e57e..1ca626f137b0 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -821,6 +821,14 @@
The default screen orientation to use on mobile devices. See [enum DisplayServer.ScreenOrientation] for possible values.
[b]Note:[/b] When set to a portrait orientation, this project setting does not flip the project resolution's width and height automatically. Instead, you have to set [member display/window/size/viewport_width] and [member display/window/size/viewport_height] accordingly.
+
+ If [code]true[/code], enables HDR output on supported platforms, falling back to SDR if not supported.
+ Only available on platforms that support HDR output, have HDR enabled in the system settings, and have a compatible display connected.
+
+
+ Sets the maximum luminance of the display in nits (cd/m²) when HDR is enabled.
+ This is used to scale the HDR effect to avoid clipping.
+
If [code]true[/code], iOS devices that support high refresh rate/"ProMotion" will be allowed to render at up to 120 frames per second.
diff --git a/drivers/d3d12/rendering_context_driver_d3d12.cpp b/drivers/d3d12/rendering_context_driver_d3d12.cpp
index d01ae5a73f23..9d6d66529fe6 100644
--- a/drivers/d3d12/rendering_context_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_context_driver_d3d12.cpp
@@ -271,6 +271,26 @@ DisplayServer::VSyncMode RenderingContextDriverD3D12::surface_get_vsync_mode(Sur
return surface->vsync_mode;
}
+void RenderingContextDriverD3D12::surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) {
+ Surface *surface = (Surface *)(p_surface);
+ surface->hdr_output = p_enabled;
+}
+
+bool RenderingContextDriverD3D12::surface_get_hdr_output_enabled(SurfaceID p_surface) const {
+ Surface *surface = (Surface *)(p_surface);
+ return surface->hdr_output;
+}
+
+void RenderingContextDriverD3D12::surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) {
+ Surface *surface = (Surface *)(p_surface);
+ surface->hdr_max_luminance = p_max_luminance;
+}
+
+float RenderingContextDriverD3D12::surface_get_hdr_output_max_luminance(SurfaceID p_surface) const {
+ Surface *surface = (Surface *)(p_surface);
+ return surface->hdr_max_luminance;
+}
+
uint32_t RenderingContextDriverD3D12::surface_get_width(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->width;
diff --git a/drivers/d3d12/rendering_context_driver_d3d12.h b/drivers/d3d12/rendering_context_driver_d3d12.h
index a2d828ded1ac..342b4d0c9d4c 100644
--- a/drivers/d3d12/rendering_context_driver_d3d12.h
+++ b/drivers/d3d12/rendering_context_driver_d3d12.h
@@ -95,6 +95,10 @@ class RenderingContextDriverD3D12 : public RenderingContextDriver {
virtual void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) override;
virtual void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) override;
virtual DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const override;
+ virtual void surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) override;
+ virtual bool surface_get_hdr_output_enabled(SurfaceID p_surface) const override;
+ virtual void surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) override;
+ virtual float surface_get_hdr_output_max_luminance(SurfaceID p_surface) const override;
virtual uint32_t surface_get_width(SurfaceID p_surface) const override;
virtual uint32_t surface_get_height(SurfaceID p_surface) const override;
virtual void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) override;
@@ -113,6 +117,8 @@ class RenderingContextDriverD3D12 : public RenderingContextDriver {
uint32_t width = 0;
uint32_t height = 0;
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
+ bool hdr_output = false;
+ float hdr_max_luminance = 0.0f;
bool needs_resize = false;
};
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index 122585e59533..20272274215c 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -2616,6 +2616,10 @@ RDD::DataFormat RenderingDeviceDriverD3D12::swap_chain_get_format(SwapChainID p_
return swap_chain->data_format;
}
+RDD::ColorSpace RenderingDeviceDriverD3D12::swap_chain_get_color_space(SwapChainID p_swap_chain) {
+ return RDD::COLOR_SPACE_SRGB_NONLINEAR;
+}
+
void RenderingDeviceDriverD3D12::swap_chain_free(SwapChainID p_swap_chain) {
SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
_swap_chain_release(swap_chain);
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h
index 61b1498755f6..150e02408dbf 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.h
+++ b/drivers/d3d12/rendering_device_driver_d3d12.h
@@ -523,6 +523,7 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override;
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override;
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override;
+ virtual ColorSpace swap_chain_get_color_space(SwapChainID p_swap_chain) override;
virtual void swap_chain_free(SwapChainID p_swap_chain) override;
/*********************/
diff --git a/drivers/vulkan/rendering_context_driver_vulkan.cpp b/drivers/vulkan/rendering_context_driver_vulkan.cpp
index fe2ff5e0daeb..b75de3b4271c 100644
--- a/drivers/vulkan/rendering_context_driver_vulkan.cpp
+++ b/drivers/vulkan/rendering_context_driver_vulkan.cpp
@@ -657,6 +657,27 @@ DisplayServer::VSyncMode RenderingContextDriverVulkan::surface_get_vsync_mode(Su
return surface->vsync_mode;
}
+void RenderingContextDriverVulkan::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;
+}
+
+bool RenderingContextDriverVulkan::surface_get_hdr_output_enabled(SurfaceID p_surface) const {
+ Surface *surface = (Surface *)(p_surface);
+ return surface->hdr_output;
+}
+
+void RenderingContextDriverVulkan::surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) {
+ Surface *surface = (Surface *)(p_surface);
+ surface->hdr_max_luminance = p_max_luminance;
+}
+
+float RenderingContextDriverVulkan::surface_get_hdr_output_max_luminance(SurfaceID p_surface) const {
+ Surface *surface = (Surface *)(p_surface);
+ return surface->hdr_max_luminance;
+}
+
uint32_t RenderingContextDriverVulkan::surface_get_width(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->width;
diff --git a/drivers/vulkan/rendering_context_driver_vulkan.h b/drivers/vulkan/rendering_context_driver_vulkan.h
index f1d4021e3225..034f970e9aec 100644
--- a/drivers/vulkan/rendering_context_driver_vulkan.h
+++ b/drivers/vulkan/rendering_context_driver_vulkan.h
@@ -130,6 +130,10 @@ class RenderingContextDriverVulkan : public RenderingContextDriver {
virtual void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) override;
virtual void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) override;
virtual DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const override;
+ virtual void surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) override;
+ virtual bool surface_get_hdr_output_enabled(SurfaceID p_surface) const override;
+ virtual void surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) override;
+ virtual float surface_get_hdr_output_max_luminance(SurfaceID p_surface) const override;
virtual uint32_t surface_get_width(SurfaceID p_surface) const override;
virtual uint32_t surface_get_height(SurfaceID p_surface) const override;
virtual void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) override;
@@ -143,6 +147,8 @@ class RenderingContextDriverVulkan : public RenderingContextDriver {
uint32_t width = 0;
uint32_t height = 0;
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
+ bool hdr_output = false;
+ float hdr_max_luminance = 0.0f;
bool needs_resize = false;
};
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp
index 97fd156584cc..98caa0bbfd23 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp
@@ -2473,6 +2473,80 @@ void RenderingDeviceDriverVulkan::command_buffer_execute_secondary(CommandBuffer
/**** SWAP CHAIN ****/
/********************/
+bool RenderingDeviceDriverVulkan::_determine_swap_chain_format(RenderingContextDriver::SurfaceID p_surface, VkFormat &r_format, VkColorSpaceKHR &r_color_space) {
+ DEV_ASSERT(p_surface != 0);
+
+ RenderingContextDriverVulkan::Surface *surface = (RenderingContextDriverVulkan::Surface *)(p_surface);
+ const RenderingContextDriverVulkan::Functions &functions = context_driver->functions_get();
+
+ // Retrieve the formats supported by the surface.
+ uint32_t format_count = 0;
+ VkResult err = functions.GetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface->vk_surface, &format_count, nullptr);
+ ERR_FAIL_COND_V(err != VK_SUCCESS, false);
+
+ TightLocalVector formats;
+ formats.resize(format_count);
+ err = functions.GetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface->vk_surface, &format_count, formats.ptr());
+ ERR_FAIL_COND_V(err != VK_SUCCESS, false);
+
+ // If the format list includes just one entry of VK_FORMAT_UNDEFINED, the surface has no preferred format.
+ if (format_count == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
+ r_format = VK_FORMAT_B8G8R8A8_UNORM;
+ r_color_space = formats[0].colorSpace;
+ return VK_SUCCESS;
+ }
+
+ // If the surface requests HDR output, try to get an HDR format.
+ if (context_driver->surface_get_hdr_output_enabled(p_surface)) {
+ // Use one of the supported formats, prefer A2B10G10R10_UNORM_PACK32.
+ const VkFormat preferred_format = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
+ const VkFormat second_format = VK_FORMAT_A2R10G10B10_UNORM_PACK32;
+
+ // Only HDR10 is supported for now.
+ const VkColorSpaceKHR preferred_color_space = VK_COLOR_SPACE_HDR10_ST2084_EXT;
+
+ // Search for a valid format.
+ for (uint32_t i = 0; i < format_count; i++) {
+ if (formats[i].colorSpace == preferred_color_space && (formats[i].format == preferred_format || formats[i].format == second_format)) {
+ r_format = formats[i].format;
+ r_color_space = formats[i].colorSpace;
+
+ // This is the preferred format, stop searching.
+ if (formats[i].format == preferred_format) {
+ return true;
+ }
+ }
+ }
+
+ WARN_PRINT("HDR output requested but no HDR compatible format was found, falling back to SDR.");
+ }
+
+ // If HDR output was not requested or we failed to find an HDR compatible format above, try to get an SDR format.
+ {
+ // Use one of the supported formats, prefer B8G8R8A8_UNORM.
+ const VkFormat preferred_format = VK_FORMAT_B8G8R8A8_UNORM;
+ const VkFormat second_format = VK_FORMAT_R8G8B8A8_UNORM;
+
+ // Only SRGB is supported and should be available everywhere.
+ const VkColorSpaceKHR preferred_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
+
+ for (uint32_t i = 0; i < format_count; i++) {
+ if (formats[i].colorSpace == preferred_color_space && (formats[i].format == preferred_format || formats[i].format == second_format)) {
+ r_format = formats[i].format;
+ r_color_space = formats[i].colorSpace;
+
+ // This is the preferred format, stop searching.
+ if (formats[i].format == preferred_format) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // No formats are supported.
+ return false;
+}
+
void RenderingDeviceDriverVulkan::_swap_chain_release(SwapChain *swap_chain) {
// Destroy views and framebuffers associated to the swapchain's images.
for (FramebufferID framebuffer : swap_chain->framebuffers) {
@@ -2504,43 +2578,13 @@ void RenderingDeviceDriverVulkan::_swap_chain_release(SwapChain *swap_chain) {
RenderingDeviceDriver::SwapChainID RenderingDeviceDriverVulkan::swap_chain_create(RenderingContextDriver::SurfaceID p_surface) {
DEV_ASSERT(p_surface != 0);
- RenderingContextDriverVulkan::Surface *surface = (RenderingContextDriverVulkan::Surface *)(p_surface);
- const RenderingContextDriverVulkan::Functions &functions = context_driver->functions_get();
-
- // Retrieve the formats supported by the surface.
- uint32_t format_count = 0;
- VkResult err = functions.GetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface->vk_surface, &format_count, nullptr);
- ERR_FAIL_COND_V(err != VK_SUCCESS, SwapChainID());
-
- TightLocalVector formats;
- formats.resize(format_count);
- err = functions.GetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface->vk_surface, &format_count, formats.ptr());
- ERR_FAIL_COND_V(err != VK_SUCCESS, SwapChainID());
-
+ // Determine the format and color space for the swap chain.
VkFormat format = VK_FORMAT_UNDEFINED;
VkColorSpaceKHR color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
- if (format_count == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
- // If the format list includes just one entry of VK_FORMAT_UNDEFINED, the surface has no preferred format.
- format = VK_FORMAT_B8G8R8A8_UNORM;
- color_space = formats[0].colorSpace;
- } else if (format_count > 0) {
- // Use one of the supported formats, prefer B8G8R8A8_UNORM.
- const VkFormat preferred_format = VK_FORMAT_B8G8R8A8_UNORM;
- const VkFormat second_format = VK_FORMAT_R8G8B8A8_UNORM;
- for (uint32_t i = 0; i < format_count; i++) {
- if (formats[i].format == preferred_format || formats[i].format == second_format) {
- format = formats[i].format;
- if (formats[i].format == preferred_format) {
- // This is the preferred format, stop searching.
- break;
- }
- }
- }
+ if (!_determine_swap_chain_format(p_surface, format, color_space)) {
+ ERR_FAIL_V_MSG(SwapChainID(), "Surface did not return any valid formats.");
}
- // No formats are supported.
- ERR_FAIL_COND_V_MSG(format == VK_FORMAT_UNDEFINED, SwapChainID(), "Surface did not return any valid formats.");
-
// Create the render pass for the chosen format.
VkAttachmentDescription2KHR attachment = {};
attachment.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR;
@@ -2571,7 +2615,7 @@ RenderingDeviceDriver::SwapChainID RenderingDeviceDriverVulkan::swap_chain_creat
pass_info.pSubpasses = &subpass;
VkRenderPass render_pass = VK_NULL_HANDLE;
- err = _create_render_pass(vk_device, &pass_info, nullptr, &render_pass);
+ VkResult err = _create_render_pass(vk_device, &pass_info, nullptr, &render_pass);
ERR_FAIL_COND_V(err != VK_SUCCESS, SwapChainID());
SwapChain *swap_chain = memnew(SwapChain);
@@ -2700,6 +2744,16 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
has_comp_alpha[(uint64_t)p_cmd_queue.id] = (composite_alpha != VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR);
}
+ // Determine the format and color space for the swap chain.
+ VkFormat format = VK_FORMAT_UNDEFINED;
+ VkColorSpaceKHR color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
+ if (!_determine_swap_chain_format(swap_chain->surface, format, color_space)) {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Surface did not return any valid formats.");
+ } else {
+ swap_chain->format = format;
+ swap_chain->color_space = color_space;
+ }
+
VkSwapchainCreateInfoKHR swap_create_info = {};
swap_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swap_create_info.surface = surface->vk_surface;
@@ -2851,12 +2905,31 @@ RDD::DataFormat RenderingDeviceDriverVulkan::swap_chain_get_format(SwapChainID p
return DATA_FORMAT_B8G8R8A8_UNORM;
case VK_FORMAT_R8G8B8A8_UNORM:
return DATA_FORMAT_R8G8B8A8_UNORM;
+ case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
+ return DATA_FORMAT_A2B10G10R10_UNORM_PACK32;
+ case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
+ return DATA_FORMAT_A2R10G10B10_UNORM_PACK32;
default:
DEV_ASSERT(false && "Unknown swap chain format.");
return DATA_FORMAT_MAX;
}
}
+RDD::ColorSpace RenderingDeviceDriverVulkan::swap_chain_get_color_space(SwapChainID p_swap_chain) {
+ DEV_ASSERT(p_swap_chain.id != 0);
+
+ SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
+ switch (swap_chain->color_space) {
+ case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
+ return COLOR_SPACE_SRGB_NONLINEAR;
+ case VK_COLOR_SPACE_HDR10_ST2084_EXT:
+ return COLOR_SPACE_HDR10_ST2084;
+ default:
+ DEV_ASSERT(false && "Unknown swap chain color space.");
+ return COLOR_SPACE_MAX;
+ }
+}
+
void RenderingDeviceDriverVulkan::swap_chain_free(SwapChainID p_swap_chain) {
DEV_ASSERT(p_swap_chain.id != 0);
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h
index 6847ae00bee4..313142d23b0e 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.h
+++ b/drivers/vulkan/rendering_device_driver_vulkan.h
@@ -335,6 +335,7 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver {
uint32_t image_index = 0;
};
+ bool _determine_swap_chain_format(RenderingContextDriver::SurfaceID p_surface, VkFormat &r_format, VkColorSpaceKHR &r_color_space);
void _swap_chain_release(SwapChain *p_swap_chain);
public:
@@ -343,6 +344,7 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver {
virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override final;
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override final;
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override final;
+ virtual ColorSpace swap_chain_get_color_space(SwapChainID p_swap_chain) override final;
virtual void swap_chain_free(SwapChainID p_swap_chain) override final;
/*********************/
diff --git a/main/main.cpp b/main/main.cpp
index e42469b51b06..9cdafefdfe67 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2255,6 +2255,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (bool(GLOBAL_GET("display/window/size/no_focus"))) {
window_flags |= DisplayServer::WINDOW_FLAG_NO_FOCUS_BIT;
}
+
window_mode = (DisplayServer::WindowMode)(GLOBAL_GET("display/window/size/mode").operator int());
int initial_position_type = GLOBAL_GET("display/window/size/initial_position_type").operator int();
if (initial_position_type == 0) { // Absolute.
@@ -2914,6 +2915,12 @@ Error Main::setup2(bool p_show_boot_logo) {
DisplayServer::get_singleton()->window_set_flag(DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP, true);
}
+ // Enable HDR if requested and available.
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_HDR)) {
+ DisplayServer::get_singleton()->window_set_hdr_output_enabled(GLOBAL_GET("display/window/hdr/enabled"));
+ DisplayServer::get_singleton()->window_set_hdr_output_max_luminance(GLOBAL_GET("display/window/hdr/max_luminance"));
+ }
+
Color clear = GLOBAL_DEF_BASIC("rendering/environment/defaults/default_clear_color", Color(0.3, 0.3, 0.3));
RenderingServer::get_singleton()->set_default_clear_color(clear);
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 36f3f632d510..9db1fd9acd4b 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -122,6 +122,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_TEXT_TO_SPEECH:
case FEATURE_SCREEN_CAPTURE:
case FEATURE_STATUS_INDICATOR:
+ case FEATURE_HDR:
return true;
default:
return false;
@@ -3443,6 +3444,46 @@ DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_
return DisplayServer::VSYNC_ENABLED;
}
+void DisplayServerWindows::window_set_hdr_output_enabled(const bool p_enabled, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+#if defined(RD_ENABLED)
+ if (rendering_context) {
+ rendering_context->window_set_hdr_output_enabled(p_window, p_enabled);
+ }
+#endif
+}
+
+bool DisplayServerWindows::window_get_hdr_output_enabled(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+#if defined(RD_ENABLED)
+ if (rendering_context) {
+ return rendering_context->window_get_hdr_output_enabled(p_window);
+ }
+#endif
+
+ return false;
+}
+
+void DisplayServerWindows::window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+#if defined(RD_ENABLED)
+ if (rendering_context) {
+ rendering_context->window_set_hdr_output_max_luminance(p_window, p_max_luminance);
+ }
+#endif
+}
+
+float DisplayServerWindows::window_get_hdr_output_max_luminance(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+#if defined(RD_ENABLED)
+ if (rendering_context) {
+ return rendering_context->window_get_hdr_output_max_luminance(p_window);
+ }
+#endif
+
+ return 0.0f;
+}
+
void DisplayServerWindows::set_context(Context p_context) {
}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index de5b81395337..e93b54c59fd0 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -728,6 +728,11 @@ class DisplayServerWindows : public DisplayServer {
virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
+ virtual void window_set_hdr_output_enabled(const bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual bool window_get_hdr_output_enabled(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 void cursor_set_shape(CursorShape p_shape) override;
virtual CursorShape cursor_get_shape() const override;
virtual void cursor_set_custom_image(const Ref &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 54510576459b..8904cff7a5ea 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -752,6 +752,24 @@ DisplayServer::VSyncMode DisplayServer::window_get_vsync_mode(WindowID p_window)
return VSyncMode::VSYNC_ENABLED;
}
+void DisplayServer::window_set_hdr_output_enabled(const bool p_enabled, WindowID p_window) {
+ WARN_PRINT("Changing the HDR mode is not supported by this display server.");
+}
+
+bool DisplayServer::window_get_hdr_output_enabled(WindowID p_window) const {
+ WARN_PRINT("Changing the HDR mode is not supported by this display server.");
+ return false;
+}
+
+void DisplayServer::window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window) {
+ WARN_PRINT("Changing the HDR mode is not supported by this display server.");
+}
+
+float DisplayServer::window_get_hdr_output_max_luminance(WindowID p_window) const {
+ WARN_PRINT("Changing the HDR mode is not supported by this display server.");
+ return 0.0f;
+}
+
DisplayServer::WindowID DisplayServer::get_focused_window() const {
return MAIN_WINDOW_ID; // Proper value for single windows.
}
@@ -953,6 +971,11 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("window_set_vsync_mode", "vsync_mode", "window_id"), &DisplayServer::window_set_vsync_mode, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_get_vsync_mode", "window_id"), &DisplayServer::window_get_vsync_mode, DEFVAL(MAIN_WINDOW_ID));
+ ClassDB::bind_method(D_METHOD("window_set_hdr_output_enabled", "enabled", "window_id"), &DisplayServer::window_set_hdr_output_enabled, DEFVAL(MAIN_WINDOW_ID));
+ ClassDB::bind_method(D_METHOD("window_get_hdr_output_enabled", "window_id"), &DisplayServer::window_get_hdr_output_enabled, DEFVAL(MAIN_WINDOW_ID));
+ ClassDB::bind_method(D_METHOD("window_set_hdr_output_max_luminance", "max_luminance", "window_id"), &DisplayServer::window_set_hdr_output_max_luminance, DEFVAL(MAIN_WINDOW_ID));
+ ClassDB::bind_method(D_METHOD("window_get_hdr_output_max_luminance", "window_id"), &DisplayServer::window_get_hdr_output_max_luminance, DEFVAL(MAIN_WINDOW_ID));
+
ClassDB::bind_method(D_METHOD("window_is_maximize_allowed", "window_id"), &DisplayServer::window_is_maximize_allowed, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_maximize_on_title_dbl_click"), &DisplayServer::window_maximize_on_title_dbl_click);
ClassDB::bind_method(D_METHOD("window_minimize_on_title_dbl_click"), &DisplayServer::window_minimize_on_title_dbl_click);
@@ -1039,6 +1062,7 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(FEATURE_NATIVE_HELP);
BIND_ENUM_CONSTANT(FEATURE_NATIVE_DIALOG_INPUT);
BIND_ENUM_CONSTANT(FEATURE_NATIVE_DIALOG_FILE);
+ BIND_ENUM_CONSTANT(FEATURE_HDR);
BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE);
BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN);
diff --git a/servers/display_server.h b/servers/display_server.h
index d0fe76faff37..a94c0b9732e5 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -150,6 +150,7 @@ class DisplayServer : public Object {
FEATURE_NATIVE_HELP,
FEATURE_NATIVE_DIALOG_INPUT,
FEATURE_NATIVE_DIALOG_FILE,
+ FEATURE_HDR,
};
virtual bool has_feature(Feature p_feature) const = 0;
@@ -460,6 +461,11 @@ class DisplayServer : public Object {
virtual void window_set_vsync_mode(VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID);
virtual VSyncMode window_get_vsync_mode(WindowID p_window) const;
+ virtual void window_set_hdr_output_enabled(const bool p_enabled, WindowID p_window = MAIN_WINDOW_ID);
+ virtual bool window_get_hdr_output_enabled(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual void window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window = MAIN_WINDOW_ID);
+ virtual float window_get_hdr_output_max_luminance(WindowID p_window = MAIN_WINDOW_ID) const;
+
virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const = 0;
virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) = 0;
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
index 84ea6a5da203..0eff5ec74963 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
@@ -43,6 +43,9 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin_for_screen(p_screen);
ERR_FAIL_COND(draw_list == RD::INVALID_ID);
+ const RD::ColorSpace color_space = RD::get_singleton()->screen_get_color_space(p_screen);
+ const float max_luminance = RD::get_singleton()->get_context_driver()->window_get_hdr_output_max_luminance(p_screen);
+
for (int i = 0; i < p_amount; i++) {
RID rd_texture = texture_storage->render_target_get_rd_texture(p_render_targets[i].render_target);
ERR_CONTINUE(rd_texture.is_null());
@@ -62,6 +65,7 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID
Size2 screen_size(RD::get_singleton()->screen_get_width(p_screen), RD::get_singleton()->screen_get_height(p_screen));
BlitMode mode = p_render_targets[i].lens_distortion.apply ? BLIT_MODE_LENS : (p_render_targets[i].multi_view.use_layer ? BLIT_MODE_USE_LAYER : BLIT_MODE_NORMAL);
+
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blit.pipelines[mode]);
RD::get_singleton()->draw_list_bind_index_array(draw_list, blit.array);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, render_target_descriptors[rd_texture], 0);
@@ -81,7 +85,11 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID
blit.push_constant.k2 = p_render_targets[i].lens_distortion.k2;
blit.push_constant.upscale = p_render_targets[i].lens_distortion.upscale;
blit.push_constant.aspect_ratio = p_render_targets[i].lens_distortion.aspect_ratio;
- blit.push_constant.convert_to_srgb = texture_storage->render_target_is_using_hdr(p_render_targets[i].render_target);
+ blit.push_constant.source_is_srgb = !texture_storage->render_target_is_using_hdr(p_render_targets[i].render_target);
+ blit.push_constant.target_color_space = color_space;
+ blit.push_constant.max_display_luminance = max_luminance;
+ blit.push_constant.pad[0] = 0.0;
+ blit.push_constant.pad[1] = 0.0;
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blit.push_constant, sizeof(BlitPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
@@ -222,6 +230,9 @@ void RendererCompositorRD::set_boot_image(const Ref &p_image, const Color
screenrect.position /= window_size;
screenrect.size /= window_size;
+ const RD::ColorSpace color_space = RD::get_singleton()->screen_get_color_space(DisplayServer::MAIN_WINDOW_ID);
+ const float max_luminance = RD::get_singleton()->get_context_driver()->window_get_hdr_output_max_luminance(DisplayServer::MAIN_WINDOW_ID);
+
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin_for_screen(DisplayServer::MAIN_WINDOW_ID, p_color);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blit.pipelines[BLIT_MODE_NORMAL_ALPHA]);
@@ -243,7 +254,11 @@ void RendererCompositorRD::set_boot_image(const Ref &p_image, const Color
blit.push_constant.k2 = 0;
blit.push_constant.upscale = 1.0;
blit.push_constant.aspect_ratio = 1.0;
- blit.push_constant.convert_to_srgb = false;
+ blit.push_constant.source_is_srgb = true;
+ blit.push_constant.target_color_space = color_space;
+ blit.push_constant.max_display_luminance = max_luminance;
+ blit.push_constant.pad[0] = 0.0;
+ blit.push_constant.pad[1] = 0.0;
RD::get_singleton()->draw_list_set_push_constant(draw_list, &blit.push_constant, sizeof(BlitPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h
index 0222a9957794..c018af16f3ff 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.h
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h
@@ -80,7 +80,11 @@ class RendererCompositorRD : public RendererCompositor {
float upscale;
float aspect_ratio;
uint32_t layer;
- uint32_t convert_to_srgb;
+ uint32_t source_is_srgb;
+
+ uint32_t target_color_space;
+ float max_display_luminance;
+ float pad[2];
};
struct Blit {
diff --git a/servers/rendering/renderer_rd/shaders/blit.glsl b/servers/rendering/renderer_rd/shaders/blit.glsl
index d451647bec66..39bb7576b7b2 100644
--- a/servers/rendering/renderer_rd/shaders/blit.glsl
+++ b/servers/rendering/renderer_rd/shaders/blit.glsl
@@ -15,7 +15,11 @@ layout(push_constant, std140) uniform Pos {
float upscale;
float aspect_ratio;
uint layer;
- uint pad1;
+ bool source_is_srgb;
+
+ uint target_color_space;
+ float max_display_luminance;
+ vec2 pad;
}
data;
@@ -34,6 +38,10 @@ void main() {
#VERSION_DEFINES
+// Keep in sync with RenderingDeviceCommons::ColorSpace
+const uint COLOR_SPACE_SRGB_NONLINEAR = 0;
+const uint COLOR_SPACE_HDR10_ST2048 = 1;
+
layout(push_constant, std140) uniform Pos {
vec4 src_rect;
vec4 dst_rect;
@@ -45,7 +53,11 @@ layout(push_constant, std140) uniform Pos {
float upscale;
float aspect_ratio;
uint layer;
- bool convert_to_srgb;
+ bool source_is_srgb;
+
+ uint target_color_space;
+ float max_display_luminance;
+ vec2 pad;
}
data;
@@ -59,12 +71,7 @@ layout(binding = 0) uniform sampler2DArray src_rt;
layout(binding = 0) uniform sampler2D src_rt;
#endif
-vec3 linear_to_srgb(vec3 color) {
- // If going to srgb, clamp from 0 to 1.
- color = clamp(color, vec3(0.0), vec3(1.0));
- const vec3 a = vec3(0.055f);
- return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
-}
+#include "color_space_inc.glsl"
void main() {
#ifdef APPLY_LENS_DISTORTION
@@ -102,7 +109,14 @@ void main() {
color = texture(src_rt, uv);
#endif
- if (data.convert_to_srgb) {
- color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
+ if (data.target_color_space == COLOR_SPACE_SRGB_NONLINEAR && data.source_is_srgb == false) {
+ color.rgb = linear_to_srgb(color.rgb); // linear -> sRGB conversion.
+ } else if (data.target_color_space == COLOR_SPACE_HDR10_ST2048) {
+ if (data.source_is_srgb == true) {
+ // HACK: Converting back to linear since I'm not sure who else converts to sRGB.
+ color.rgb = srgb_to_linear(color.rgb); // sRGB -> linear conversion.
+ }
+
+ color.rgb = linear_to_st2084(color.rgb, data.max_display_luminance); // linear -> ST2084 conversion.
}
}
diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl
index 4426d9eb6622..5a0a9c68a3c8 100644
--- a/servers/rendering/renderer_rd/shaders/canvas.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas.glsl
@@ -45,9 +45,7 @@ layout(set = 1, binding = 0, std140) uniform MaterialUniforms{
#GLOBALS
#ifdef USE_ATTRIBUTES
-vec3 srgb_to_linear(vec3 color) {
- return mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), color.rgb * (1.0 / 12.92), lessThan(color.rgb, vec3(0.04045)));
-}
+#include "color_space_inc.glsl"
#endif
void main() {
diff --git a/servers/rendering/renderer_rd/shaders/color_space_inc.glsl b/servers/rendering/renderer_rd/shaders/color_space_inc.glsl
new file mode 100644
index 000000000000..3583ee83652a
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/color_space_inc.glsl
@@ -0,0 +1,32 @@
+
+vec3 srgb_to_linear(vec3 color) {
+ // This expects 0-1 range input, outside that range it behaves poorly.
+ // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
+ return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878);
+}
+
+vec3 linear_to_srgb(vec3 color) {
+ //if going to srgb, clamp from 0 to 1.
+ color = clamp(color, vec3(0.0), vec3(1.0));
+ const vec3 a = vec3(0.055f);
+ return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
+}
+
+vec3 linear_to_st2084(vec3 color, float max_luminance) {
+ color = clamp(color, vec3(0.0), vec3(1.0));
+
+ // Keep the output from blowing out the display
+ // max_luminance is the display's peak luminance in nits
+ // we map it here to the native 10000 nits range of ST2084
+ float adjustment = max_luminance * (1.0f / 10000.0f);
+
+ // Apply ST2084 curve
+ const float c1 = 0.8359375;
+ const float c2 = 18.8515625;
+ const float c3 = 18.6875;
+ const float m1 = 0.1593017578125;
+ const float m2 = 78.84375;
+ vec3 cp = pow(abs(color.rgb * adjustment), vec3(m1));
+
+ return pow((c1 + c2 * cp) / (1 + c3 * cp), vec3(m2));
+}
diff --git a/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl b/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl
index 26ee06aa03e7..26a1eb3443f5 100644
--- a/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/copy_to_fb.glsl
@@ -118,16 +118,7 @@ layout(set = 1, binding = 0) uniform sampler2D source_color2;
layout(location = 0) out vec4 frag_color;
-vec3 linear_to_srgb(vec3 color) {
- //if going to srgb, clamp from 0 to 1.
- color = clamp(color, vec3(0.0), vec3(1.0));
- const vec3 a = vec3(0.055f);
- return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
-}
-
-vec3 srgb_to_linear(vec3 color) {
- return mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), color.rgb * (1.0 / 12.92), lessThan(color.rgb, vec3(0.04045)));
-}
+#include "../color_space_inc.glsl"
void main() {
#ifdef MODE_SET_COLOR
diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
index 841f02f673ea..e47b16e61964 100644
--- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl
@@ -260,12 +260,7 @@ vec3 tonemap_reinhard(vec3 color, float white) {
return (white * color + color) / (color * white + white);
}
-vec3 linear_to_srgb(vec3 color) {
- //if going to srgb, clamp from 0 to 1.
- color = clamp(color, vec3(0.0), vec3(1.0));
- const vec3 a = vec3(0.055f);
- return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
-}
+#include "../color_space_inc.glsl"
#define TONEMAPPER_LINEAR 0
#define TONEMAPPER_REINHARD 1
diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl
index 177dab16c763..f9cc74f163fb 100644
--- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl
@@ -49,12 +49,7 @@ layout(push_constant, std430) uniform Params {
}
params;
-vec3 linear_to_srgb(vec3 color) {
- //if going to srgb, clamp from 0 to 1.
- color = clamp(color, vec3(0.0), vec3(1.0));
- const vec3 a = vec3(0.055f);
- return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
-}
+#include "../color_space_inc.glsl"
vec2 octahedron_wrap(vec2 v) {
vec2 signVal;
diff --git a/servers/rendering/rendering_context_driver.cpp b/servers/rendering/rendering_context_driver.cpp
index 19c0b0838c80..aeab880b41e3 100644
--- a/servers/rendering/rendering_context_driver.cpp
+++ b/servers/rendering/rendering_context_driver.cpp
@@ -75,6 +75,38 @@ DisplayServer::VSyncMode RenderingContextDriver::window_get_vsync_mode(DisplaySe
}
}
+void RenderingContextDriver::window_set_hdr_output_enabled(DisplayServer::WindowID p_window, bool p_enabled) {
+ SurfaceID surface = surface_get_from_window(p_window);
+ if (surface) {
+ surface_set_hdr_output_enabled(surface, p_enabled);
+ }
+}
+
+bool RenderingContextDriver::window_get_hdr_output_enabled(DisplayServer::WindowID p_window) const {
+ SurfaceID surface = surface_get_from_window(p_window);
+ if (surface) {
+ return surface_get_hdr_output_enabled(surface);
+ } else {
+ return false;
+ }
+}
+
+void RenderingContextDriver::window_set_hdr_output_max_luminance(DisplayServer::WindowID p_window, float p_max_luminance) {
+ SurfaceID surface = surface_get_from_window(p_window);
+ if (surface) {
+ surface_set_hdr_output_max_luminance(surface, p_max_luminance);
+ }
+}
+
+float RenderingContextDriver::window_get_hdr_output_max_luminance(DisplayServer::WindowID p_window) const {
+ SurfaceID surface = surface_get_from_window(p_window);
+ if (surface) {
+ return surface_get_hdr_output_max_luminance(surface);
+ } else {
+ return 0.0f;
+ }
+}
+
void RenderingContextDriver::window_destroy(DisplayServer::WindowID p_window) {
SurfaceID surface = surface_get_from_window(p_window);
if (surface) {
diff --git a/servers/rendering/rendering_context_driver.h b/servers/rendering/rendering_context_driver.h
index 539b3814a041..ce679d773c3c 100644
--- a/servers/rendering/rendering_context_driver.h
+++ b/servers/rendering/rendering_context_driver.h
@@ -49,6 +49,10 @@ class RenderingContextDriver {
void window_set_size(DisplayServer::WindowID p_window, uint32_t p_width, uint32_t p_height);
void window_set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_vsync_mode);
DisplayServer::VSyncMode window_get_vsync_mode(DisplayServer::WindowID p_window) const;
+ void window_set_hdr_output_enabled(DisplayServer::WindowID p_window, bool p_enabled);
+ bool window_get_hdr_output_enabled(DisplayServer::WindowID p_window) const;
+ void window_set_hdr_output_max_luminance(DisplayServer::WindowID p_window, float p_max_luminance);
+ float window_get_hdr_output_max_luminance(DisplayServer::WindowID p_window) const;
void window_destroy(DisplayServer::WindowID p_window);
public:
@@ -95,6 +99,10 @@ class RenderingContextDriver {
virtual void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) = 0;
virtual void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) = 0;
virtual DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const = 0;
+ virtual void surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) = 0;
+ virtual bool surface_get_hdr_output_enabled(SurfaceID p_surface) const = 0;
+ virtual void surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) = 0;
+ virtual float surface_get_hdr_output_max_luminance(SurfaceID p_surface) const = 0;
virtual uint32_t surface_get_width(SurfaceID p_surface) const = 0;
virtual uint32_t surface_get_height(SurfaceID p_surface) const = 0;
virtual void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) = 0;
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index 22044a8c2d12..6e253dd8cbbf 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -3526,6 +3526,17 @@ RenderingDevice::FramebufferFormatID RenderingDevice::screen_get_framebuffer_for
return const_cast(this)->framebuffer_format_create(screen_attachment);
}
+RenderingDevice::ColorSpace RenderingDevice::screen_get_color_space(DisplayServer::WindowID p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ HashMap::ConstIterator it = screen_swap_chains.find(p_screen);
+ ERR_FAIL_COND_V_MSG(it == screen_swap_chains.end(), COLOR_SPACE_SRGB_NONLINEAR, "Screen was never prepared, defaulting to sRGB.");
+
+ ColorSpace color_space = driver->swap_chain_get_color_space(it->value);
+ ERR_FAIL_COND_V_MSG(color_space == COLOR_SPACE_MAX, COLOR_SPACE_SRGB_NONLINEAR, "Unknown color space, defaulting to sRGB.");
+ return color_space;
+}
+
Error RenderingDevice::screen_free(DisplayServer::WindowID p_screen) {
_THREAD_SAFE_METHOD_
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 38ffd5d23de1..c55c1b47eafa 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -1053,6 +1053,7 @@ class RenderingDevice : public RenderingDeviceCommons {
int screen_get_width(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
int screen_get_height(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
FramebufferFormatID screen_get_framebuffer_format(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
+ ColorSpace screen_get_color_space(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
Error screen_free(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID);
/*************************/
diff --git a/servers/rendering/rendering_device_commons.h b/servers/rendering/rendering_device_commons.h
index 918bf9b834ad..0689562a793e 100644
--- a/servers/rendering/rendering_device_commons.h
+++ b/servers/rendering/rendering_device_commons.h
@@ -271,6 +271,12 @@ class RenderingDeviceCommons : public Object {
DATA_FORMAT_MAX,
};
+ enum ColorSpace {
+ COLOR_SPACE_SRGB_NONLINEAR,
+ COLOR_SPACE_HDR10_ST2084,
+ COLOR_SPACE_MAX,
+ };
+
enum CompareOperator {
COMPARE_OP_NEVER,
COMPARE_OP_LESS,
diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h
index 0b5fc51a1d86..851a88ad8aff 100644
--- a/servers/rendering/rendering_device_driver.h
+++ b/servers/rendering/rendering_device_driver.h
@@ -456,6 +456,9 @@ class RenderingDeviceDriver : public RenderingDeviceCommons {
// Retrieve the format used by the swap chain's framebuffers.
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) = 0;
+ // Retrieve the color space used by the swap chain's framebuffers.
+ virtual ColorSpace swap_chain_get_color_space(SwapChainID p_swap_chain) = 0;
+
// Wait until all rendering associated to the swap chain is finished before deleting it.
virtual void swap_chain_free(SwapChainID p_swap_chain) = 0;