diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index 758d65acc01..1b78196fd9f 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -228,6 +228,13 @@ extern GHOST_TSuccess GHOST_DisposeWindow(GHOST_SystemHandle systemhandle, */ extern bool GHOST_ValidWindow(GHOST_SystemHandle systemhandle, GHOST_WindowHandle windowhandle); +/* + ** + * Returns high dynamic range color information about this window. + * \return HDR info. + */ +extern GHOST_WindowHDRInfo GHOST_WindowGetHDRInfo(GHOST_WindowHandle windowhandle); + /** * Get the Window under the cursor. Although coordinates of the mouse are supplied, platform- * specific implementations are free to ignore these and query the mouse location themselves, due diff --git a/intern/ghost/GHOST_IWindow.hh b/intern/ghost/GHOST_IWindow.hh index 3b04edf2471..b7a95bed10b 100644 --- a/intern/ghost/GHOST_IWindow.hh +++ b/intern/ghost/GHOST_IWindow.hh @@ -379,6 +379,12 @@ class GHOST_IWindow { */ virtual uint16_t getDPIHint() = 0; + /** + * Returns high dynamic range color information about this window. + * \return HDR info. + * */ + virtual GHOST_WindowHDRInfo getHDRInfo() = 0; + #ifdef WITH_INPUT_IME /** * Enable IME attached to the given window, i.e. allows user-input diff --git a/intern/ghost/intern/GHOST_C-api.cc b/intern/ghost/intern/GHOST_C-api.cc index e9686336d88..c10e0b20f24 100644 --- a/intern/ghost/intern/GHOST_C-api.cc +++ b/intern/ghost/intern/GHOST_C-api.cc @@ -217,6 +217,12 @@ bool GHOST_ValidWindow(GHOST_SystemHandle systemhandle, GHOST_WindowHandle windo return system->validWindow(window); } +GHOST_WindowHDRInfo GHOST_WindowGetHDRInfo(GHOST_WindowHandle windowhandle) +{ + GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; + return window->getHDRInfo(); +} + GHOST_WindowHandle GHOST_GetWindowUnderCursor(GHOST_SystemHandle systemhandle, int32_t x, int32_t y) diff --git a/intern/ghost/intern/GHOST_ContextVK.cc b/intern/ghost/intern/GHOST_ContextVK.cc index cd088e3e979..9cc79d86a9a 100644 --- a/intern/ghost/intern/GHOST_ContextVK.cc +++ b/intern/ghost/intern/GHOST_ContextVK.cc @@ -598,7 +598,8 @@ GHOST_ContextVK::GHOST_ContextVK(const GHOST_ContextParams &context_params, surface_(VK_NULL_HANDLE), swapchain_(VK_NULL_HANDLE), frame_data_(GHOST_FRAMES_IN_FLIGHT), - render_frame_(0) + render_frame_(0), + use_hdr_swapchain_(false) { } @@ -648,24 +649,31 @@ GHOST_TSuccess GHOST_ContextVK::swapBuffers() vkWaitForFences(device, 1, &submission_frame_data.submission_fence, true, UINT64_MAX); } submission_frame_data.discard_pile.destroy(device); - bool use_hdr_swapchain = true; -#ifdef WITH_GHOST_WAYLAND - /* Wayland doesn't provide a WSI with windowing capabilities, therefore cannot detect whether the - * swap-chain needs to be recreated. But as a side effect we can recreate the swap-chain before - * presenting. */ - if (wayland_window_info_) { - const bool recreate_swapchain = ((wayland_window_info_->size[0] != - std::max(render_extent_.width, render_extent_min_.width)) || - (wayland_window_info_->size[1] != - std::max(render_extent_.height, render_extent_min_.height))); - use_hdr_swapchain = wayland_window_info_->is_color_managed; - if (recreate_swapchain) { - /* Swap-chain is out of date. Recreate swap-chain. */ - recreateSwapchain(use_hdr_swapchain); - } + const bool use_hdr_swapchain = hdr_info_ && hdr_info_->hdr_enabled; + if (use_hdr_swapchain != use_hdr_swapchain_) { + /* Re-create swapchain if HDR mode was toggled in the system settings. */ + recreateSwapchain(use_hdr_swapchain); } + else { +#ifdef WITH_GHOST_WAYLAND + /* Wayland doesn't provide a WSI with windowing capabilities, therefore cannot detect whether + * the swap-chain needs to be recreated. But as a side effect we can recreate the swap-chain + * before presenting. */ + if (wayland_window_info_) { + const bool recreate_swapchain = + ((wayland_window_info_->size[0] != + std::max(render_extent_.width, render_extent_min_.width)) || + (wayland_window_info_->size[1] != + std::max(render_extent_.height, render_extent_min_.height))); + + if (recreate_swapchain) { + /* Swap-chain is out of date. Recreate swap-chain. */ + recreateSwapchain(use_hdr_swapchain); + } + } #endif + } /* There is no valid swapchain as the previous window was minimized. User can have maximized the * window so we need to check if the swapchain can be created. */ if (swapchain_ == VK_NULL_HANDLE) { @@ -1014,6 +1022,7 @@ GHOST_TSuccess GHOST_ContextVK::recreateSwapchain(bool use_hdr_swapchain) vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface_, &capabilities); } + use_hdr_swapchain_ = use_hdr_swapchain; render_extent_ = capabilities.currentExtent; render_extent_min_ = capabilities.minImageExtent; if (render_extent_.width == UINT32_MAX) { diff --git a/intern/ghost/intern/GHOST_ContextVK.hh b/intern/ghost/intern/GHOST_ContextVK.hh index 2582f23c1cc..1b0c1afc51c 100644 --- a/intern/ghost/intern/GHOST_ContextVK.hh +++ b/intern/ghost/intern/GHOST_ContextVK.hh @@ -56,7 +56,6 @@ enum GHOST_TVulkanPlatformType { struct GHOST_ContextVK_WindowInfo { int size[2]; - bool is_color_managed; }; struct GHOST_FrameDiscard { @@ -245,6 +244,7 @@ class GHOST_ContextVK : public GHOST_Context { VkExtent2D render_extent_; VkExtent2D render_extent_min_; VkSurfaceFormatKHR surface_format_; + bool use_hdr_swapchain_; std::function swap_buffers_pre_callback_; std::function swap_buffers_post_callback_; diff --git a/intern/ghost/intern/GHOST_SystemWin32.cc b/intern/ghost/intern/GHOST_SystemWin32.cc index 2a527915d75..5b937f6ef81 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cc +++ b/intern/ghost/intern/GHOST_SystemWin32.cc @@ -2361,6 +2361,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, uint msg, WPARAM wParam, * another window-management function. */ case WM_SETFOCUS: { /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */ + window->updateHDRInfo(); break; } /* ============ diff --git a/intern/ghost/intern/GHOST_Window.hh b/intern/ghost/intern/GHOST_Window.hh index e6899c45489..aaa8c4fef65 100644 --- a/intern/ghost/intern/GHOST_Window.hh +++ b/intern/ghost/intern/GHOST_Window.hh @@ -250,6 +250,12 @@ class GHOST_Window : public GHOST_IWindow { return 96; } + /** \copydoc #GHOST_IWindow::getHDRInfo */ + GHOST_WindowHDRInfo getHDRInfo() override + { + return hdr_info_; + } + #ifdef WITH_INPUT_IME void beginIME( int32_t /*x*/, int32_t /*y*/, int32_t /*w*/, int32_t /*h*/, bool /*completed*/) override @@ -358,6 +364,8 @@ class GHOST_Window : public GHOST_IWindow { /* OSX only, retina screens */ float native_pixel_size_; + GHOST_WindowHDRInfo hdr_info_ = GHOST_WINDOW_HDR_INFO_NONE; + private: GHOST_Context *context_; }; diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm index cb47c4b280f..b821fd13c4b 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.mm +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -401,6 +401,10 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa, CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(name); metal_layer_.colorspace = colorspace; CGColorSpaceRelease(colorspace); + + /* For Blender to know if this window supports HDR. */ + hdr_info_.hdr_enabled = true; + hdr_info_.sdr_white_level = 1.0f; } metal_view_ = [[CocoaMetalView alloc] initWithSystemCocoa:systemCocoa @@ -898,7 +902,7 @@ GHOST_Context *GHOST_WindowCocoa::newDrawingContext(GHOST_TDrawingContextType ty #ifdef WITH_VULKAN_BACKEND case GHOST_kDrawingContextTypeVulkan: { GHOST_Context *context = new GHOST_ContextVK( - want_context_params_, metal_layer_, 1, 2, true, preferred_device_); + want_context_params_, metal_layer_, 1, 2, true, preferred_device_, &hdr_info_); if (context->initializeDrawingContext()) { return context; } diff --git a/intern/ghost/intern/GHOST_WindowWayland.cc b/intern/ghost/intern/GHOST_WindowWayland.cc index 0e4d9e1329f..954e812be26 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cc +++ b/intern/ghost/intern/GHOST_WindowWayland.cc @@ -1991,7 +1991,12 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, window_->backend.vulkan_window_info = new GHOST_ContextVK_WindowInfo; window_->backend.vulkan_window_info->size[0] = window_->frame.size[0]; window_->backend.vulkan_window_info->size[1] = window_->frame.size[1]; - window_->backend.vulkan_window_info->is_color_managed = true; + + /* There is no HDR on/off settings as on Windows, so from the Window side + * consider it always enabled. But may still get disabled if Vulkan has no + * appropriate surface format. */ + hdr_info_.hdr_enabled = true; + hdr_info_.sdr_white_level = 1.0f; } #endif @@ -2472,7 +2477,8 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType window_->backend.vulkan_window_info, 1, 2, - preferred_device_); + preferred_device_, + &hdr_info_); if (context->initializeDrawingContext()) { return context; } diff --git a/intern/ghost/intern/GHOST_WindowWin32.cc b/intern/ghost/intern/GHOST_WindowWin32.cc index 1b92e7d0a4c..2e15690cce6 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cc +++ b/intern/ghost/intern/GHOST_WindowWin32.cc @@ -1326,7 +1326,10 @@ void GHOST_WindowWin32::updateHDRInfo() color_info.header.id = path.targetInfo.id; if (::DisplayConfigGetDeviceInfo(&color_info.header) == ERROR_SUCCESS) { - info.hdr_enabled = color_info.advancedColorSupported && color_info.advancedColorEnabled; + /* This particular combination indicates HDR mode is enabled. This is undocumented but + * used by WinRT. When wideColorEnforced is true we are in SDR mode with advanced color. */ + info.hdr_enabled = color_info.advancedColorSupported && color_info.advancedColorEnabled && + !color_info.wideColorEnforced; } if (info.hdr_enabled) { diff --git a/intern/ghost/intern/GHOST_WindowWin32.hh b/intern/ghost/intern/GHOST_WindowWin32.hh index 624515fe1ff..76c545bb96a 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.hh +++ b/intern/ghost/intern/GHOST_WindowWin32.hh @@ -415,8 +415,6 @@ class GHOST_WindowWin32 : public GHOST_Window { GHOST_DirectManipulationHelper *direct_manipulation_helper_; - GHOST_WindowHDRInfo hdr_info_ = GHOST_WINDOW_HDR_INFO_NONE; - #ifdef WITH_INPUT_IME /** Handle input method editors event */ GHOST_ImeWin32 ime_input_; diff --git a/intern/ghost/intern/GHOST_WindowX11.cc b/intern/ghost/intern/GHOST_WindowX11.cc index 242237233d8..23197389639 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cc +++ b/intern/ghost/intern/GHOST_WindowX11.cc @@ -1191,7 +1191,8 @@ GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type nullptr, 1, 2, - preferred_device_); + preferred_device_, + &hdr_info_); if (context->initializeDrawingContext()) { return context; } diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index 19025d5743c..78f56565429 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -76,12 +76,10 @@ class RENDER_PT_color_management(RenderButtonsPanel, Panel): col.prop(view, "view_transform") col.prop(view, "look") - if view.is_hdr: - import gpu - if not gpu.capabilities.hdr_support_get(): - row = col.split(factor=0.4) - row.label() - row.label(text="HDR display not supported", icon="INFO") + if view.is_hdr and not context.window.support_hdr_color: + row = col.split(factor=0.4) + row.label() + row.label(text="HDR display not supported", icon="INFO") col = flow.column() col.prop(view, "exposure") diff --git a/source/blender/makesrna/intern/rna_wm.cc b/source/blender/makesrna/intern/rna_wm.cc index 7a59666d1bf..f6811f30484 100644 --- a/source/blender/makesrna/intern/rna_wm.cc +++ b/source/blender/makesrna/intern/rna_wm.cc @@ -1049,6 +1049,12 @@ static void rna_Window_view_layer_set(PointerRNA *ptr, PointerRNA value, ReportL WM_window_set_active_view_layer(win, view_layer); } +static bool rna_Window_support_hdr_color_get(PointerRNA *ptr) +{ + wmWindow *win = static_cast(ptr->data); + return WM_window_support_hdr_color(win); +} + static bool rna_Window_modal_handler_skip(CollectionPropertyIterator * /*iter*/, void *data) { const wmEventHandler_Op *handler = (wmEventHandler_Op *)data; @@ -2782,6 +2788,14 @@ static void rna_def_window(BlenderRNA *brna) RNA_def_property_struct_type(prop, "Stereo3dDisplay"); RNA_def_property_ui_text(prop, "Stereo 3D Display", "Settings for stereo 3D display"); + prop = RNA_def_property(srna, "support_hdr_color", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, + "Support HDR Color", + "The window has a HDR graphics buffer that wide gamut and high dynamic " + "range colors can be written to, in extended sRGB color space."); + RNA_def_property_boolean_funcs(prop, "rna_Window_support_hdr_color_get", nullptr); + prop = RNA_def_property(srna, "modal_operators", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "Operator"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); diff --git a/source/blender/windowmanager/WM_api.hh b/source/blender/windowmanager/WM_api.hh index aaab582081b..178457aa316 100644 --- a/source/blender/windowmanager/WM_api.hh +++ b/source/blender/windowmanager/WM_api.hh @@ -316,6 +316,11 @@ bool WM_window_is_main_top_level(const wmWindow *win); bool WM_window_is_fullscreen(const wmWindow *win); bool WM_window_is_maximized(const wmWindow *win); +/* + * Support for wide gamut and HDR colors. + */ +bool WM_window_support_hdr_color(const wmWindow *win); + /** * Some editor data may need to be synced with scene data (3D View camera and layers). * This function ensures data is synced for editors diff --git a/source/blender/windowmanager/intern/wm_window.cc b/source/blender/windowmanager/intern/wm_window.cc index d2d5aeaf192..9d5ca7a67a7 100644 --- a/source/blender/windowmanager/intern/wm_window.cc +++ b/source/blender/windowmanager/intern/wm_window.cc @@ -83,6 +83,7 @@ #include "UI_interface_layout.hh" #include "BLF_api.hh" +#include "GPU_capabilities.hh" #include "GPU_context.hh" #include "GPU_framebuffer.hh" #include "GPU_init_exit.hh" @@ -2895,6 +2896,12 @@ bool WM_window_is_main_top_level(const wmWindow *win) return true; } +bool WM_window_support_hdr_color(const wmWindow *win) +{ + return GPU_hdr_support() && win->ghostwin && + GHOST_WindowGetHDRInfo(static_cast(win->ghostwin)).hdr_enabled; +} + /** \} */ /* -------------------------------------------------------------------- */