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;