Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable float pixel format on windows for HDR and/or WCG #1907

Open
miroandel opened this issue Jun 1, 2021 · 5 comments
Open

Enable float pixel format on windows for HDR and/or WCG #1907

miroandel opened this issue Jun 1, 2021 · 5 comments

Comments

@miroandel
Copy link

This feature request enables to switch from WGL_TYPE_RGBA_ARB to WGL_TYPE_RGBA_FLOAT_ARB (On Windows 10) in order to use higher color bit depths than 10 as well as HDR rendering in non-exclusive mode. This also enables wide color gamut rendering (WCG). Usage for the patch located at the end of this issue:

glfwWindowHint(GLFW_FLOAT_PIXEL_TYPE, GLFW_TRUE);
glfwWindowHint(GLFW_RED_BITS, 16);
glfwWindowHint(GLFW_GREEN_BITS, 16);
glfwWindowHint(GLFW_BLUE_BITS, 16);
glfwWindowHint(GLFW_ALPHA_BITS, 16);

Values in the range 0.0f - 1.0f will be in low-dynamic-range while values above 1.0f are in high-dynamic range. The color space is Windows' scRGB where 1.0f corresponds to a brightness of 80 nits, 12.5f corresponds to 1000 nits on a linear scale. More info here: https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range

For sending color space and HDR metadata to the display, each vendor's API must be used. More info from Nvidia here:

Patch:

From bda446768eacf07a4f49dee1b037561e7ebcb369 Mon Sep 17 00:00:00 2001
From: Miro <miroslav@dataton.se>
Date: Fri, 26 Mar 2021 12:15:02 +0100
Subject: [PATCH] Added GLFW_FLOAT_PIXEL_TYPE window creation hint for WGL
 inorder to create float16 pixel-format for HDR and WCG rendering in windows.

---
 include/GLFW/glfw3.h | 5 +++++
 src/context.c        | 6 ++++++
 src/internal.h       | 1 +
 src/wgl_context.c    | 5 ++++-
 src/wgl_context.h    | 1 +
 src/window.c         | 3 +++
 6 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h
index 7d9dee02..f9caa366 100644
--- a/include/GLFW/glfw3.h
+++ b/include/GLFW/glfw3.h
@@ -982,6 +982,11 @@ extern "C" {
  *  Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER).
  */
 #define GLFW_DOUBLEBUFFER           0x00021010
