From 728912f1d4321463caff03a248824c5caed041e1 Mon Sep 17 00:00:00 2001 From: Josh Jones Date: Thu, 11 Jul 2024 23:45:36 -0700 Subject: [PATCH] Support output to HDR monitors --- core/config/project_settings.cpp | 2 + doc/classes/DisplayServer.xml | 35 ++++ doc/classes/ProjectSettings.xml | 8 + .../d3d12/rendering_context_driver_d3d12.cpp | 20 +++ .../d3d12/rendering_context_driver_d3d12.h | 6 + .../d3d12/rendering_device_driver_d3d12.cpp | 4 + drivers/d3d12/rendering_device_driver_d3d12.h | 1 + .../rendering_context_driver_vulkan.cpp | 21 +++ .../vulkan/rendering_context_driver_vulkan.h | 6 + .../vulkan/rendering_device_driver_vulkan.cpp | 153 ++++++++++++++---- .../vulkan/rendering_device_driver_vulkan.h | 2 + main/main.cpp | 7 + platform/windows/display_server_windows.cpp | 41 +++++ platform/windows/display_server_windows.h | 5 + servers/display_server.cpp | 24 +++ servers/display_server.h | 6 + .../renderer_rd/renderer_compositor_rd.cpp | 19 ++- .../renderer_rd/renderer_compositor_rd.h | 6 +- .../rendering/renderer_rd/shaders/blit.glsl | 34 ++-- .../rendering/renderer_rd/shaders/canvas.glsl | 4 +- .../renderer_rd/shaders/color_space_inc.glsl | 32 ++++ .../shaders/effects/copy_to_fb.glsl | 11 +- .../renderer_rd/shaders/effects/tonemap.glsl | 7 +- .../shaders/environment/sdfgi_debug.glsl | 7 +- .../rendering/rendering_context_driver.cpp | 32 ++++ servers/rendering/rendering_context_driver.h | 8 + servers/rendering/rendering_device.cpp | 11 ++ servers/rendering/rendering_device.h | 1 + servers/rendering/rendering_device_commons.h | 6 + servers/rendering/rendering_device_driver.h | 3 + 30 files changed, 450 insertions(+), 72 deletions(-) create mode 100644 servers/rendering/renderer_rd/shaders/color_space_inc.glsl diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 32f36e01f9a5..7a2f2996f723 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 79064a88ba6c..cb032b659419 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 1f31fef5ca54..f7cc0231a82d 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -820,6 +820,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 8fa495f5c4f3..28a46dc10328 100644 --- a/drivers/d3d12/rendering_context_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_context_driver_d3d12.cpp @@ -276,6 +276,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 a445006058cd..c6e47cccad56 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -2642,6 +2642,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 d8381279ec83..bad99b82257f 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.h +++ b/drivers/d3d12/rendering_device_driver_d3d12.h @@ -528,6 +528,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 df9bd986240d..b3fc8e9a62c9 100644 --- a/drivers/vulkan/rendering_context_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_context_driver_vulkan.cpp @@ -991,6 +991,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 4fbca012c619..425bb2bda38a 100644 --- a/drivers/vulkan/rendering_context_driver_vulkan.h +++ b/drivers/vulkan/rendering_context_driver_vulkan.h @@ -143,6 +143,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; @@ -156,6 +160,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 2ba353868b55..be2d562caec9 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -2653,6 +2653,92 @@ 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 true; + } + + // 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. + bool found = false; + 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)) { + found = true; + r_format = formats[i].format; + r_color_space = formats[i].colorSpace; + + // This is the preferred format, stop searching. + if (formats[i].format == preferred_format) { + break; + } + } + } + + if (found) { + 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; + + bool found = false; + 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)) { + found = true; + r_format = formats[i].format; + r_color_space = formats[i].colorSpace; + + // This is the preferred format, stop searching. + if (formats[i].format == preferred_format) { + break; + } + } + } + + if (found) { + 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) { @@ -2684,43 +2770,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; @@ -2751,7 +2807,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, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_RENDER_PASS), &render_pass); + VkResult err = _create_render_pass(vk_device, &pass_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_RENDER_PASS), &render_pass); ERR_FAIL_COND_V(err != VK_SUCCESS, SwapChainID()); SwapChain *swap_chain = memnew(SwapChain); @@ -2880,6 +2936,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; @@ -3031,12 +3097,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 2615d9824d34..1e4f0ae7ed21 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.h +++ b/drivers/vulkan/rendering_device_driver_vulkan.h @@ -353,6 +353,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: @@ -361,6 +362,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 791562a9bb62..4a5df9cd68e9 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2372,6 +2372,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. @@ -3121,6 +3122,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 8bcd556c2207..841aa5052d3c 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; @@ -3614,6 +3615,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 3deb7ac8b016..266152695483 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -768,6 +768,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 8cb3e560ac9f..31b29c3142c3 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -760,6 +760,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. } @@ -961,6 +979,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); @@ -1047,6 +1070,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 04f4b0c03d25..f603c029eb3b 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 b623be40980f..3d4a38fea49d 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 2e5951ae4f83..0da448db05f7 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 f0f267c2466c..4c460e12196f 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -3545,6 +3545,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 d8f9e2c31a77..d2fa9e12afba 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -1067,6 +1067,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 8c3996bd8043..a33dc430f005 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, + }; + // Breadcrumb markers are useful for debugging GPU crashes (i.e. DEVICE_LOST). Internally // they're just an uint32_t to "tag" a GPU command. These are only used for debugging and do not // (or at least shouldn't) alter the execution behavior in any way. diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h index 97c84c9d05f9..f606e01b49f9 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;