diff --git a/libobs-d3d11/d3d11-duplicator.cpp b/libobs-d3d11/d3d11-duplicator.cpp index 86d1f66aa..132f3a782 100644 --- a/libobs-d3d11/d3d11-duplicator.cpp +++ b/libobs-d3d11/d3d11-duplicator.cpp @@ -45,6 +45,8 @@ void gs_duplicator::Start() throw "Invalid monitor index"; hr = output->QueryInterface(IID_PPV_ARGS(output5.Assign())); + hdr = false; + sdr_white_nits = 80.f; if (SUCCEEDED(hr)) { constexpr DXGI_FORMAT supportedFormats[]{ DXGI_FORMAT_R16G16B16A16_FLOAT, @@ -56,6 +58,13 @@ void gs_duplicator::Start() duplicator.Assign()); if (FAILED(hr)) throw HRError("Failed to DuplicateOutput1", hr); + DXGI_OUTPUT_DESC desc; + if (SUCCEEDED(output->GetDesc(&desc))) { + gs_monitor_color_info info = + device->GetMonitorColorInfo(desc.Monitor); + hdr = info.hdr; + sdr_white_nits = (float)info.sdr_white_nits; + } } else { hr = output->QueryInterface(IID_PPV_ARGS(output1.Assign())); if (FAILED(hr)) @@ -238,6 +247,11 @@ static inline void copy_texture(gs_duplicator_t *d, ID3D11Texture2D *tex) delete d->texture; d->texture = (gs_texture_2d *)gs_texture_create( desc.Width, desc.Height, general_format, 1, nullptr, 0); + d->color_space = d->hdr ? GS_CS_709_SCRGB + : ((desc.Format == + DXGI_FORMAT_R16G16B16A16_FLOAT) + ? GS_CS_SRGB_16F + : GS_CS_SRGB); } if (d->texture) @@ -294,4 +308,15 @@ EXPORT gs_texture_t *gs_duplicator_get_texture(gs_duplicator_t *duplicator) { return duplicator->texture; } + +EXPORT enum gs_color_space +gs_duplicator_get_color_space(gs_duplicator_t *duplicator) +{ + return duplicator->color_space; +} + +EXPORT float gs_duplicator_get_sdr_white_level(gs_duplicator_t *duplicator) +{ + return duplicator->sdr_white_nits; +} } diff --git a/libobs-d3d11/d3d11-subsystem.cpp b/libobs-d3d11/d3d11-subsystem.cpp index 04f8c1120..6e7e0e334 100644 --- a/libobs-d3d11/d3d11-subsystem.cpp +++ b/libobs-d3d11/d3d11-subsystem.cpp @@ -67,53 +67,6 @@ gs_obj::~gs_obj() next->prev_next = prev_next; } -static gs_monitor_color_info get_monitor_color_info(gs_device_t *device, - HMONITOR hMonitor) -{ - IDXGIFactory1 *factory1 = device->factory; - if (!factory1->IsCurrent()) { - device->InitFactory(); - factory1 = device->factory; - device->monitor_to_hdr.clear(); - } - - for (const std::pair &pair : - device->monitor_to_hdr) { - if (pair.first == hMonitor) - return pair.second; - } - - ComPtr adapter; - ComPtr output; - ComPtr output6; - for (UINT adapterIndex = 0; - SUCCEEDED(factory1->EnumAdapters(adapterIndex, &adapter)); - ++adapterIndex) { - for (UINT outputIndex = 0; - SUCCEEDED(adapter->EnumOutputs(outputIndex, &output)); - ++outputIndex) { - if (SUCCEEDED(output->QueryInterface(&output6))) { - DXGI_OUTPUT_DESC1 desc1; - if (SUCCEEDED(output6->GetDesc1(&desc1)) && - (desc1.Monitor == hMonitor)) { - const bool hdr = - desc1.ColorSpace == - DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; - return device->monitor_to_hdr - .emplace_back( - hMonitor, - gs_monitor_color_info( - hdr, - desc1.BitsPerColor)) - .second; - } - } - } - } - - return gs_monitor_color_info(false, 8); -} - static enum gs_color_space get_next_space(gs_device_t *device, HWND hwnd, DXGI_SWAP_EFFECT effect) { @@ -123,7 +76,7 @@ static enum gs_color_space get_next_space(gs_device_t *device, HWND hwnd, MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); if (hMonitor) { const gs_monitor_color_info info = - get_monitor_color_info(device, hMonitor); + device->GetMonitorColorInfo(hMonitor); if (info.hdr) next_space = GS_CS_709_SCRGB; else if (info.bits_per_color > 8) @@ -1170,9 +1123,9 @@ static HRESULT GetPathInfo(HMONITOR hMonitor, return hr; } -static ULONG GetSdrWhiteNits(HMONITOR monitor) +static ULONG GetSdrMaxNits(HMONITOR monitor) { - ULONG nits = 0; + ULONG nits = 80; DISPLAYCONFIG_PATH_INFO info; if (SUCCEEDED(GetPathInfo(monitor, &info))) { @@ -1192,6 +1145,51 @@ static ULONG GetSdrWhiteNits(HMONITOR monitor) return nits; } +gs_monitor_color_info gs_device::GetMonitorColorInfo(HMONITOR hMonitor) +{ + IDXGIFactory1 *factory1 = factory; + if (!factory1->IsCurrent()) { + InitFactory(); + factory1 = factory; + monitor_to_hdr.clear(); + } + + for (const std::pair &pair : + monitor_to_hdr) { + if (pair.first == hMonitor) + return pair.second; + } + + ComPtr adapter; + ComPtr output; + ComPtr output6; + for (UINT adapterIndex = 0; + SUCCEEDED(factory1->EnumAdapters(adapterIndex, &adapter)); + ++adapterIndex) { + for (UINT outputIndex = 0; + SUCCEEDED(adapter->EnumOutputs(outputIndex, &output)); + ++outputIndex) { + DXGI_OUTPUT_DESC1 desc1; + if (SUCCEEDED(output->QueryInterface(&output6)) && + SUCCEEDED(output6->GetDesc1(&desc1)) && + (desc1.Monitor == hMonitor)) { + const bool hdr = + desc1.ColorSpace == + DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + const UINT bits = desc1.BitsPerColor; + const ULONG nits = GetSdrMaxNits(desc1.Monitor); + return monitor_to_hdr + .emplace_back(hMonitor, + gs_monitor_color_info( + hdr, bits, nits)) + .second; + } + } + } + + return gs_monitor_color_info(false, 8, 80); +} + static void PopulateMonitorIds(HMONITOR handle, char *id, char *alt_id, size_t capacity) { @@ -1286,7 +1284,7 @@ static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter) } const RECT &rect = desc.DesktopCoordinates; - const ULONG nits = GetSdrWhiteNits(desc.Monitor); + const ULONG nits = GetSdrMaxNits(desc.Monitor); char *friendly_name; os_wcs_to_utf8_ptr(target.monitorFriendlyDeviceName, 0, @@ -2098,10 +2096,9 @@ bool device_framebuffer_srgb_enabled(gs_device_t *device) return device->curFramebufferSrgb; } -inline void gs_device::CopyTex(ID3D11Texture2D *dst, uint32_t dst_x, - uint32_t dst_y, gs_texture_t *src, - uint32_t src_x, uint32_t src_y, uint32_t src_w, - uint32_t src_h) +void gs_device::CopyTex(ID3D11Texture2D *dst, uint32_t dst_x, uint32_t dst_y, + gs_texture_t *src, uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) { if (src->type != GS_TEXTURE_2D) throw "Source texture must be a 2D texture"; @@ -3090,7 +3087,7 @@ extern "C" EXPORT bool device_p010_available(gs_device_t *device) extern "C" EXPORT bool device_is_monitor_hdr(gs_device_t *device, void *monitor) { const HMONITOR hMonitor = static_cast(monitor); - return get_monitor_color_info(device, hMonitor).hdr; + return device->GetMonitorColorInfo(hMonitor).hdr; } extern "C" EXPORT void device_debug_marker_begin(gs_device_t *, diff --git a/libobs-d3d11/d3d11-subsystem.hpp b/libobs-d3d11/d3d11-subsystem.hpp index 0be9a9e2d..1d167051d 100644 --- a/libobs-d3d11/d3d11-subsystem.hpp +++ b/libobs-d3d11/d3d11-subsystem.hpp @@ -779,6 +779,9 @@ struct gs_vertex_shader : gs_shader { struct gs_duplicator : gs_obj { ComPtr duplicator; gs_texture_2d *texture; + bool hdr = false; + enum gs_color_space color_space = GS_CS_SRGB; + float sdr_white_nits = 80.f; int idx; long refs; bool updated; @@ -982,9 +985,13 @@ struct mat4float { struct gs_monitor_color_info { bool hdr; UINT bits_per_color; + ULONG sdr_white_nits; - gs_monitor_color_info(bool hdr, int bits_per_color) - : hdr(hdr), bits_per_color(bits_per_color) + gs_monitor_color_info(bool hdr, int bits_per_color, + ULONG sdr_white_nits) + : hdr(hdr), + bits_per_color(bits_per_color), + sdr_white_nits(sdr_white_nits) { } }; @@ -1061,9 +1068,9 @@ struct gs_device { void LoadVertexBufferData(); - inline void CopyTex(ID3D11Texture2D *dst, uint32_t dst_x, - uint32_t dst_y, gs_texture_t *src, uint32_t src_x, - uint32_t src_y, uint32_t src_w, uint32_t src_h); + void CopyTex(ID3D11Texture2D *dst, uint32_t dst_x, uint32_t dst_y, + gs_texture_t *src, uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h); void UpdateViewProjMatrix(); @@ -1073,6 +1080,8 @@ struct gs_device { bool HasBadNV12Output(); + gs_monitor_color_info GetMonitorColorInfo(HMONITOR hMonitor); + gs_device(uint32_t adapterIdx); ~gs_device(); }; diff --git a/libobs/graphics/graphics-imports.c b/libobs/graphics/graphics-imports.c index 93e9686c2..d6eaccccc 100644 --- a/libobs/graphics/graphics-imports.c +++ b/libobs/graphics/graphics-imports.c @@ -218,6 +218,8 @@ bool load_graphics_imports(struct gs_exports *exports, void *module, GRAPHICS_IMPORT_OPTIONAL(gs_duplicator_destroy); GRAPHICS_IMPORT_OPTIONAL(gs_duplicator_update_frame); GRAPHICS_IMPORT_OPTIONAL(gs_duplicator_get_texture); + GRAPHICS_IMPORT_OPTIONAL(gs_duplicator_get_color_space); + GRAPHICS_IMPORT_OPTIONAL(gs_duplicator_get_sdr_white_level); GRAPHICS_IMPORT_OPTIONAL(gs_get_adapter_count); GRAPHICS_IMPORT_OPTIONAL(device_texture_create_gdi); GRAPHICS_IMPORT_OPTIONAL(gs_texture_get_dc); diff --git a/libobs/graphics/graphics-internal.h b/libobs/graphics/graphics-internal.h index 137a02196..d0ae5b895 100644 --- a/libobs/graphics/graphics-internal.h +++ b/libobs/graphics/graphics-internal.h @@ -307,6 +307,9 @@ struct gs_exports { bool (*gs_duplicator_update_frame)(gs_duplicator_t *duplicator); gs_texture_t *(*gs_duplicator_get_texture)(gs_duplicator_t *duplicator); + enum gs_color_space (*gs_duplicator_get_color_space)( + gs_duplicator_t *duplicator); + float (*gs_duplicator_get_sdr_white_level)(gs_duplicator_t *duplicator); uint32_t (*gs_get_adapter_count)(void); diff --git a/libobs/graphics/graphics.c b/libobs/graphics/graphics.c index 7bc03dff1..288fb1db6 100644 --- a/libobs/graphics/graphics.c +++ b/libobs/graphics/graphics.c @@ -3050,6 +3050,28 @@ gs_texture_t *gs_duplicator_get_texture(gs_duplicator_t *duplicator) return thread_graphics->exports.gs_duplicator_get_texture(duplicator); } +enum gs_color_space gs_duplicator_get_color_space(gs_duplicator_t *duplicator) +{ + if (!gs_valid_p("gs_duplicator_get_color_space", duplicator)) + return GS_CS_SRGB; + if (!thread_graphics->exports.gs_duplicator_get_color_space) + return GS_CS_SRGB; + + return thread_graphics->exports.gs_duplicator_get_color_space( + duplicator); +} + +float gs_duplicator_get_sdr_white_level(gs_duplicator_t *duplicator) +{ + if (!gs_valid_p("gs_duplicator_get_sdr_white_level", duplicator)) + return 80.f; + if (!thread_graphics->exports.gs_duplicator_get_sdr_white_level) + return 80.f; + + return thread_graphics->exports.gs_duplicator_get_sdr_white_level( + duplicator); +} + /** creates a windows GDI-lockable texture */ gs_texture_t *gs_texture_create_gdi(uint32_t width, uint32_t height) { diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index 5b309d002..0f62d5b95 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -919,6 +919,9 @@ EXPORT void gs_duplicator_destroy(gs_duplicator_t *duplicator); EXPORT bool gs_duplicator_update_frame(gs_duplicator_t *duplicator); EXPORT gs_texture_t *gs_duplicator_get_texture(gs_duplicator_t *duplicator); +EXPORT enum gs_color_space +gs_duplicator_get_color_space(gs_duplicator_t *duplicator); +EXPORT float gs_duplicator_get_sdr_white_level(gs_duplicator_t *duplicator); EXPORT uint32_t gs_get_adapter_count(void); diff --git a/plugins/win-capture/duplicator-monitor-capture.c b/plugins/win-capture/duplicator-monitor-capture.c index 4dd0222a8..69d206235 100644 --- a/plugins/win-capture/duplicator-monitor-capture.c +++ b/plugins/win-capture/duplicator-monitor-capture.c @@ -265,10 +265,11 @@ static void log_settings(struct duplicator_capture *capture, "\tmethod: %s\n" "\tid: %s\n" "\talt_id: %s\n" - "\tsetting_id: %s", + "\tsetting_id: %s\n" + "\tforce SDR: %s", monitor, width, height, capture->capture_cursor ? "true" : "false", get_method_name(capture->method), capture->id, capture->alt_id, - capture->monitor_id); + capture->monitor_id, capture->force_sdr ? "true" : "false"); } static enum display_capture_method @@ -734,18 +735,32 @@ static void duplicator_capture_render(void *data, gs_effect_t *unused) const char *tech_name = "Draw"; float multiplier = 1.f; const enum gs_color_space current_space = gs_get_color_space(); - if (gs_texture_get_color_format(texture) == GS_RGBA16F) { - switch (current_space) { - case GS_CS_SRGB: - case GS_CS_SRGB_16F: - tech_name = "DrawMultiplyTonemap"; - multiplier = - 80.f / obs_get_video_sdr_white_level(); - break; - case GS_CS_709_EXTENDED: + if (gs_duplicator_get_color_space(capture->duplicator) == + GS_CS_709_SCRGB) { + if (capture->force_sdr) { tech_name = "DrawMultiply"; - multiplier = - 80.f / obs_get_video_sdr_white_level(); + const float target_nits = + (current_space == GS_CS_709_SCRGB) + ? obs_get_video_sdr_white_level() + : 80.f; + multiplier = target_nits / + gs_duplicator_get_sdr_white_level( + capture->duplicator); + } else { + switch (current_space) { + case GS_CS_SRGB: + case GS_CS_SRGB_16F: + tech_name = "DrawMultiplyTonemap"; + multiplier = + 80.f / + obs_get_video_sdr_white_level(); + break; + case GS_CS_709_EXTENDED: + tech_name = "DrawMultiply"; + multiplier = + 80.f / + obs_get_video_sdr_white_level(); + } } } else if (current_space == GS_CS_709_SCRGB) { tech_name = "DrawMultiply"; @@ -844,9 +859,6 @@ static void update_settings_visibility(obs_properties_t *props, obs_property_t *p = obs_properties_get(props, "cursor"); obs_property_set_visible(p, dxgi_options || wgc_cursor_toggle); - p = obs_properties_get(props, "force_sdr"); - obs_property_set_visible(p, wgc_options); - pthread_mutex_unlock(&capture->update_mutex); } @@ -909,17 +921,9 @@ duplicator_capture_get_color_space(void *data, size_t count, capture->exports.winrt_capture_get_color_space( capture->capture_winrt); } - } else { - if (capture->duplicator) { - gs_texture_t *const texture = - gs_duplicator_get_texture(capture->duplicator); - if (texture) { - capture_space = (gs_texture_get_color_format( - texture) == GS_RGBA16F) - ? GS_CS_709_EXTENDED - : GS_CS_SRGB; - } - } + } else if (capture->duplicator && !capture->force_sdr) { + capture_space = + gs_duplicator_get_color_space(capture->duplicator); } enum gs_color_space space = capture_space; diff --git a/plugins/win-capture/window-capture.c b/plugins/win-capture/window-capture.c index 6083e5f5e..ebac0812b 100644 --- a/plugins/win-capture/window-capture.c +++ b/plugins/win-capture/window-capture.c @@ -181,9 +181,12 @@ static void log_settings(struct window_capture *wc, obs_data_t *s) "[window-capture: '%s'] update settings:\n" "\texecutable: %s\n" "\tmethod selected: %s\n" - "\tmethod chosen: %s\n", + "\tmethod chosen: %s\n" + "\tforce SDR: %s", obs_source_get_name(wc->source), wc->executable, - get_method_name(method), get_method_name(wc->method)); + get_method_name(method), get_method_name(wc->method), + (wc->force_sdr && (wc->method == METHOD_WGC)) ? "true" + : "false"); blog(LOG_DEBUG, "\tclass: %s", wc->class); } }