diff --git a/docs/sphinx/reference-libobs-graphics-graphics.rst b/docs/sphinx/reference-libobs-graphics-graphics.rst index d1f83bb28..4fc8bdd76 100644 --- a/docs/sphinx/reference-libobs-graphics-graphics.rst +++ b/docs/sphinx/reference-libobs-graphics-graphics.rst @@ -47,6 +47,14 @@ Graphics Enumerations - GS_BGRA_UNORM - BGRA, 8 bits per channel, no SRGB aliasing - GS_RG16 - RG, 16 bits per channel +.. type:: enum gs_color_space + + Color space. Can be one of the following values: + + - GS_CS_SRGB - sRGB + - GS_CS_709_EXTENDED - Canvas, Mac EDR (HDR) + - GS_CS_709_SCRGB - 1.0 = 80 nits, Windows/Linux HDR + .. type:: enum gs_zstencil_format Z-Stencil buffer format. Can be one of the following values: @@ -542,6 +550,13 @@ Swap Chains --------------------- +.. function:: void gs_update_color_space(void) + + Updates the color space of the swap chain based on the HDR status of + the nearest monitor + +--------------------- + .. function:: void gs_get_size(uint32_t *cx, uint32_t *cy) Gets the size of the currently active swap chain @@ -613,6 +628,12 @@ Resource Loading Draw Functions -------------- +.. function:: enum gs_color_space gs_get_color_space(void) + + :return: The currently active color space + +--------------------- + .. function:: gs_texture_t *gs_get_render_target(void) :return: The currently active render target @@ -627,13 +648,23 @@ Draw Functions .. function:: void gs_set_render_target(gs_texture_t *tex, gs_zstencil_t *zstencil) - Sets the active render target + Sets the active render target with implicit GS_CS_SRGB color space :param tex: Texture to set as the active render target :param zstencil: Z-stencil to use as the active render target --------------------- +.. function:: void gs_set_render_target_with_color_space(gs_texture_t *tex, gs_zstencil_t *zstencil, enum gs_color_space space) + + Sets the active render target along with color space + + :param tex: Texture to set as the active render target + :param zstencil: Z-stencil to use as the active render target + :param space: Color space of the render target + +--------------------- + .. function:: void gs_set_cube_render_target(gs_texture_t *cubetex, int side, gs_zstencil_t *zstencil) Sets a cubemap side as the active render target diff --git a/libobs-d3d11/d3d11-subsystem.cpp b/libobs-d3d11/d3d11-subsystem.cpp index 40d52627b..1d90f7c77 100644 --- a/libobs-d3d11/d3d11-subsystem.cpp +++ b/libobs-d3d11/d3d11-subsystem.cpp @@ -66,21 +66,78 @@ gs_obj::~gs_obj() next->prev_next = prev_next; } -static inline void make_swap_desc(DXGI_SWAP_CHAIN_DESC &desc, - const gs_init_data *data, - DXGI_SWAP_EFFECT effect, UINT flags) +static bool screen_supports_hdr(gs_device_t *device, HMONITOR hMonitor) { + IDXGIFactory1 *factory1 = device->factory; + if (!factory1->IsCurrent()) { + device->InitFactory(); + factory1 = device->factory; + } + + 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) { + return desc1.ColorSpace == + DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + } + } + } + } + + return false; +} + +static enum gs_color_space get_next_space(gs_device_t *device, HWND hwnd) +{ + enum gs_color_space next_space = GS_CS_SRGB; + const HMONITOR hMonitor = + MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + if (hMonitor) { + if (screen_supports_hdr(device, hMonitor)) + next_space = GS_CS_709_SCRGB; + } + + return next_space; +} + +static enum gs_color_format +get_swap_format_from_space(gs_color_space space, gs_color_format sdr_format) +{ + return (space == GS_CS_709_SCRGB) ? GS_RGBA16F : sdr_format; +} + +static inline enum gs_color_space +make_swap_desc(gs_device *device, DXGI_SWAP_CHAIN_DESC &desc, + const gs_init_data *data, DXGI_SWAP_EFFECT effect, UINT flags) +{ + const HWND hwnd = (HWND)data->window.hwnd; + const enum gs_color_space space = get_next_space(device, hwnd); + const gs_color_format format = + get_swap_format_from_space(space, data->format); + memset(&desc, 0, sizeof(desc)); desc.BufferDesc.Width = data->cx; desc.BufferDesc.Height = data->cy; - desc.BufferDesc.Format = ConvertGSTextureFormatView(data->format); + desc.BufferDesc.Format = ConvertGSTextureFormatView(format); desc.SampleDesc.Count = 1; desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; desc.BufferCount = data->num_backbuffers; - desc.OutputWindow = (HWND)data->window.hwnd; + desc.OutputWindow = hwnd; desc.Windowed = TRUE; desc.SwapEffect = effect; desc.Flags = flags; + + return space; } void gs_swap_chain::InitTarget(uint32_t cx, uint32_t cy) @@ -128,7 +185,7 @@ void gs_swap_chain::InitZStencilBuffer(uint32_t cx, uint32_t cy) } } -void gs_swap_chain::Resize(uint32_t cx, uint32_t cy) +void gs_swap_chain::Resize(uint32_t cx, uint32_t cy, gs_color_format format) { RECT clientRect; HRESULT hr; @@ -150,25 +207,40 @@ void gs_swap_chain::Resize(uint32_t cx, uint32_t cy) cy = clientRect.bottom; } - hr = swap->ResizeBuffers(swapDesc.BufferCount, cx, cy, - DXGI_FORMAT_UNKNOWN, swapDesc.Flags); + const DXGI_FORMAT dxgi_format = ConvertGSTextureFormatView(format); + hr = swap->ResizeBuffers(swapDesc.BufferCount, cx, cy, dxgi_format, + swapDesc.Flags); if (FAILED(hr)) throw HRError("Failed to resize swap buffers", hr); + ComQIPtr swap3 = swap; + if (swap3) { + const DXGI_COLOR_SPACE_TYPE dxgi_space = + (format == GS_RGBA16F) + ? DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 + : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + hr = swap3->SetColorSpace1(dxgi_space); + if (FAILED(hr)) + throw HRError("Failed to set color space", hr); + } + target.dxgiFormatResource = ConvertGSTextureFormatResource(format); + target.dxgiFormatView = dxgi_format; + target.dxgiFormatViewLinear = ConvertGSTextureFormatViewLinear(format); InitTarget(cx, cy); InitZStencilBuffer(cx, cy); } void gs_swap_chain::Init() { + const gs_color_format format = get_swap_format_from_space( + get_next_space(device, hwnd), initData.format); + target.device = device; target.isRenderTarget = true; target.format = initData.format; - target.dxgiFormatResource = - ConvertGSTextureFormatResource(initData.format); - target.dxgiFormatView = ConvertGSTextureFormatView(initData.format); - target.dxgiFormatViewLinear = - ConvertGSTextureFormatViewLinear(initData.format); + target.dxgiFormatResource = ConvertGSTextureFormatResource(format); + target.dxgiFormatView = ConvertGSTextureFormatView(format); + target.dxgiFormatViewLinear = ConvertGSTextureFormatViewLinear(format); InitTarget(initData.cx, initData.cy); zs.device = device; @@ -180,7 +252,8 @@ void gs_swap_chain::Init() gs_swap_chain::gs_swap_chain(gs_device *device, const gs_init_data *data) : gs_obj(device, gs_type::gs_swap_chain), hwnd((HWND)data->window.hwnd), - initData(*data) + initData(*data), + space(GS_CS_SRGB) { DXGI_SWAP_EFFECT effect = DXGI_SWAP_EFFECT_DISCARD; UINT flags = 0; @@ -203,7 +276,7 @@ gs_swap_chain::gs_swap_chain(gs_device *device, const gs_init_data *data) } } - make_swap_desc(swapDesc, &initData, effect, flags); + space = make_swap_desc(device, swapDesc, &initData, effect, flags); HRESULT hr = device->factory->CreateSwapChain(device->device, &swapDesc, swap.Assign()); if (FAILED(hr)) @@ -1340,6 +1413,24 @@ gs_swapchain_t *device_swapchain_create(gs_device_t *device, return swap; } +static void device_resize_internal(gs_device_t *device, uint32_t cx, + uint32_t cy, gs_color_space space) +{ + try { + const gs_color_format format = get_swap_format_from_space( + space, device->curSwapChain->initData.format); + + device->context->OMSetRenderTargets(0, NULL, NULL); + device->curSwapChain->Resize(cx, cy, format); + device->curSwapChain->space = space; + device->curFramebufferInvalidate = true; + } catch (const HRError &error) { + blog(LOG_ERROR, "device_resize_internal (D3D11): %s (%08lX)", + error.str, error.hr); + LogD3D11ErrorDetails(error, device); + } +} + void device_resize(gs_device_t *device, uint32_t cx, uint32_t cy) { if (!device->curSwapChain) { @@ -1347,14 +1438,26 @@ void device_resize(gs_device_t *device, uint32_t cx, uint32_t cy) return; } - try { - device->context->OMSetRenderTargets(0, NULL, NULL); - device->curSwapChain->Resize(cx, cy); - device->curFramebufferInvalidate = true; - } catch (const HRError &error) { - blog(LOG_ERROR, "device_resize (D3D11): %s (%08lX)", error.str, - error.hr); - LogD3D11ErrorDetails(error, device); + const enum gs_color_space next_space = + get_next_space(device, device->curSwapChain->hwnd); + device_resize_internal(device, cx, cy, next_space); +} + +enum gs_color_space device_get_color_space(gs_device_t *device) +{ + return device->curColorSpace; +} + +void device_update_color_space(gs_device_t *device) +{ + if (device->curSwapChain) { + const enum gs_color_space next_space = + get_next_space(device, device->curSwapChain->hwnd); + if (device->curSwapChain->space != next_space) + device_resize_internal(device, 0, 0, next_space); + } else { + blog(LOG_WARNING, + "device_update_color_space (D3D11): No active swap"); } } @@ -1874,8 +1977,10 @@ gs_zstencil_t *device_get_zstencil_target(const gs_device_t *device) return device->curZStencilBuffer; } -void device_set_render_target(gs_device_t *device, gs_texture_t *tex, - gs_zstencil_t *zstencil) +static void device_set_render_target_internal(gs_device_t *device, + gs_texture_t *tex, + gs_zstencil_t *zstencil, + enum gs_color_space space) { if (device->curSwapChain) { if (!tex) @@ -1885,12 +1990,13 @@ void device_set_render_target(gs_device_t *device, gs_texture_t *tex, } if (device->curRenderTarget == tex && - device->curZStencilBuffer == zstencil) - return; + device->curZStencilBuffer == zstencil) { + device->curColorSpace = space; + } if (tex && tex->type != GS_TEXTURE_2D) { blog(LOG_ERROR, - "device_set_render_target (D3D11): texture is not a 2D texture"); + "device_set_render_target_internal (D3D11): texture is not a 2D texture"); return; } @@ -1898,12 +2004,27 @@ void device_set_render_target(gs_device_t *device, gs_texture_t *tex, if (device->curRenderTarget != tex2d || device->curRenderSide != 0 || device->curZStencilBuffer != zstencil) { device->curRenderTarget = tex2d; - device->curRenderSide = 0; device->curZStencilBuffer = zstencil; + device->curRenderSide = 0; + device->curColorSpace = space; device->curFramebufferInvalidate = true; } } +void device_set_render_target(gs_device_t *device, gs_texture_t *tex, + gs_zstencil_t *zstencil) +{ + device_set_render_target_internal(device, tex, zstencil, GS_CS_SRGB); +} + +void device_set_render_target_with_color_space(gs_device_t *device, + gs_texture_t *tex, + gs_zstencil_t *zstencil, + enum gs_color_space space) +{ + device_set_render_target_internal(device, tex, zstencil, space); +} + void device_set_cube_render_target(gs_device_t *device, gs_texture_t *tex, int side, gs_zstencil_t *zstencil) { @@ -1931,8 +2052,9 @@ void device_set_cube_render_target(gs_device_t *device, gs_texture_t *tex, if (device->curRenderTarget != tex2d || device->curRenderSide != side || device->curZStencilBuffer != zstencil) { device->curRenderTarget = tex2d; - device->curRenderSide = side; device->curZStencilBuffer = zstencil; + device->curRenderSide = side; + device->curColorSpace = GS_CS_SRGB; device->curFramebufferInvalidate = true; } } @@ -2177,11 +2299,14 @@ void device_load_swapchain(gs_device_t *device, gs_swapchain_t *swapchain) device->curSwapChain = swapchain; - if (is_cube) + if (is_cube) { device_set_cube_render_target(device, target, device->curRenderSide, zs); - else - device_set_render_target(device, target, zs); + } else { + const enum gs_color_space space = swapchain ? swapchain->space + : GS_CS_SRGB; + device_set_render_target_internal(device, target, zs, space); + } } void device_clear(gs_device_t *device, uint32_t clear_flags, diff --git a/libobs-d3d11/d3d11-subsystem.hpp b/libobs-d3d11/d3d11-subsystem.hpp index f693e52f4..34297e00b 100644 --- a/libobs-d3d11/d3d11-subsystem.hpp +++ b/libobs-d3d11/d3d11-subsystem.hpp @@ -820,6 +820,7 @@ struct gs_swap_chain : gs_obj { HWND hwnd; gs_init_data initData; DXGI_SWAP_CHAIN_DESC swapDesc = {}; + gs_color_space space; UINT presentFlags = 0; gs_texture_2d target; @@ -829,7 +830,7 @@ struct gs_swap_chain : gs_obj { void InitTarget(uint32_t cx, uint32_t cy); void InitZStencilBuffer(uint32_t cx, uint32_t cy); - void Resize(uint32_t cx, uint32_t cy); + void Resize(uint32_t cx, uint32_t cy, gs_color_format format); void Init(); void Rebuild(ID3D11Device *dev); @@ -991,6 +992,7 @@ struct gs_device { gs_texture_2d *curRenderTarget = nullptr; gs_zstencil_buffer *curZStencilBuffer = nullptr; int curRenderSide = 0; + enum gs_color_space curColorSpace = GS_CS_SRGB; bool curFramebufferSrgb = false; bool curFramebufferInvalidate = false; gs_texture *curTextures[GS_MAX_TEXTURES]; diff --git a/libobs-opengl/gl-subsystem.c b/libobs-opengl/gl-subsystem.c index e49c89788..d8969b329 100644 --- a/libobs-opengl/gl-subsystem.c +++ b/libobs-opengl/gl-subsystem.c @@ -340,6 +340,17 @@ void device_resize(gs_device_t *device, uint32_t cx, uint32_t cy) gl_update(device); } +enum gs_color_space device_get_color_space(gs_device_t *device) +{ + return device->cur_color_space; +} + +void device_update_color_space(gs_device_t *device) +{ + if (!device->cur_swap) + blog(LOG_WARNING, "device_display_change (GL): No active swap"); +} + void device_get_size(const gs_device_t *device, uint32_t *cx, uint32_t *cy) { if (device->cur_swap) { @@ -819,9 +830,9 @@ static bool attach_zstencil(struct fbo_info *fbo, gs_zstencil_t *zs) } static bool set_target(gs_device_t *device, gs_texture_t *tex, int side, - gs_zstencil_t *zs) + gs_zstencil_t *zs, enum gs_color_space space) { - struct fbo_info *fbo; + device->cur_color_space = space; if (device->cur_render_target == tex && device->cur_zstencil_buffer == zs && @@ -835,7 +846,7 @@ static bool set_target(gs_device_t *device, gs_texture_t *tex, int side, if (!tex) return set_current_fbo(device, NULL); - fbo = get_fbo_by_tex(tex); + struct fbo_info *const fbo = get_fbo_by_tex(tex); if (!fbo) return false; @@ -864,7 +875,7 @@ void device_set_render_target(gs_device_t *device, gs_texture_t *tex, } } - if (!set_target(device, tex, 0, zstencil)) + if (!set_target(device, tex, 0, zstencil, GS_CS_SRGB)) goto fail; return; @@ -873,6 +884,33 @@ fail: blog(LOG_ERROR, "device_set_render_target (GL) failed"); } +void device_set_render_target_with_color_space(gs_device_t *device, + gs_texture_t *tex, + gs_zstencil_t *zstencil, + enum gs_color_space space) +{ + if (tex) { + if (tex->type != GS_TEXTURE_2D) { + blog(LOG_ERROR, "Texture is not a 2D texture"); + goto fail; + } + + if (!tex->is_render_target) { + blog(LOG_ERROR, "Texture is not a render target"); + goto fail; + } + } + + if (!set_target(device, tex, 0, zstencil, space)) + goto fail; + + return; + +fail: + blog(LOG_ERROR, + "device_set_render_target_with_color_space (GL) failed"); +} + void device_set_cube_render_target(gs_device_t *device, gs_texture_t *cubetex, int side, gs_zstencil_t *zstencil) { @@ -888,7 +926,7 @@ void device_set_cube_render_target(gs_device_t *device, gs_texture_t *cubetex, } } - if (!set_target(device, cubetex, side, zstencil)) + if (!set_target(device, cubetex, side, zstencil, GS_CS_SRGB)) goto fail; return; diff --git a/libobs-opengl/gl-subsystem.h b/libobs-opengl/gl-subsystem.h index 513ba179a..daddffffc 100644 --- a/libobs-opengl/gl-subsystem.h +++ b/libobs-opengl/gl-subsystem.h @@ -653,6 +653,7 @@ struct gs_device { gs_shader_t *cur_pixel_shader; gs_swapchain_t *cur_swap; struct gs_program *cur_program; + enum gs_color_space cur_color_space; struct gs_program *first_program; diff --git a/libobs/graphics/device-exports.h b/libobs/graphics/device-exports.h index 55ebaf2be..290d33f94 100644 --- a/libobs/graphics/device-exports.h +++ b/libobs/graphics/device-exports.h @@ -37,6 +37,8 @@ EXPORT void *device_get_device_obj(gs_device_t *device); EXPORT gs_swapchain_t *device_swapchain_create(gs_device_t *device, const struct gs_init_data *data); EXPORT void device_resize(gs_device_t *device, uint32_t x, uint32_t y); +EXPORT enum gs_color_space device_get_color_space(gs_device_t *device); +EXPORT void device_update_color_space(gs_device_t *device); EXPORT void device_get_size(const gs_device_t *device, uint32_t *x, uint32_t *y); EXPORT uint32_t device_get_width(const gs_device_t *device); @@ -104,6 +106,9 @@ EXPORT gs_texture_t *device_get_render_target(const gs_device_t *device); EXPORT gs_zstencil_t *device_get_zstencil_target(const gs_device_t *device); EXPORT void device_set_render_target(gs_device_t *device, gs_texture_t *tex, gs_zstencil_t *zstencil); +EXPORT void device_set_render_target_with_color_space( + gs_device_t *device, gs_texture_t *tex, gs_zstencil_t *zstencil, + enum gs_color_space space); EXPORT void device_set_cube_render_target(gs_device_t *device, gs_texture_t *cubetex, int side, gs_zstencil_t *zstencil); diff --git a/libobs/graphics/graphics-imports.c b/libobs/graphics/graphics-imports.c index 7b92b2994..48b7f8c3b 100644 --- a/libobs/graphics/graphics-imports.c +++ b/libobs/graphics/graphics-imports.c @@ -53,6 +53,8 @@ bool load_graphics_imports(struct gs_exports *exports, void *module, GRAPHICS_IMPORT(device_get_device_obj); GRAPHICS_IMPORT(device_swapchain_create); GRAPHICS_IMPORT(device_resize); + GRAPHICS_IMPORT(device_get_color_space); + GRAPHICS_IMPORT(device_update_color_space); GRAPHICS_IMPORT(device_get_size); GRAPHICS_IMPORT(device_get_width); GRAPHICS_IMPORT(device_get_height); @@ -81,6 +83,7 @@ bool load_graphics_imports(struct gs_exports *exports, void *module, GRAPHICS_IMPORT(device_get_render_target); GRAPHICS_IMPORT(device_get_zstencil_target); GRAPHICS_IMPORT(device_set_render_target); + GRAPHICS_IMPORT(device_set_render_target_with_color_space); GRAPHICS_IMPORT(device_set_cube_render_target); GRAPHICS_IMPORT(device_enable_framebuffer_srgb); GRAPHICS_IMPORT(device_framebuffer_srgb_enabled); diff --git a/libobs/graphics/graphics-internal.h b/libobs/graphics/graphics-internal.h index 706632478..a6d7e4eb2 100644 --- a/libobs/graphics/graphics-internal.h +++ b/libobs/graphics/graphics-internal.h @@ -38,6 +38,8 @@ struct gs_exports { gs_swapchain_t *(*device_swapchain_create)( gs_device_t *device, const struct gs_init_data *data); void (*device_resize)(gs_device_t *device, uint32_t x, uint32_t y); + enum gs_color_space (*device_get_color_space)(gs_device_t *device); + void (*device_update_color_space)(gs_device_t *device); void (*device_get_size)(const gs_device_t *device, uint32_t *x, uint32_t *y); uint32_t (*device_get_width)(const gs_device_t *device); @@ -103,6 +105,9 @@ struct gs_exports { gs_zstencil_t *(*device_get_zstencil_target)(const gs_device_t *device); void (*device_set_render_target)(gs_device_t *device, gs_texture_t *tex, gs_zstencil_t *zstencil); + void (*device_set_render_target_with_color_space)( + gs_device_t *device, gs_texture_t *tex, gs_zstencil_t *zstencil, + enum gs_color_space space); void (*device_set_cube_render_target)(gs_device_t *device, gs_texture_t *cubetex, int side, gs_zstencil_t *zstencil); diff --git a/libobs/graphics/graphics.c b/libobs/graphics/graphics.c index e931fe8d5..96f31b65e 100644 --- a/libobs/graphics/graphics.c +++ b/libobs/graphics/graphics.c @@ -1308,6 +1308,16 @@ void gs_resize(uint32_t x, uint32_t y) graphics->exports.device_resize(graphics->device, x, y); } +void gs_update_color_space(void) +{ + graphics_t *graphics = thread_graphics; + + if (!gs_valid("gs_update_color_space")) + return; + + graphics->exports.device_update_color_space(graphics->device); +} + void gs_get_size(uint32_t *x, uint32_t *y) { graphics_t *graphics = thread_graphics; @@ -1715,6 +1725,16 @@ gs_shader_t *gs_get_pixel_shader(void) return graphics->exports.device_get_pixel_shader(graphics->device); } +enum gs_color_space gs_get_color_space(void) +{ + graphics_t *graphics = thread_graphics; + + if (!gs_valid("gs_get_color_space")) + return GS_CS_SRGB; + + return graphics->exports.device_get_color_space(graphics->device); +} + gs_texture_t *gs_get_render_target(void) { graphics_t *graphics = thread_graphics; @@ -1746,6 +1766,19 @@ void gs_set_render_target(gs_texture_t *tex, gs_zstencil_t *zstencil) zstencil); } +void gs_set_render_target_with_color_space(gs_texture_t *tex, + gs_zstencil_t *zstencil, + enum gs_color_space space) +{ + graphics_t *graphics = thread_graphics; + + if (!gs_valid("gs_set_render_target_with_color_space")) + return; + + graphics->exports.device_set_render_target_with_color_space( + graphics->device, tex, zstencil, space); +} + void gs_set_cube_render_target(gs_texture_t *cubetex, int side, gs_zstencil_t *zstencil) { diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index 35d2c8687..e160e909f 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -79,6 +79,12 @@ enum gs_color_format { GS_RG16, }; +enum gs_color_space { + GS_CS_SRGB, /* SDR */ + GS_CS_709_EXTENDED, /* Canvas, Mac EDR (HDR) */ + GS_CS_709_SCRGB, /* 1.0 = 80 nits, Windows/Linux HDR */ +}; + enum gs_zstencil_format { GS_ZS_NONE, GS_Z16, @@ -461,6 +467,9 @@ EXPORT gs_texrender_t *gs_texrender_create(enum gs_color_format format, EXPORT void gs_texrender_destroy(gs_texrender_t *texrender); EXPORT bool gs_texrender_begin(gs_texrender_t *texrender, uint32_t cx, uint32_t cy); +EXPORT bool gs_texrender_begin_with_color_space(gs_texrender_t *texrender, + uint32_t cx, uint32_t cy, + enum gs_color_space space); EXPORT void gs_texrender_end(gs_texrender_t *texrender); EXPORT void gs_texrender_reset(gs_texrender_t *texrender); EXPORT gs_texture_t *gs_texrender_get_texture(const gs_texrender_t *texrender); @@ -634,6 +643,7 @@ EXPORT void gs_reset_blend_state(void); EXPORT gs_swapchain_t *gs_swapchain_create(const struct gs_init_data *data); EXPORT void gs_resize(uint32_t x, uint32_t y); +EXPORT void gs_update_color_space(void); EXPORT void gs_get_size(uint32_t *x, uint32_t *y); EXPORT uint32_t gs_get_width(void); EXPORT uint32_t gs_get_height(void); @@ -689,10 +699,14 @@ EXPORT void gs_load_default_samplerstate(bool b_3d, int unit); EXPORT gs_shader_t *gs_get_vertex_shader(void); EXPORT gs_shader_t *gs_get_pixel_shader(void); +EXPORT enum gs_color_space gs_get_color_space(void); EXPORT gs_texture_t *gs_get_render_target(void); EXPORT gs_zstencil_t *gs_get_zstencil_target(void); EXPORT void gs_set_render_target(gs_texture_t *tex, gs_zstencil_t *zstencil); +EXPORT void gs_set_render_target_with_color_space(gs_texture_t *tex, + gs_zstencil_t *zstencil, + enum gs_color_space space); EXPORT void gs_set_cube_render_target(gs_texture_t *cubetex, int side, gs_zstencil_t *zstencil); @@ -1039,6 +1053,20 @@ gs_generalize_format(enum gs_color_format format) } } +static inline enum gs_color_format +gs_get_format_from_space(enum gs_color_space space) +{ + switch (space) { + case GS_CS_SRGB: + break; + case GS_CS_709_EXTENDED: + case GS_CS_709_SCRGB: + return GS_RGBA16F; + } + + return GS_RGBA; +} + static inline uint32_t gs_get_total_levels(uint32_t width, uint32_t height, uint32_t depth) { diff --git a/libobs/graphics/texture-render.c b/libobs/graphics/texture-render.c index e06b21f24..476073178 100644 --- a/libobs/graphics/texture-render.c +++ b/libobs/graphics/texture-render.c @@ -26,6 +26,7 @@ struct gs_texture_render { gs_texture_t *target, *prev_target; gs_zstencil_t *zs, *prev_zs; + enum gs_color_space prev_space; uint32_t cx, cy; @@ -88,6 +89,13 @@ static bool texrender_resetbuffer(gs_texrender_t *texrender, uint32_t cx, } bool gs_texrender_begin(gs_texrender_t *texrender, uint32_t cx, uint32_t cy) +{ + return gs_texrender_begin_with_color_space(texrender, cx, cy, + GS_CS_SRGB); +} + +bool gs_texrender_begin_with_color_space(gs_texrender_t *texrender, uint32_t cx, + uint32_t cy, enum gs_color_space space) { if (!texrender || texrender->rendered) return false; @@ -109,7 +117,9 @@ bool gs_texrender_begin(gs_texrender_t *texrender, uint32_t cx, uint32_t cy) texrender->prev_target = gs_get_render_target(); texrender->prev_zs = gs_get_zstencil_target(); - gs_set_render_target(texrender->target, texrender->zs); + texrender->prev_space = gs_get_color_space(); + gs_set_render_target_with_color_space(texrender->target, texrender->zs, + space); gs_set_viewport(0, 0, texrender->cx, texrender->cy); @@ -121,7 +131,9 @@ void gs_texrender_end(gs_texrender_t *texrender) if (!texrender) return; - gs_set_render_target(texrender->prev_target, texrender->prev_zs); + gs_set_render_target_with_color_space(texrender->prev_target, + texrender->prev_zs, + texrender->prev_space); gs_matrix_pop(); gs_projection_pop();