+ /*! @brief Framebuffer pixel format float type hint.
+  *
+  *  Framebuffer pixel format float type [hint](@ref GLFW_FLOAT_PIXEL_TYPE).
+  */
+#define GLFW_FLOAT_PIXEL_TYPE       0x00021011
 
 /*! @brief Context client API hint and attribute.
  *
diff --git a/src/context.c b/src/context.c
index 48311e5f..b8bf5dce 100644
--- a/src/context.c
+++ b/src/context.c
@@ -202,6 +202,12 @@ const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
             continue;
         }
 
+        if (desired->floatPixelType != current->floatPixelType)
+        {
+            // Float pixel type is a hard constraint
+            continue;
+        }
+
         // Count number of missing buffers
         {
             missing = 0;
diff --git a/src/internal.h b/src/internal.h
index f87c2f93..083228f4 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -335,6 +335,7 @@ struct _GLFWfbconfig
     GLFWbool    sRGB;
     GLFWbool    doublebuffer;
     GLFWbool    transparent;
+    GLFWbool    floatPixelType;
     uintptr_t   handle;
 };
 
diff --git a/src/wgl_context.c b/src/wgl_context.c
index 4f9a6ffe..952a9343 100644
--- a/src/wgl_context.c
+++ b/src/wgl_context.c
@@ -159,7 +159,8 @@ static int choosePixelFormat(_GLFWwindow* window,
                 continue;
             }
 
-            if (findAttribValue(WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB)
+            if (findAttribValue(WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB &&
+                findAttribValue(WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_FLOAT_ARB)
                 continue;
 
             if (findAttribValue(WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB)
@@ -184,6 +185,8 @@ static int choosePixelFormat(_GLFWwindow* window,
                 u->stereo = GLFW_TRUE;
             if (findAttribValue(WGL_DOUBLE_BUFFER_ARB))
                 u->doublebuffer = GLFW_TRUE;
+            if (findAttribValue(WGL_PIXEL_TYPE_ARB) == WGL_TYPE_RGBA_FLOAT_ARB)
+                u->floatPixelType = GLFW_TRUE;
 
             if (_glfw.wgl.ARB_multisample)
                 u->samples = findAttribValue(WGL_SAMPLES_ARB);
diff --git a/src/wgl_context.h b/src/wgl_context.h
index 2cf7e4e5..05e70cd8 100644
--- a/src/wgl_context.h
+++ b/src/wgl_context.h
@@ -30,6 +30,7 @@
 #define WGL_DRAW_TO_WINDOW_ARB 0x2001
 #define WGL_PIXEL_TYPE_ARB 0x2013
 #define WGL_TYPE_RGBA_ARB 0x202b
+#define WGL_TYPE_RGBA_FLOAT_ARB 0x21A0
 #define WGL_ACCELERATION_ARB 0x2003
 #define WGL_NO_ACCELERATION_ARB 0x2025
 #define WGL_RED_BITS_ARB 0x2015
diff --git a/src/window.c b/src/window.c
index d2196e2f..982472a9 100644
--- a/src/window.c
+++ b/src/window.c
@@ -343,6 +343,9 @@ GLFWAPI void glfwWindowHint(int hint, int value)
         case GLFW_SRGB_CAPABLE:
             _glfw.hints.framebuffer.sRGB = value ? GLFW_TRUE : GLFW_FALSE;
             return;
+        case GLFW_FLOAT_PIXEL_TYPE:
+            _glfw.hints.framebuffer.floatPixelType = value ? GLFW_TRUE : GLFW_FALSE;
+            return;
         case GLFW_RESIZABLE:
             _glfw.hints.window.resizable = value ? GLFW_TRUE : GLFW_FALSE;
             return;
-- 
2.31.1.windows.1

@eddiejames
Copy link

Thanks for this! Is there any reason this hasn't been opened as a PR and included in GLFW?

@miroandel
Copy link
Author

I can have a look on how to make a PR out of it. It probably needs rebasing to master. Also half-float buffer works only on Windows at the moment as I understand it. There might need to be some error handling when trying to use GLFW_FLOAT_PIXEL_TYPE on other OS:es/windowing systems.

I'm also a but unsure about if there are any rules when creating new defines. I simply set a value that wasn't used.

However, this code has been used successfully on both Nvidia GPUs, Intel GPUs, and hybrid versions between those. It probably works on AMD too but hasn't been tested. Windows is handling the static HDR meta data but it's possible to override with Nvidias API for example.

@eddiejames
Copy link

OK, yea I was just testing it yesterday. I'm on AMD. It worked, but strangely, I could only get a BGRA buffer. I thought to add some code to check the bit shifts, but they were actually all BGRA.

@meshula
Copy link

meshula commented Mar 30, 2023

I'd like to get fp16 and other extended formats on macOS as well ~ grepping around in the code I think float pixel formats haven't been implemented since this issue was posted? Just want to double check that I haven't overlooked something.

@Ext3h
Copy link

Ext3h commented Jan 8, 2024

Values in the range 0.0f - 1.0f will be in low-dynamic-range while values above 1.0f are in high-dynamic range

That's technically incorrect. When using an sRGB render target, 1.0f is the user defined preferred white point when targeting an HDR display (that behaves like LDR content, and that 1.0f is usually far off from 80 nits on any modern display). But when using scRGB render target, 1.0f is reduced to 80 nits, and you have to query the native system APIs to get the preferred brightness of "reference white" as a multiplier just in order to get LDR content to show as expected again.

So you can't just switch the default behavior and expect applications to "just work".

For sending color space and HDR metadata to the display

Forget about that part for now. That's the least of your concerns. Not to mention that most displays ignore this information anyway, as it can be trivially reconstructed from the image content. Plus you are clashing with Windows here, which has already opted to take control, and has (for very good reasons) stated towards to the display that it will produce content which exactly fits the native capabilities of the display.

More important is the part where you query the effective color gamut and brightness range. Everything in that range is safe to use, and will behave somewhat as expected. This is what the final applications tone mapping needs to yield.

Everything outside that range is subject to "smart optimizations" the display vendor may have applied. Do not go this route, you will encounter highly unexpected effects such as the display randomly adjusting the brightness, deviating from the previously absolute scale, just in order to still fit the highlights into it's own range. If you do this, you will face displays (thanks for nothing, BenQ) trying to do the tone mapping in your stead, and the result will suck as you are no longer in the expected color space. You won't notice this specific issue if your reference system is a 10bit wide gamut display with a color profile, in that case Windows had already taken care of properly clipping. But you will notice issues on many HDR10 displays.

TLDR: Make sure the content is clipped to the displays capabilities, or UB occurs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants