HDR: Implement surface supports HDR output.
Previously the Wayland display server would attempt to enable HDR output and try to detect if it failed afterwards which had some issues. Now we can query the rendering driver for support and avoid ever enabling HDR output when this would fail.
This commit is contained in:
@@ -854,30 +854,43 @@ void DisplayServerAppleEmbedded::current_edr_headroom_changed() {
|
||||
|
||||
bool DisplayServerAppleEmbedded::window_is_hdr_output_supported(DisplayServerEnums::WindowID p_window) const {
|
||||
bool renderer_supports_hdr_output = false;
|
||||
bool surface_supports_hdr_output = false;
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_device && rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) {
|
||||
renderer_supports_hdr_output = true;
|
||||
surface_supports_hdr_output = rendering_device->screen_get_hdr_output_supported(p_window);
|
||||
}
|
||||
#endif
|
||||
if (!renderer_supports_hdr_output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!surface_supports_hdr_output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _screen_hdr_is_supported();
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_request_hdr_output(const bool p_enabled, DisplayServerEnums::WindowID p_window) {
|
||||
if (p_enabled) {
|
||||
bool renderer_supports_hdr_output = false;
|
||||
bool surface_supports_hdr_output = false;
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_device && rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) {
|
||||
renderer_supports_hdr_output = true;
|
||||
surface_supports_hdr_output = rendering_device->screen_get_hdr_output_supported(p_window);
|
||||
}
|
||||
#endif
|
||||
if (!renderer_supports_hdr_output) {
|
||||
WARN_PRINT("HDR output requested, but is not supported by the renderer or rendering device driver.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!surface_supports_hdr_output) {
|
||||
WARN_PRINT("HDR output requested, but the window does not support an HDR format.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
edr_requested = p_enabled;
|
||||
|
||||
@@ -2977,6 +2977,11 @@ RDD::ColorSpace RenderingDeviceDriverD3D12::swap_chain_get_color_space(SwapChain
|
||||
return swap_chain->color_space;
|
||||
}
|
||||
|
||||
bool RenderingDeviceDriverD3D12::swap_chain_get_hdr_output_supported(SwapChainID p_swap_chain) {
|
||||
// Our minimum supported version of D3D12 requires support for our HDR colorspaces.
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderingDeviceDriverD3D12::swap_chain_free(SwapChainID p_swap_chain) {
|
||||
SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
|
||||
_swap_chain_release(swap_chain);
|
||||
|
||||
@@ -551,6 +551,7 @@ public:
|
||||
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 bool swap_chain_get_hdr_output_supported(SwapChainID p_swap_chain) override;
|
||||
virtual void swap_chain_free(SwapChainID p_swap_chain) override;
|
||||
|
||||
/*********************/
|
||||
|
||||
@@ -983,6 +983,11 @@ RDD::ColorSpace RenderingDeviceDriverMetal::swap_chain_get_color_space(SwapChain
|
||||
return swap_chain->color_space;
|
||||
}
|
||||
|
||||
bool RenderingDeviceDriverMetal::swap_chain_get_hdr_output_supported(SwapChainID p_swap_chain) {
|
||||
// Our minimum supported version of metal requires support for EDR.
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderingDeviceDriverMetal::swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) {
|
||||
SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
|
||||
RenderingContextDriverMetal::Surface *metal_surface = (RenderingContextDriverMetal::Surface *)(swap_chain->surface);
|
||||
|
||||
@@ -316,6 +316,7 @@ public:
|
||||
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 bool swap_chain_get_hdr_output_supported(SwapChainID p_swap_chain) override final;
|
||||
virtual void swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) override final;
|
||||
virtual void swap_chain_free(SwapChainID p_swap_chain) override final;
|
||||
|
||||
|
||||
@@ -4008,6 +4008,58 @@ RDD::ColorSpace RenderingDeviceDriverVulkan::swap_chain_get_color_space(SwapChai
|
||||
return swap_chain->rdd_color_space;
|
||||
}
|
||||
|
||||
bool RenderingDeviceDriverVulkan::swap_chain_get_hdr_output_supported(SwapChainID p_swap_chain) {
|
||||
DEV_ASSERT(p_swap_chain.id != 0);
|
||||
|
||||
SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
|
||||
RenderingContextDriverVulkan::Surface *surface = (RenderingContextDriverVulkan::Surface *)(swap_chain->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<VkSurfaceFormatKHR> 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.
|
||||
// Just to be safe, we assume this means HDR will not be supported.
|
||||
if (format_count == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool colorspace_supported = context_driver->is_colorspace_supported();
|
||||
|
||||
// Determine which formats to prefer based on the requested capabilities.
|
||||
// Our preferred HDR format is 16-bit float + extended linear.
|
||||
FixedVector<FormatCandidate, 2> hdr_formats;
|
||||
if (context_driver->is_colorspace_externally_managed()) {
|
||||
// When the colorspace is managed externally to the driver we need to disable its color management.
|
||||
// The colorspace which disables color management is VK_COLOR_SPACE_PASS_THROUGH_EXT.
|
||||
hdr_formats.push_back({ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_PASS_THROUGH_EXT, COLOR_SPACE_REC709_LINEAR });
|
||||
|
||||
// SRGB_NONLINEAR_KHR is required for some NVIDIA drivers that support HDR output but do not support PASS_THROUGH_EXT.
|
||||
hdr_formats.push_back({ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, COLOR_SPACE_REC709_LINEAR });
|
||||
} else if (colorspace_supported) {
|
||||
hdr_formats.push_back({ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT, COLOR_SPACE_REC709_LINEAR });
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FormatCandidate &candidate : hdr_formats) {
|
||||
for (uint32_t i = 0; i < format_count; i++) {
|
||||
if (formats[i].format == candidate.format && formats[i].colorSpace == candidate.colorspace) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RenderingDeviceDriverVulkan::swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) {
|
||||
DEV_ASSERT(p_swap_chain.id != 0);
|
||||
|
||||
|
||||
@@ -445,6 +445,7 @@ public:
|
||||
virtual int swap_chain_get_pre_rotation_degrees(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 bool swap_chain_get_hdr_output_supported(SwapChainID p_swap_chain) override final;
|
||||
virtual void swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) override final;
|
||||
virtual void swap_chain_free(SwapChainID p_swap_chain) override final;
|
||||
|
||||
|
||||
@@ -1536,15 +1536,21 @@ void DisplayServerWayland::_window_update_hdr_state(WindowData &p_window) {
|
||||
bool DisplayServerWayland::window_is_hdr_output_supported(DisplayServerEnums::WindowID p_window_id) const {
|
||||
ERR_FAIL_COND_V(!windows.has(p_window_id), false);
|
||||
bool renderer_supports_hdr_output = false;
|
||||
bool surface_supports_hdr_output = false;
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_device && rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) {
|
||||
renderer_supports_hdr_output = true;
|
||||
surface_supports_hdr_output = rendering_device->screen_get_hdr_output_supported(p_window_id);
|
||||
}
|
||||
#endif
|
||||
if (!renderer_supports_hdr_output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!surface_supports_hdr_output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const WindowData &wd = windows[p_window_id];
|
||||
|
||||
return wd.color_profile.target_max_luminance > wd.color_profile.reference_luminance;
|
||||
@@ -1553,15 +1559,22 @@ bool DisplayServerWayland::window_is_hdr_output_supported(DisplayServerEnums::Wi
|
||||
void DisplayServerWayland::window_request_hdr_output(const bool p_enabled, DisplayServerEnums::WindowID p_window_id) {
|
||||
if (p_enabled) {
|
||||
bool renderer_supports_hdr_output = false;
|
||||
bool surface_supports_hdr_output = false;
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_device && rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) {
|
||||
renderer_supports_hdr_output = true;
|
||||
surface_supports_hdr_output = rendering_device->screen_get_hdr_output_supported(p_window_id);
|
||||
}
|
||||
#endif
|
||||
if (!renderer_supports_hdr_output) {
|
||||
WARN_PRINT("HDR output requested, but is not supported by the renderer or rendering device driver.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!surface_supports_hdr_output) {
|
||||
WARN_PRINT("HDR output requested, but the window does not support an HDR format.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(!windows.has(p_window_id));
|
||||
@@ -1894,41 +1907,6 @@ void DisplayServerWayland::process_events() {
|
||||
|
||||
wayland_thread.keyboard_echo_keys();
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
// Enabling HDR may have failed, in which case we need to clear the color profile.
|
||||
// NOTE: this happens _before_ reading events because the rendering driver is only updated the frame _after_ we try to enable HDR.
|
||||
if (rendering_device && (!OS::get_singleton()->is_in_low_processor_usage_mode() || RS::get_singleton()->has_changed())) {
|
||||
for (KeyValue<DisplayServerEnums::WindowID, WindowData> &pair : windows) {
|
||||
const RD::ColorSpace color_space = rendering_device->screen_get_color_space(pair.key);
|
||||
|
||||
bool dirty_srgb = color_space == RDD::COLOR_SPACE_REC709_NONLINEAR_SRGB && pair.value.color_profile.named_transfer_function != WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22;
|
||||
bool dirty_linear = color_space == RDD::COLOR_SPACE_REC709_LINEAR && pair.value.color_profile.named_transfer_function != WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR;
|
||||
|
||||
if (dirty_srgb) {
|
||||
pair.value.color_profile.named_primary = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB;
|
||||
pair.value.color_profile.named_transfer_function = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22;
|
||||
|
||||
if (pair.value.visible) {
|
||||
wayland_thread.window_set_color_profile(pair.key, pair.value.color_profile);
|
||||
}
|
||||
|
||||
rendering_context->window_set_hdr_output_enabled(pair.key, false);
|
||||
_send_window_event(DisplayServerEnums::WINDOW_EVENT_OUTPUT_MAX_LINEAR_VALUE_CHANGED, pair.key);
|
||||
} else if (dirty_linear) {
|
||||
pair.value.color_profile.named_primary = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB;
|
||||
pair.value.color_profile.named_transfer_function = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR;
|
||||
|
||||
if (pair.value.visible) {
|
||||
wayland_thread.window_set_color_profile(pair.key, pair.value.color_profile);
|
||||
}
|
||||
|
||||
rendering_context->window_set_hdr_output_enabled(pair.key, true);
|
||||
_send_window_event(DisplayServerEnums::WINDOW_EVENT_OUTPUT_MAX_LINEAR_VALUE_CHANGED, pair.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
while (wayland_thread.has_message()) {
|
||||
Ref<WaylandThread::Message> msg = wayland_thread.pop_message();
|
||||
|
||||
|
||||
@@ -591,15 +591,21 @@ bool DisplayServerMacOSBase::window_is_hdr_output_supported(DisplayServerEnums::
|
||||
|
||||
ERR_FAIL_COND_V(!has_window(p_window), false);
|
||||
bool renderer_supports_hdr_output = false;
|
||||
bool surface_supports_hdr_output = false;
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_device && rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) {
|
||||
renderer_supports_hdr_output = true;
|
||||
surface_supports_hdr_output = rendering_device->screen_get_hdr_output_supported(p_window);
|
||||
}
|
||||
#endif
|
||||
if (!renderer_supports_hdr_output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!surface_supports_hdr_output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CGFloat max_potential_edr;
|
||||
window_get_edr_values(p_window, &max_potential_edr, nullptr);
|
||||
return max_potential_edr > 1.0f;
|
||||
@@ -611,15 +617,22 @@ void DisplayServerMacOSBase::window_request_hdr_output(const bool p_enabled, Dis
|
||||
ERR_FAIL_COND(!has_window(p_window));
|
||||
if (p_enabled) {
|
||||
bool renderer_supports_hdr_output = false;
|
||||
bool surface_supports_hdr_output = false;
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_device && rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) {
|
||||
renderer_supports_hdr_output = true;
|
||||
surface_supports_hdr_output = rendering_device->screen_get_hdr_output_supported(p_window);
|
||||
}
|
||||
#endif
|
||||
if (!renderer_supports_hdr_output) {
|
||||
WARN_PRINT("HDR output requested, but is not supported by the renderer or rendering device driver.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!surface_supports_hdr_output) {
|
||||
WARN_PRINT("HDR output requested, but the window does not support an HDR format.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HDROutput &hdr = _get_hdr_output(p_window);
|
||||
|
||||
@@ -4781,15 +4781,21 @@ bool DisplayServerWindows::window_is_hdr_output_supported(DisplayServerEnums::Wi
|
||||
|
||||
ERR_FAIL_COND_V(!windows.has(p_window), false);
|
||||
bool renderer_supports_hdr_output = false;
|
||||
bool surface_supports_hdr_output = false;
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_device && rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) {
|
||||
renderer_supports_hdr_output = true;
|
||||
surface_supports_hdr_output = rendering_device->screen_get_hdr_output_supported(p_window);
|
||||
}
|
||||
#endif
|
||||
if (!renderer_supports_hdr_output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!surface_supports_hdr_output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The window supports HDR if the screen it is on supports HDR.
|
||||
DisplayServerWindows::ScreenHdrData data = _get_screen_hdr_data(p_window, false);
|
||||
return data.hdr_supported;
|
||||
@@ -4801,15 +4807,22 @@ void DisplayServerWindows::window_request_hdr_output(const bool p_enable, Displa
|
||||
ERR_FAIL_COND(!windows.has(p_window));
|
||||
if (p_enable) {
|
||||
bool renderer_supports_hdr_output = false;
|
||||
bool surface_supports_hdr_output = false;
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_device && rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) {
|
||||
renderer_supports_hdr_output = true;
|
||||
surface_supports_hdr_output = rendering_device->screen_get_hdr_output_supported(p_window);
|
||||
}
|
||||
#endif
|
||||
if (!renderer_supports_hdr_output) {
|
||||
WARN_PRINT("HDR output requested, but is not supported by the renderer or rendering device driver.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!surface_supports_hdr_output) {
|
||||
WARN_PRINT("HDR output requested, but the window does not support an HDR format.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WindowData &wd = windows[p_window];
|
||||
|
||||
@@ -5467,6 +5467,15 @@ RenderingDevice::ColorSpace RenderingDevice::screen_get_color_space(DisplayServe
|
||||
return color_space;
|
||||
}
|
||||
|
||||
bool RenderingDevice::screen_get_hdr_output_supported(DisplayServerEnums::WindowID p_screen) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
HashMap<DisplayServerEnums::WindowID, RDD::SwapChainID>::ConstIterator it = screen_swap_chains.find(p_screen);
|
||||
ERR_FAIL_COND_V_MSG(it == screen_swap_chains.end(), false, "Screen was never prepared.");
|
||||
|
||||
return driver->swap_chain_get_hdr_output_supported(it->value);
|
||||
}
|
||||
|
||||
Error RenderingDevice::screen_free(DisplayServerEnums::WindowID p_screen) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
|
||||
@@ -1312,6 +1312,7 @@ public:
|
||||
int screen_get_pre_rotation_degrees(DisplayServerEnums::WindowID p_screen = DisplayServerEnums::MAIN_WINDOW_ID) const;
|
||||
FramebufferFormatID screen_get_framebuffer_format(DisplayServerEnums::WindowID p_screen = DisplayServerEnums::MAIN_WINDOW_ID) const;
|
||||
ColorSpace screen_get_color_space(DisplayServerEnums::WindowID p_screen = DisplayServerEnums::MAIN_WINDOW_ID) const;
|
||||
bool screen_get_hdr_output_supported(DisplayServerEnums::WindowID p_screen = DisplayServerEnums::MAIN_WINDOW_ID) const;
|
||||
Error screen_free(DisplayServerEnums::WindowID p_screen = DisplayServerEnums::MAIN_WINDOW_ID);
|
||||
|
||||
private:
|
||||
|
||||
@@ -484,6 +484,9 @@ public:
|
||||
// Retrieve the color space used by the swap chain's framebuffers.
|
||||
virtual ColorSpace swap_chain_get_color_space(SwapChainID p_swap_chain) = 0;
|
||||
|
||||
// Retrieve whether the swapchain supports our preferred HDR formats.
|
||||
virtual bool swap_chain_get_hdr_output_supported(SwapChainID p_swap_chain) = 0;
|
||||
|
||||
// Tells the swapchain the max_fps so it can use the proper frame pacing.
|
||||
// Android uses this with Swappy library. Some implementations or platforms may ignore this hint.
|
||||
virtual void swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) {}
|
||||
|
||||
Reference in New Issue
Block a user