From d87217251dfe74d4f1567b170be8f2488e9d1dba Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Wed, 1 Dec 2021 13:59:38 +0300 Subject: [PATCH 01/17] Add ClearBits for FBitSet Because otherwise the condition will be called all the time. --- ref_vk/vk_framectl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ref_vk/vk_framectl.c b/ref_vk/vk_framectl.c index 40dbca265..29a700b28 100644 --- a/ref_vk/vk_framectl.c +++ b/ref_vk/vk_framectl.c @@ -315,6 +315,7 @@ void R_BeginFrame( qboolean clearScene ) if (vk_core.rtx && FBitSet( vk_rtx->flags, FCVAR_CHANGED )) { g_frame.rtx_enabled = CVAR_TO_BOOL( vk_rtx ); } + ClearBits( vk_rtx->flags, FCVAR_CHANGED ); { gEngine.Con_NPrintf(5, "Perf scopes:"); for (int i = 0; i < g_aprof.num_scopes; ++i) { From c7aa3647f17736d06598d2dff375472baca269bf Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Thu, 2 Dec 2021 00:14:29 +0300 Subject: [PATCH 02/17] Add FCVAR_READ_ONLY and unlock FCVAR_GLCONFIG (temporary solution for some commands) --- engine/common/cvar.c | 3 ++- ref_vk/vk_cvar.h | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 1d7650375..5cbfb7f0c 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -510,7 +510,8 @@ void Cvar_DirectSet( convar_t *var, const char *value ) if( var != Cvar_FindVar( var->name )) return; // how this possible? - if( FBitSet( var->flags, FCVAR_READ_ONLY|FCVAR_GLCONFIG )) + //if( FBitSet( var->flags, FCVAR_READ_ONLY|FCVAR_GLCONFIG )) + if( FBitSet( var->flags, FCVAR_READ_ONLY )) // TODO: fix order VK init render or fix this GL-eccentric { Con_Printf( "%s is read-only.\n", var->name ); return; diff --git a/ref_vk/vk_cvar.h b/ref_vk/vk_cvar.h index c75accadb..c79ad7eb6 100644 --- a/ref_vk/vk_cvar.h +++ b/ref_vk/vk_cvar.h @@ -2,6 +2,9 @@ #include "cvardef.h" +// from engine/common/cvar.h +#define FCVAR_READ_ONLY (1<<17) // cannot be set by user at all, and can't be requested by CvarGetPointer from game dlls + #define CVAR_TO_BOOL( x ) ((x) && ((x)->value != 0.0f) ? true : false ) void VK_LoadCvars( void ); From 0f1c13608be11ede5fec82750a7d4a7baf811a64 Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Thu, 2 Dec 2021 02:37:23 +0300 Subject: [PATCH 03/17] add vk_rtx_extension cvar for rtx options, more smart vk_rtx cvar --- ref_vk/vk_cvar.c | 8 +++++++- ref_vk/vk_cvar.h | 1 + ref_vk/vk_framectl.c | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ref_vk/vk_cvar.c b/ref_vk/vk_cvar.c index 116ff6333..b0a0bd88c 100644 --- a/ref_vk/vk_cvar.c +++ b/ref_vk/vk_cvar.c @@ -1,5 +1,6 @@ #include "vk_cvar.h" #include "vk_common.h" +#include "vk_render.h" #define NONEXTERN_CVAR(cvar) cvar_t *cvar; DECLARE_CVAR(NONEXTERN_CVAR) @@ -19,5 +20,10 @@ void VK_LoadCvars( void ) vk_rtx_light_end = gEngine.Cvar_Get( "vk_rtx_light_end", "0", 0, "DEBUG: disable lights with index higher than this "); r_lightmap = gEngine.Cvar_Get( "r_lightmap", "0", FCVAR_CHEAT, "lightmap debugging tool" ); ui_infotool = gEngine.Cvar_Get( "ui_infotool", "0", FCVAR_CHEAT, "DEBUG: print entity info under crosshair" ); - vk_rtx = gEngine.Cvar_Get( "vk_rtx", "1", FCVAR_ARCHIVE, "Enable or disable ray tracing mode" ); + vk_rtx_extension = gEngine.Cvar_Get( "vk_rtx_extension", vk_core.rtx ? "1" : "0", FCVAR_READ_ONLY, "" ); + if (vk_core.rtx) { + vk_rtx = gEngine.Cvar_Get( "vk_rtx", "1", FCVAR_GLCONFIG, "Enable or disable Ray Tracing mode" ); + } else { + vk_rtx = gEngine.Cvar_Get( "vk_rtx", "0", FCVAR_READ_ONLY, "DISABLED: not supported without -rtx" ); + } } diff --git a/ref_vk/vk_cvar.h b/ref_vk/vk_cvar.h index c79ad7eb6..b1c8a7445 100644 --- a/ref_vk/vk_cvar.h +++ b/ref_vk/vk_cvar.h @@ -19,6 +19,7 @@ void VK_LoadCvars( void ); X(r_lightmap) \ X(ui_infotool) \ X(vk_rtx) \ + X(vk_rtx_extension) \ #define EXTERN_CVAR(cvar) extern cvar_t *cvar; DECLARE_CVAR(EXTERN_CVAR) diff --git a/ref_vk/vk_framectl.c b/ref_vk/vk_framectl.c index 29a700b28..4839bec55 100644 --- a/ref_vk/vk_framectl.c +++ b/ref_vk/vk_framectl.c @@ -512,6 +512,7 @@ void R_EndFrame( void ) static void toggleRaytracing( void ) { ASSERT(vk_core.rtx); g_frame.rtx_enabled = !g_frame.rtx_enabled; + gEngine.Cvar_Set("vk_rtx", g_frame.rtx_enabled ? "1" : "0"); gEngine.Con_Printf(S_WARN "Switching ray tracing to %d\n", g_frame.rtx_enabled); } From 7694fb6b9324b5dc7041efe207e015d8dbaef9d4 Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Thu, 2 Dec 2021 21:24:19 +0300 Subject: [PATCH 04/17] initial HDR output support --- ref_vk/vk_cvar.c | 1 + ref_vk/vk_cvar.h | 1 + ref_vk/vk_framectl.c | 25 ++++++++++++++++++------- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/ref_vk/vk_cvar.c b/ref_vk/vk_cvar.c index b0a0bd88c..a8ba59958 100644 --- a/ref_vk/vk_cvar.c +++ b/ref_vk/vk_cvar.c @@ -26,4 +26,5 @@ void VK_LoadCvars( void ) } else { vk_rtx = gEngine.Cvar_Get( "vk_rtx", "0", FCVAR_READ_ONLY, "DISABLED: not supported without -rtx" ); } + vk_hdr = gEngine.Cvar_Get( "vk_hdr", "0", FCVAR_GLCONFIG, "Enable or disable High Dynamic Range output (RESTART REQUIRED)" ); } diff --git a/ref_vk/vk_cvar.h b/ref_vk/vk_cvar.h index b1c8a7445..536af6b2f 100644 --- a/ref_vk/vk_cvar.h +++ b/ref_vk/vk_cvar.h @@ -20,6 +20,7 @@ void VK_LoadCvars( void ); X(ui_infotool) \ X(vk_rtx) \ X(vk_rtx_extension) \ + X(vk_hdr) \ #define EXTERN_CVAR(cvar) extern cvar_t *cvar; DECLARE_CVAR(EXTERN_CVAR) diff --git a/ref_vk/vk_framectl.c b/ref_vk/vk_framectl.c index 4839bec55..4909077fa 100644 --- a/ref_vk/vk_framectl.c +++ b/ref_vk/vk_framectl.c @@ -132,11 +132,11 @@ static void destroyDepthImage( void ) { freeDeviceMemory(&g_frame.depth.device_memory); } -static VkRenderPass createRenderPass( qboolean ray_tracing ) { +static VkRenderPass createRenderPass( qboolean ray_tracing, qboolean hdr_output ) { VkRenderPass render_pass; VkAttachmentDescription attachments[] = {{ - .format = VK_FORMAT_B8G8R8A8_UNORM, //SRGB,// FIXME too early swapchain.create_info.imageFormat; + .format = hdr_output ? VK_FORMAT_A2B10G10R10_UNORM_PACK32 : VK_FORMAT_B8G8R8A8_UNORM, //SRGB,// FIXME too early swapchain.create_info.imageFormat; .samples = VK_SAMPLE_COUNT_1_BIT, .loadOp = ray_tracing ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR /* TODO: prod renderer should not care VK_ATTACHMENT_LOAD_OP_DONT_CARE */, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, @@ -213,6 +213,8 @@ static qboolean createSwapchain( void ) // recreating swapchain means invalidating any previous frames g_frame.last_frame_index = -1; + qboolean hdr_output = CVAR_TO_BOOL( vk_hdr ); + XVK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_core.physical_device.device, vk_core.surface.surface, &g_frame.surface_caps)); vk_frame.width = g_frame.surface_caps.currentExtent.width; @@ -230,8 +232,8 @@ static qboolean createSwapchain( void ) create_info->sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; create_info->pNext = NULL; create_info->surface = vk_core.surface.surface; - create_info->imageFormat = VK_FORMAT_B8G8R8A8_UNORM;//SRGB; // TODO get from surface_formats - create_info->imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; // TODO get from surface_formats + create_info->imageFormat = hdr_output ? VK_FORMAT_A2B10G10R10_UNORM_PACK32 : VK_FORMAT_B8G8R8A8_UNORM;//SRGB; // TODO get from surface_formats + create_info->imageColorSpace = hdr_output ? VK_COLOR_SPACE_HDR10_ST2084_EXT : VK_COLORSPACE_SRGB_NONLINEAR_KHR; // TODO get from surface_formats create_info->imageExtent.width = vk_frame.width; create_info->imageExtent.height = vk_frame.height; create_info->imageArrayLayers = 1; @@ -346,6 +348,14 @@ void R_BeginFrame( qboolean clearScene ) } } + // TODO: VkFramebufferCreateInfo attachment #0 has format of VK_FORMAT_A2B10G10R10_UNORM_PACK32 that does not match the format of VK_FORMAT_B8G8R8A8_UNORM used by the corresponding attachment for VkRenderPass + /* + if (FBitSet( vk_hdr->flags, FCVAR_CHANGED )) { + createSwapchain(); + } + ClearBits( vk_hdr->flags, FCVAR_CHANGED ); + */ + for (int i = 0;; ++i) { const VkResult acquire_result = vkAcquireNextImageKHR(vk_core.device, g_frame.swapchain, UINT64_MAX, g_frame.image_available, @@ -519,9 +529,10 @@ static void toggleRaytracing( void ) { qboolean VK_FrameCtlInit( void ) { PROFILER_SCOPES(APROF_SCOPE_INIT); - vk_frame.render_pass.raster = createRenderPass(false); + qboolean hdr_output = CVAR_TO_BOOL( vk_hdr ); + vk_frame.render_pass.raster = createRenderPass(false, hdr_output); if (vk_core.rtx) - vk_frame.render_pass.after_ray_tracing = createRenderPass(true); + vk_frame.render_pass.after_ray_tracing = createRenderPass(true, hdr_output); if (!createSwapchain()) return false; @@ -764,7 +775,7 @@ static rgbdata_t *XVK_ReadPixels( void ) { r_shot->buffer = Mem_Malloc( r_temppool, r_shot->size ); if (!blit) { - if (dest_format != VK_FORMAT_R8G8B8A8_UNORM || g_frame.create_info.imageFormat != VK_FORMAT_B8G8R8A8_UNORM) { + if (dest_format != VK_FORMAT_R8G8B8A8_UNORM || g_frame.create_info.imageFormat != VK_FORMAT_B8G8R8A8_UNORM) { // TODO: fix for HDR gEngine.Con_Printf(S_WARN "Don't have a blit function for this format pair, will save as-is w/o conversion; expect image to look wrong\n"); blit = true; } else { From 758de3031f0dca6f9ce1c68f8eaa393ff3eb595f Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Thu, 2 Dec 2021 22:29:17 +0300 Subject: [PATCH 05/17] fix C90 error --- ref_vk/vk_framectl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ref_vk/vk_framectl.c b/ref_vk/vk_framectl.c index 4909077fa..119cdbfb7 100644 --- a/ref_vk/vk_framectl.c +++ b/ref_vk/vk_framectl.c @@ -528,8 +528,8 @@ static void toggleRaytracing( void ) { qboolean VK_FrameCtlInit( void ) { - PROFILER_SCOPES(APROF_SCOPE_INIT); qboolean hdr_output = CVAR_TO_BOOL( vk_hdr ); + PROFILER_SCOPES(APROF_SCOPE_INIT); vk_frame.render_pass.raster = createRenderPass(false, hdr_output); if (vk_core.rtx) vk_frame.render_pass.after_ray_tracing = createRenderPass(true, hdr_output); From 236339a1474e7750d1919cb1b9935dae89bd17c7 Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Thu, 2 Dec 2021 22:36:07 +0300 Subject: [PATCH 06/17] =?UTF-8?q?fix=20C90=20error=20=E2=84=962?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ref_vk/vk_framectl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ref_vk/vk_framectl.c b/ref_vk/vk_framectl.c index 119cdbfb7..44e40be2d 100644 --- a/ref_vk/vk_framectl.c +++ b/ref_vk/vk_framectl.c @@ -209,12 +209,11 @@ static uint32_t clamp_u32(uint32_t v, uint32_t min, uint32_t max) { static qboolean createSwapchain( void ) { VkSwapchainCreateInfoKHR *create_info = &g_frame.create_info; + qboolean hdr_output = CVAR_TO_BOOL( vk_hdr ); const uint32_t prev_num_images = g_frame.num_images; // recreating swapchain means invalidating any previous frames g_frame.last_frame_index = -1; - qboolean hdr_output = CVAR_TO_BOOL( vk_hdr ); - XVK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_core.physical_device.device, vk_core.surface.surface, &g_frame.surface_caps)); vk_frame.width = g_frame.surface_caps.currentExtent.width; From cbdcabbdbefaa3c6922810c0bdeba17f03210084 Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Thu, 2 Dec 2021 23:53:07 +0300 Subject: [PATCH 07/17] add check colorSpace for HDR mode, add vk_hdr_extension for option --- ref_vk/vk_core.c | 5 +++++ ref_vk/vk_core.h | 2 +- ref_vk/vk_cvar.c | 7 ++++++- ref_vk/vk_cvar.h | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ref_vk/vk_core.c b/ref_vk/vk_core.c index da1961ab5..e4ac3d53a 100644 --- a/ref_vk/vk_core.c +++ b/ref_vk/vk_core.c @@ -567,6 +567,10 @@ static qboolean initSurface( void ) { // TODO symbolicate gEngine.Con_Reportf("\t%u: %u %u\n", i, vk_core.surface.surface_formats[i].format, vk_core.surface.surface_formats[i].colorSpace); + // TODO: VK_COLOR_SPACE_HDR10_HLG_EXT, VK_COLOR_SPACE_DOLBYVISION_EXT + if (vk_core.surface.surface_formats[i].colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT) { + vk_core.hdr = true; + } } return true; @@ -608,6 +612,7 @@ qboolean R_VkInit( void ) vk_core.debug = !!(gEngine.Sys_CheckParm("-vkdebug") || gEngine.Sys_CheckParm("-gldebug")); vk_core.rtx = !!(gEngine.Sys_CheckParm("-rtx")); + vk_core.hdr = false; if( !gEngine.R_Init_Video( REF_VULKAN )) // request Vulkan surface { diff --git a/ref_vk/vk_core.h b/ref_vk/vk_core.h index dd1536bfa..e9024098b 100644 --- a/ref_vk/vk_core.h +++ b/ref_vk/vk_core.h @@ -55,7 +55,7 @@ typedef struct vulkan_core_s { // TODO store important capabilities that affect render code paths // (as rtx, dedicated gpu memory, bindless, etc) separately in a struct - qboolean debug, rtx; + qboolean debug, rtx, hdr; struct { VkSurfaceKHR surface; uint32_t num_surface_formats; diff --git a/ref_vk/vk_cvar.c b/ref_vk/vk_cvar.c index a8ba59958..8b5c38c5b 100644 --- a/ref_vk/vk_cvar.c +++ b/ref_vk/vk_cvar.c @@ -26,5 +26,10 @@ void VK_LoadCvars( void ) } else { vk_rtx = gEngine.Cvar_Get( "vk_rtx", "0", FCVAR_READ_ONLY, "DISABLED: not supported without -rtx" ); } - vk_hdr = gEngine.Cvar_Get( "vk_hdr", "0", FCVAR_GLCONFIG, "Enable or disable High Dynamic Range output (RESTART REQUIRED)" ); + vk_hdr_extension = gEngine.Cvar_Get( "vk_hdr_extension", vk_core.hdr ? "1" : "0", FCVAR_READ_ONLY, "" ); + if (vk_core.hdr) { + vk_hdr = gEngine.Cvar_Get( "vk_hdr", "0", FCVAR_GLCONFIG, "Enable or disable High Dynamic Range output (RESTART REQUIRED)" ); + } else { + vk_hdr = gEngine.Cvar_Get( "vk_hdr", "0", FCVAR_READ_ONLY, "DISABLED: not supported by your hardware/software" ); + } } diff --git a/ref_vk/vk_cvar.h b/ref_vk/vk_cvar.h index 536af6b2f..36b123850 100644 --- a/ref_vk/vk_cvar.h +++ b/ref_vk/vk_cvar.h @@ -21,6 +21,7 @@ void VK_LoadCvars( void ); X(vk_rtx) \ X(vk_rtx_extension) \ X(vk_hdr) \ + X(vk_hdr_extension) \ #define EXTERN_CVAR(cvar) extern cvar_t *cvar; DECLARE_CVAR(EXTERN_CVAR) From 3ce4b22cbfb86c9cc57e5a2a3ace9c83d62969f1 Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Fri, 3 Dec 2021 00:08:32 +0300 Subject: [PATCH 08/17] force disable HDR if not supported by hardware/software --- ref_vk/vk_framectl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ref_vk/vk_framectl.c b/ref_vk/vk_framectl.c index 44e40be2d..79b5725ee 100644 --- a/ref_vk/vk_framectl.c +++ b/ref_vk/vk_framectl.c @@ -209,7 +209,7 @@ static uint32_t clamp_u32(uint32_t v, uint32_t min, uint32_t max) { static qboolean createSwapchain( void ) { VkSwapchainCreateInfoKHR *create_info = &g_frame.create_info; - qboolean hdr_output = CVAR_TO_BOOL( vk_hdr ); + qboolean hdr_output = vk_core.hdr && CVAR_TO_BOOL( vk_hdr ); const uint32_t prev_num_images = g_frame.num_images; // recreating swapchain means invalidating any previous frames g_frame.last_frame_index = -1; @@ -527,7 +527,7 @@ static void toggleRaytracing( void ) { qboolean VK_FrameCtlInit( void ) { - qboolean hdr_output = CVAR_TO_BOOL( vk_hdr ); + qboolean hdr_output = vk_core.hdr && CVAR_TO_BOOL( vk_hdr ); PROFILER_SCOPES(APROF_SCOPE_INIT); vk_frame.render_pass.raster = createRenderPass(false, hdr_output); if (vk_core.rtx) From a218ebfd7afc1f8fabf81235c7f93cf026819775 Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Tue, 7 Dec 2021 02:42:23 +0300 Subject: [PATCH 09/17] fix UI in HDR, fix HDR in with/without RTX, specialization for HDR --- ref_vk/shaders/2d.frag | 12 +++++++++++- ref_vk/shaders/brush.frag | 22 ++++++++++++++++++---- ref_vk/shaders/brush.vert | 15 +++++++++++++-- ref_vk/shaders/denoiser.comp | 28 +++++++++++----------------- ref_vk/vk_2d.c | 15 +++++++++++++++ ref_vk/vk_cvar.c | 2 +- ref_vk/vk_denoiser.c | 15 ++++++++++++++- ref_vk/vk_render.c | 6 ++++-- 8 files changed, 87 insertions(+), 28 deletions(-) diff --git a/ref_vk/shaders/2d.frag b/ref_vk/shaders/2d.frag index bcc024804..55ca7da62 100644 --- a/ref_vk/shaders/2d.frag +++ b/ref_vk/shaders/2d.frag @@ -1,4 +1,6 @@ #version 450 +#include "tonemapping.glsl" + layout(set=0,binding=0) uniform sampler2D tex; @@ -7,6 +9,14 @@ layout(location=1) in vec4 vColor; layout(location = 0) out vec4 outColor; +layout (constant_id = 0) const int hdr_output = 0; + void main() { - outColor = texture(tex, vUv) * vColor; + vec4 img = texture(tex, vUv) * vColor; + if (hdr_output > 0) { + // FIXME: Need to composite properly into the color space of the HDR scene, using scRGB backbuffer provides simple solution + float hdr_correction = 1.8; + img.rgb = aces_tonemap(img.rgb) / hdr_correction; + } + outColor = img; } diff --git a/ref_vk/shaders/brush.frag b/ref_vk/shaders/brush.frag index a9c87ef85..c4e0d06cb 100644 --- a/ref_vk/shaders/brush.frag +++ b/ref_vk/shaders/brush.frag @@ -1,7 +1,10 @@ #version 450 +#include "tonemapping.glsl" layout (constant_id = 0) const float alpha_test_threshold = 0.; layout (constant_id = 1) const uint max_dlights = 1; +layout (constant_id = 2) const int hdr_output = 0; + layout(set=1,binding=0) uniform sampler2D sTexture0; layout(set=2,binding=0) uniform sampler2D sLightmap; @@ -25,6 +28,7 @@ layout(location=5) flat in uint vFlags; layout(location=0) out vec4 outColor; + // FIXME what should this be? const float dlight_attenuation_const = 5000.; @@ -32,17 +36,27 @@ const float dlight_attenuation_const = 5000.; void main() { outColor = vec4(0.); - const vec4 baseColor = vColor * texture(sTexture0, vTexture0); + vec4 baseColor = vColor * texture(sTexture0, vTexture0); + + if (hdr_output > 0) { + // FIXME: Need an operator that understands the output luminance and middle gray stays at a reasonable level (400 vs 1000 nit display on 0.5) + baseColor.rgb = aces_tonemap(baseColor.rgb); + } if (baseColor.a < alpha_test_threshold) discard; outColor.a = baseColor.a; - if ((vFlags & FLAG_VERTEX_LIGHTING) == 0) - outColor.rgb += baseColor.rgb * texture(sLightmap, vLightmapUV).rgb; - else + if ((vFlags & FLAG_VERTEX_LIGHTING) == 0) { + vec3 lightmap = texture(sLightmap, vLightmapUV).rgb; + if (hdr_output > 0) { + lightmap = aces_tonemap(lightmap); + } + outColor.rgb += baseColor.rgb * lightmap; + } else { outColor.rgb += baseColor.rgb; + } for (uint i = 0; i < ubo.num_lights; ++i) { const vec4 light_pos_r = ubo.lights[i].pos_r; diff --git a/ref_vk/shaders/brush.vert b/ref_vk/shaders/brush.vert index 573d87329..2d51af6ad 100644 --- a/ref_vk/shaders/brush.vert +++ b/ref_vk/shaders/brush.vert @@ -1,4 +1,7 @@ #version 450 +#include "tonemapping.glsl" + +layout (constant_id = 2) const int hdr_output = 0; layout(set=0,binding=0) uniform UBO { mat4 mvp; @@ -27,9 +30,17 @@ void main() { vTexture0 = aTexture0; vLightmapUV = aLightmapUV; vColor = ubo.color; + vec4 lightmap = aLightColor; + if (hdr_output > 0) { + // FIXME: Avoid tone mapping "fix-ups", ideally done in scene-referred space + float hdr_correction = 1.6; + vColor.rgb = aces_tonemap(vColor.rgb) / hdr_correction; + lightmap.rgb = lightmap.rgb / hdr_correction; + } - if ((aFlags & FLAG_VERTEX_LIGHTING) != 0) - vColor *= aLightColor; + if ((aFlags & FLAG_VERTEX_LIGHTING) != 0) { + vColor *= lightmap; + } vFlags = aFlags; gl_Position = ubo.mvp * vec4(aPos.xyz, 1.); diff --git a/ref_vk/shaders/denoiser.comp b/ref_vk/shaders/denoiser.comp index 758e1c872..671001839 100644 --- a/ref_vk/shaders/denoiser.comp +++ b/ref_vk/shaders/denoiser.comp @@ -1,6 +1,7 @@ #version 460 #include "noise.glsl" +#include "tonemapping.glsl" layout(local_size_x = 16, local_size_y = 8, local_size_z = 1) in; @@ -12,23 +13,7 @@ layout(set = 0, binding = 3, rgba16f) uniform image2D src_specular; layout(set = 0, binding = 4, rgba16f) uniform image2D src_additive; layout(set = 0, binding = 5, rgba16f) uniform image2D src_normals; -// Blatantly copypasted from https://www.shadertoy.com/view/XsGfWV -vec3 aces_tonemap(vec3 color){ - mat3 m1 = mat3( - 0.59719, 0.07600, 0.02840, - 0.35458, 0.90834, 0.13383, - 0.04823, 0.01566, 0.83777 - ); - mat3 m2 = mat3( - 1.60475, -0.10208, -0.00327, - -0.53108, 1.10813, -0.07276, - -0.07367, -0.00605, 1.07602 - ); - vec3 v = m1 * color; - vec3 a = v * (v + 0.0245786) - 0.000090537; - vec3 b = v * (0.983729 * v + 0.4329510) + 0.238081; - return pow(clamp(m2 * (a / b), 0.0, 1.0), vec3(1.0 / 2.2)); -} +layout (constant_id = 0) const int hdr_output = 0; float normpdf2(in float x2, in float sigma) { return 0.39894*exp(-0.5*x2/(sigma*sigma))/sigma; } float normpdf(in float x, in float sigma) { return normpdf2(x*x, sigma); } @@ -107,12 +92,21 @@ void main() { colour *= base_color.rgb; } + // FIXME: Avoid tone mapping "fix-ups", ideally done in scene-referred space + float hdr_correction = 1.5; + // FIXME: Need an operator that understands the output luminance and middle gray stays at a reasonable level (400 vs 1000 nit display on 0.5) + if (hdr_output > 0) { + colour = aces_tonemap(colour) / hdr_correction; + speculour = aces_tonemap(speculour) / hdr_correction; + } + if (specular_total_scale > 0.) { speculour /= specular_total_scale; //speculour *= base_color.rgb; colour += speculour; } + //colour += imageLoad(src_specular, pix).rgb; colour += imageLoad(src_additive, pix).rgb; diff --git a/ref_vk/vk_2d.c b/ref_vk/vk_2d.c index 0eb2a38af..975cf24df 100644 --- a/ref_vk/vk_2d.c +++ b/ref_vk/vk_2d.c @@ -199,6 +199,19 @@ static qboolean createPipelines( void ) } { + struct ShaderSpec { + int hdr_output; + } spec_data = { vk_core.hdr ? 1 : 0 }; + const VkSpecializationMapEntry spec_map[] = { + {.constantID = 0, .offset = offsetof(struct ShaderSpec, hdr_output), .size = sizeof(int) }, + }; + VkSpecializationInfo shader_spec = { + .mapEntryCount = ARRAYSIZE(spec_map), + .pMapEntries = spec_map, + .dataSize = sizeof(struct ShaderSpec), + .pData = &spec_data + }; + const VkVertexInputAttributeDescription attribs[] = { {.binding = 0, .location = 0, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(vertex_2d_t, x)}, {.binding = 0, .location = 1, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(vertex_2d_t, u)}, @@ -209,9 +222,11 @@ static qboolean createPipelines( void ) { .stage = VK_SHADER_STAGE_VERTEX_BIT, .filename = "2d.vert.spv", + .specialization_info = NULL, }, { .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .filename = "2d.frag.spv", + .specialization_info = &shader_spec, }}; vk_pipeline_graphics_create_info_t pci = { diff --git a/ref_vk/vk_cvar.c b/ref_vk/vk_cvar.c index 8b5c38c5b..02f2a84fa 100644 --- a/ref_vk/vk_cvar.c +++ b/ref_vk/vk_cvar.c @@ -28,7 +28,7 @@ void VK_LoadCvars( void ) } vk_hdr_extension = gEngine.Cvar_Get( "vk_hdr_extension", vk_core.hdr ? "1" : "0", FCVAR_READ_ONLY, "" ); if (vk_core.hdr) { - vk_hdr = gEngine.Cvar_Get( "vk_hdr", "0", FCVAR_GLCONFIG, "Enable or disable High Dynamic Range output (RESTART REQUIRED)" ); + vk_hdr = gEngine.Cvar_Get( "vk_hdr", "0", FCVAR_GLCONFIG, "EXPERIMENTAL: Enable or disable High Dynamic Range output (RESTART REQUIRED)" ); } else { vk_hdr = gEngine.Cvar_Get( "vk_hdr", "0", FCVAR_READ_ONLY, "DISABLED: not supported by your hardware/software" ); } diff --git a/ref_vk/vk_denoiser.c b/ref_vk/vk_denoiser.c index b8b7df122..b3c9836e7 100644 --- a/ref_vk/vk_denoiser.c +++ b/ref_vk/vk_denoiser.c @@ -85,10 +85,23 @@ static void createLayouts( void ) { } static VkPipeline createPipeline( void ) { + struct ShaderSpec { + int hdr_output; + } spec_data = { vk_core.hdr ? 1 : 0 }; + const VkSpecializationMapEntry spec_map[] = { + {.constantID = 0, .offset = offsetof(struct ShaderSpec, hdr_output), .size = sizeof(int) }, + }; + VkSpecializationInfo shader_spec = { + .mapEntryCount = ARRAYSIZE(spec_map), + .pMapEntries = spec_map, + .dataSize = sizeof(struct ShaderSpec), + .pData = &spec_data + }; + const vk_pipeline_compute_create_info_t pcci = { .layout = g_denoiser.descriptors.pipeline_layout, .shader_filename = "denoiser.comp.spv", - .specialization_info = NULL, + .specialization_info = &shader_spec, }; return VK_PipelineComputeCreate( &pcci ); diff --git a/ref_vk/vk_render.c b/ref_vk/vk_render.c index da741d693..e09bee444 100644 --- a/ref_vk/vk_render.c +++ b/ref_vk/vk_render.c @@ -69,10 +69,12 @@ static qboolean createPipelines( void ) struct ShaderSpec { float alpha_test_threshold; uint32_t max_dlights; - } spec_data = { .25f, MAX_DLIGHTS }; + int hdr_output; + } spec_data = { .25f, MAX_DLIGHTS, vk_core.hdr ? 1 : 0 }; const VkSpecializationMapEntry spec_map[] = { {.constantID = 0, .offset = offsetof(struct ShaderSpec, alpha_test_threshold), .size = sizeof(float) }, {.constantID = 1, .offset = offsetof(struct ShaderSpec, max_dlights), .size = sizeof(uint32_t) }, + {.constantID = 2, .offset = offsetof(struct ShaderSpec, hdr_output), .size = sizeof(int) }, }; VkSpecializationInfo shader_spec = { @@ -95,7 +97,7 @@ static qboolean createPipelines( void ) { .stage = VK_SHADER_STAGE_VERTEX_BIT, .filename = "brush.vert.spv", - .specialization_info = NULL, + .specialization_info = &shader_spec, }, { .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .filename = "brush.frag.spv", From 0dedbac033c07d216e9eb5df9117c9e25d7b1eba Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Tue, 7 Dec 2021 02:56:45 +0300 Subject: [PATCH 10/17] fix formatting --- ref_vk/shaders/2d.frag | 1 - ref_vk/shaders/brush.frag | 1 - ref_vk/vk_core.c | 2 +- ref_vk/vk_cvar.c | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ref_vk/shaders/2d.frag b/ref_vk/shaders/2d.frag index 55ca7da62..5db12a525 100644 --- a/ref_vk/shaders/2d.frag +++ b/ref_vk/shaders/2d.frag @@ -1,7 +1,6 @@ #version 450 #include "tonemapping.glsl" - layout(set=0,binding=0) uniform sampler2D tex; layout(location=0) in vec2 vUv; diff --git a/ref_vk/shaders/brush.frag b/ref_vk/shaders/brush.frag index c4e0d06cb..84c9b22fe 100644 --- a/ref_vk/shaders/brush.frag +++ b/ref_vk/shaders/brush.frag @@ -28,7 +28,6 @@ layout(location=5) flat in uint vFlags; layout(location=0) out vec4 outColor; - // FIXME what should this be? const float dlight_attenuation_const = 5000.; diff --git a/ref_vk/vk_core.c b/ref_vk/vk_core.c index 7eeedf8d6..25b96fa75 100644 --- a/ref_vk/vk_core.c +++ b/ref_vk/vk_core.c @@ -615,7 +615,7 @@ qboolean R_VkInit( void ) const qboolean skip_first_device = !!(gEngine.Sys_CheckParm("-vkskipdev")); vk_core.debug = !!(gEngine.Sys_CheckParm("-vkdebug") || gEngine.Sys_CheckParm("-gldebug")); - vk_core.rtx = false; + vk_core.rtx = false; vk_core.hdr = false; if( !gEngine.R_Init_Video( REF_VULKAN )) // request Vulkan surface diff --git a/ref_vk/vk_cvar.c b/ref_vk/vk_cvar.c index b791594c7..404c14ebc 100644 --- a/ref_vk/vk_cvar.c +++ b/ref_vk/vk_cvar.c @@ -31,5 +31,5 @@ void VK_LoadCvars( void ) vk_hdr = gEngine.Cvar_Get( "vk_hdr", "0", FCVAR_GLCONFIG, "EXPERIMENTAL: Enable or disable High Dynamic Range output (RESTART REQUIRED)" ); } else { vk_hdr = gEngine.Cvar_Get( "vk_hdr", "0", FCVAR_READ_ONLY, "DISABLED: not supported by your hardware/software" ); - } + } } From 71ee08d1cdd741dff0f40a78becbcef8c0eb57ba Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Tue, 7 Dec 2021 02:58:34 +0300 Subject: [PATCH 11/17] fix formatting again --- ref_vk/shaders/brush.frag | 1 - 1 file changed, 1 deletion(-) diff --git a/ref_vk/shaders/brush.frag b/ref_vk/shaders/brush.frag index 84c9b22fe..39ad775b7 100644 --- a/ref_vk/shaders/brush.frag +++ b/ref_vk/shaders/brush.frag @@ -5,7 +5,6 @@ layout (constant_id = 0) const float alpha_test_threshold = 0.; layout (constant_id = 1) const uint max_dlights = 1; layout (constant_id = 2) const int hdr_output = 0; - layout(set=1,binding=0) uniform sampler2D sTexture0; layout(set=2,binding=0) uniform sampler2D sLightmap; From 67e11136744913d5a9a278ec128c69f2b49835aa Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Tue, 7 Dec 2021 03:01:15 +0300 Subject: [PATCH 12/17] oops, missing tonemapping.glsl --- ref_vk/shaders/tonemapping.glsl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 ref_vk/shaders/tonemapping.glsl diff --git a/ref_vk/shaders/tonemapping.glsl b/ref_vk/shaders/tonemapping.glsl new file mode 100644 index 000000000..51d535f21 --- /dev/null +++ b/ref_vk/shaders/tonemapping.glsl @@ -0,0 +1,17 @@ +// Blatantly copypasted from https://www.shadertoy.com/view/XsGfWV +vec3 aces_tonemap(vec3 color){ + mat3 m1 = mat3( + 0.59719, 0.07600, 0.02840, + 0.35458, 0.90834, 0.13383, + 0.04823, 0.01566, 0.83777 + ); + mat3 m2 = mat3( + 1.60475, -0.10208, -0.00327, + -0.53108, 1.10813, -0.07276, + -0.07367, -0.00605, 1.07602 + ); + vec3 v = m1 * color; + vec3 a = v * (v + 0.0245786) - 0.000090537; + vec3 b = v * (0.983729 * v + 0.4329510) + 0.238081; + return pow(clamp(m2 * (a / b), 0.0, 1.0), vec3(1.0 / 2.2)); // TODO: 2.2 is gamma, need to support the gamma console command, but smart because default gamma 2.5 (WHY?) https://github.com/FWGS/xash3d-fwgs/blob/master/engine/client/vid_common.c#L182 +} From 74ae939140fbf988525b028dbd606d0dcdd5d1c7 Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Tue, 7 Dec 2021 13:14:38 +0300 Subject: [PATCH 13/17] fix formatting again 2 --- ref_vk/shaders/denoiser.comp | 1 - 1 file changed, 1 deletion(-) diff --git a/ref_vk/shaders/denoiser.comp b/ref_vk/shaders/denoiser.comp index fb299e7b1..feaa3badd 100644 --- a/ref_vk/shaders/denoiser.comp +++ b/ref_vk/shaders/denoiser.comp @@ -107,7 +107,6 @@ void main() { colour += speculour; } - //colour += imageLoad(src_specular, pix).rgb; colour += imageLoad(src_additive, pix).rgb; From 198969052fd33cbd59acbc67aa1d04cd647d9b97 Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Tue, 7 Dec 2021 14:24:11 +0300 Subject: [PATCH 14/17] fix hdr shader init (i'm stupid) --- ref_vk/vk_2d.c | 3 ++- ref_vk/vk_denoiser.c | 3 ++- ref_vk/vk_render.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ref_vk/vk_2d.c b/ref_vk/vk_2d.c index 975cf24df..6a6c25435 100644 --- a/ref_vk/vk_2d.c +++ b/ref_vk/vk_2d.c @@ -2,6 +2,7 @@ #include "vk_buffer.h" #include "vk_core.h" +#include "vk_cvar.h" #include "vk_common.h" #include "vk_textures.h" #include "vk_framectl.h" @@ -201,7 +202,7 @@ static qboolean createPipelines( void ) { struct ShaderSpec { int hdr_output; - } spec_data = { vk_core.hdr ? 1 : 0 }; + } spec_data = { (vk_core.hdr && CVAR_TO_BOOL(vk_hdr)) ? 1 : 0 }; const VkSpecializationMapEntry spec_map[] = { {.constantID = 0, .offset = offsetof(struct ShaderSpec, hdr_output), .size = sizeof(int) }, }; diff --git a/ref_vk/vk_denoiser.c b/ref_vk/vk_denoiser.c index b3c9836e7..0fb324341 100644 --- a/ref_vk/vk_denoiser.c +++ b/ref_vk/vk_denoiser.c @@ -2,6 +2,7 @@ #include "vk_descriptor.h" #include "vk_pipeline.h" +#include "vk_cvar.h" #include "eiface.h" // ARRAYSIZE @@ -87,7 +88,7 @@ static void createLayouts( void ) { static VkPipeline createPipeline( void ) { struct ShaderSpec { int hdr_output; - } spec_data = { vk_core.hdr ? 1 : 0 }; + } spec_data = { (vk_core.hdr && CVAR_TO_BOOL(vk_hdr)) ? 1 : 0 }; const VkSpecializationMapEntry spec_map[] = { {.constantID = 0, .offset = offsetof(struct ShaderSpec, hdr_output), .size = sizeof(int) }, }; diff --git a/ref_vk/vk_render.c b/ref_vk/vk_render.c index e09bee444..86ee949b3 100644 --- a/ref_vk/vk_render.c +++ b/ref_vk/vk_render.c @@ -3,6 +3,7 @@ #include "vk_core.h" #include "vk_buffer.h" #include "vk_const.h" +#include "vk_cvar.h" #include "vk_common.h" #include "vk_pipeline.h" #include "vk_textures.h" @@ -70,7 +71,7 @@ static qboolean createPipelines( void ) float alpha_test_threshold; uint32_t max_dlights; int hdr_output; - } spec_data = { .25f, MAX_DLIGHTS, vk_core.hdr ? 1 : 0 }; + } spec_data = { .25f, MAX_DLIGHTS, (vk_core.hdr && CVAR_TO_BOOL(vk_hdr)) ? 1 : 0 }; const VkSpecializationMapEntry spec_map[] = { {.constantID = 0, .offset = offsetof(struct ShaderSpec, alpha_test_threshold), .size = sizeof(float) }, {.constantID = 1, .offset = offsetof(struct ShaderSpec, max_dlights), .size = sizeof(uint32_t) }, From 354a525b29ceb3da3a20577a8f89c9e3ce625c9f Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Tue, 7 Dec 2021 17:14:14 +0300 Subject: [PATCH 15/17] more control for HDR output and more correct RTX change hdr* to hdr_output* add new cvars for manual adjust HDR output more correct HDR output for RTX misc TODO for auto adjust (by VkHdrMetadataEXT) --- ref_vk/shaders/2d.frag | 4 ++-- ref_vk/shaders/brush.frag | 1 + ref_vk/shaders/brush.vert | 6 +++--- ref_vk/shaders/denoiser.comp | 19 +++++++++++++------ ref_vk/vk_2d.c | 7 ++++++- ref_vk/vk_core.c | 4 ++-- ref_vk/vk_core.h | 2 +- ref_vk/vk_cvar.c | 14 ++++++++++---- ref_vk/vk_cvar.h | 10 ++++++++-- ref_vk/vk_denoiser.c | 20 +++++++++++++++++++- ref_vk/vk_framectl.c | 4 ++-- ref_vk/vk_render.c | 9 ++++++++- 12 files changed, 75 insertions(+), 25 deletions(-) diff --git a/ref_vk/shaders/2d.frag b/ref_vk/shaders/2d.frag index 5db12a525..496abc5e8 100644 --- a/ref_vk/shaders/2d.frag +++ b/ref_vk/shaders/2d.frag @@ -9,13 +9,13 @@ layout(location=1) in vec4 vColor; layout(location = 0) out vec4 outColor; layout (constant_id = 0) const int hdr_output = 0; +layout (constant_id = 1) const float hdr_output_manual_adjust_ui_down = 1.8; void main() { vec4 img = texture(tex, vUv) * vColor; if (hdr_output > 0) { // FIXME: Need to composite properly into the color space of the HDR scene, using scRGB backbuffer provides simple solution - float hdr_correction = 1.8; - img.rgb = aces_tonemap(img.rgb) / hdr_correction; + img.rgb = aces_tonemap(img.rgb) / hdr_output_manual_adjust_ui_down; } outColor = img; } diff --git a/ref_vk/shaders/brush.frag b/ref_vk/shaders/brush.frag index 39ad775b7..85b2c4b6c 100644 --- a/ref_vk/shaders/brush.frag +++ b/ref_vk/shaders/brush.frag @@ -4,6 +4,7 @@ layout (constant_id = 0) const float alpha_test_threshold = 0.; layout (constant_id = 1) const uint max_dlights = 1; layout (constant_id = 2) const int hdr_output = 0; +//layout (constant_id = 3) const float hdr_output_manual_adjust_down = 1.6; layout(set=1,binding=0) uniform sampler2D sTexture0; layout(set=2,binding=0) uniform sampler2D sLightmap; diff --git a/ref_vk/shaders/brush.vert b/ref_vk/shaders/brush.vert index 2d51af6ad..7a00bd442 100644 --- a/ref_vk/shaders/brush.vert +++ b/ref_vk/shaders/brush.vert @@ -2,6 +2,7 @@ #include "tonemapping.glsl" layout (constant_id = 2) const int hdr_output = 0; +layout (constant_id = 3) const float hdr_output_manual_adjust_down = 1.6; layout(set=0,binding=0) uniform UBO { mat4 mvp; @@ -33,9 +34,8 @@ void main() { vec4 lightmap = aLightColor; if (hdr_output > 0) { // FIXME: Avoid tone mapping "fix-ups", ideally done in scene-referred space - float hdr_correction = 1.6; - vColor.rgb = aces_tonemap(vColor.rgb) / hdr_correction; - lightmap.rgb = lightmap.rgb / hdr_correction; + vColor.rgb = aces_tonemap(vColor.rgb) / hdr_output_manual_adjust_down; + lightmap.rgb /= hdr_output_manual_adjust_down; } if ((aFlags & FLAG_VERTEX_LIGHTING) != 0) { diff --git a/ref_vk/shaders/denoiser.comp b/ref_vk/shaders/denoiser.comp index feaa3badd..2af1fe21e 100644 --- a/ref_vk/shaders/denoiser.comp +++ b/ref_vk/shaders/denoiser.comp @@ -14,6 +14,12 @@ layout(set = 0, binding = 4, rgba16f) uniform image2D src_additive; layout(set = 0, binding = 5, rgba16f) uniform image2D src_normals; layout (constant_id = 0) const int hdr_output = 0; +// FIXME: Avoid tone mapping "fix-ups", ideally done in scene-referred space +// Need an operator that understands the output luminance and middle gray stays at a reasonable level (400 vs 1000 nit display on 0.5) +//layout (constant_id = 1) const float hdr_maxLuminance = 0; +//layout (constant_id = 2) const int hdr_auto_adjust = 1; +layout (constant_id = 3) const float hdr_manual_adjust_down = 3; +layout (constant_id = 4) const float hdr_manual_adjust_additive_down = 1.5; float normpdf2(in float x2, in float sigma) { return 0.39894*exp(-0.5*x2/(sigma*sigma))/sigma; } float normpdf(in float x, in float sigma) { return normpdf2(x*x, sigma); } @@ -54,6 +60,7 @@ void main() { float specular_total_scale = 0.; vec3 colour = vec3(0.); vec3 speculour = vec3(0.); + vec3 additivour = vec3(0.); for (int x = -KERNEL_SIZE; x <= KERNEL_SIZE; ++x) for (int y = -KERNEL_SIZE; y <= KERNEL_SIZE; ++y) { const ivec2 p = pix + ivec2(x, y); @@ -93,12 +100,12 @@ void main() { colour *= base_color.rgb; } - // FIXME: Avoid tone mapping "fix-ups", ideally done in scene-referred space - float hdr_correction = 1.5; - // FIXME: Need an operator that understands the output luminance and middle gray stays at a reasonable level (400 vs 1000 nit display on 0.5) + additivour = imageLoad(src_additive, pix).rgb; + if (hdr_output > 0) { - colour = aces_tonemap(colour) / hdr_correction; - speculour = aces_tonemap(speculour) / hdr_correction; + colour = aces_tonemap(colour) / hdr_manual_adjust_down; + speculour = aces_tonemap(speculour) / hdr_manual_adjust_down; + additivour = aces_tonemap(additivour) / hdr_manual_adjust_additive_down; } if (specular_total_scale > 0.) { @@ -108,7 +115,7 @@ void main() { } //colour += imageLoad(src_specular, pix).rgb; - colour += imageLoad(src_additive, pix).rgb; + colour += additivour; colour = aces_tonemap(colour); diff --git a/ref_vk/vk_2d.c b/ref_vk/vk_2d.c index 6a6c25435..5414ee84c 100644 --- a/ref_vk/vk_2d.c +++ b/ref_vk/vk_2d.c @@ -202,9 +202,14 @@ static qboolean createPipelines( void ) { struct ShaderSpec { int hdr_output; - } spec_data = { (vk_core.hdr && CVAR_TO_BOOL(vk_hdr)) ? 1 : 0 }; + float hdr_output_manual_adjust_ui_down; + } spec_data = { + .hdr_output = (vk_core.hdr_output && CVAR_TO_BOOL(vk_hdr_output)) ? 1 : 0, + .hdr_output_manual_adjust_ui_down = vk_core.hdr_output ? vk_hdr_output_manual_adjust_ui_down->value : 0, + }; const VkSpecializationMapEntry spec_map[] = { {.constantID = 0, .offset = offsetof(struct ShaderSpec, hdr_output), .size = sizeof(int) }, + {.constantID = 1, .offset = offsetof(struct ShaderSpec, hdr_output_manual_adjust_ui_down), .size = sizeof(int) }, }; VkSpecializationInfo shader_spec = { .mapEntryCount = ARRAYSIZE(spec_map), diff --git a/ref_vk/vk_core.c b/ref_vk/vk_core.c index 25b96fa75..8a42660f4 100644 --- a/ref_vk/vk_core.c +++ b/ref_vk/vk_core.c @@ -573,7 +573,7 @@ static qboolean initSurface( void ) gEngine.Con_Reportf("\t%u: %u %u\n", i, vk_core.surface.surface_formats[i].format, vk_core.surface.surface_formats[i].colorSpace); // TODO: VK_COLOR_SPACE_HDR10_HLG_EXT, VK_COLOR_SPACE_DOLBYVISION_EXT if (vk_core.surface.surface_formats[i].colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT) { - vk_core.hdr = true; + vk_core.hdr_output = true; } } @@ -616,7 +616,7 @@ qboolean R_VkInit( void ) vk_core.debug = !!(gEngine.Sys_CheckParm("-vkdebug") || gEngine.Sys_CheckParm("-gldebug")); vk_core.rtx = false; - vk_core.hdr = false; + vk_core.hdr_output = false; if( !gEngine.R_Init_Video( REF_VULKAN )) // request Vulkan surface { diff --git a/ref_vk/vk_core.h b/ref_vk/vk_core.h index e9024098b..5e3bf99db 100644 --- a/ref_vk/vk_core.h +++ b/ref_vk/vk_core.h @@ -55,7 +55,7 @@ typedef struct vulkan_core_s { // TODO store important capabilities that affect render code paths // (as rtx, dedicated gpu memory, bindless, etc) separately in a struct - qboolean debug, rtx, hdr; + qboolean debug, rtx, hdr_output; struct { VkSurfaceKHR surface; uint32_t num_surface_formats; diff --git a/ref_vk/vk_cvar.c b/ref_vk/vk_cvar.c index 404c14ebc..d4c529f23 100644 --- a/ref_vk/vk_cvar.c +++ b/ref_vk/vk_cvar.c @@ -26,10 +26,16 @@ void VK_LoadCvars( void ) } else { vk_rtx = gEngine.Cvar_Get( "vk_rtx", "0", FCVAR_READ_ONLY, "DISABLED: not supported by your hardware/software" ); } - vk_hdr_extension = gEngine.Cvar_Get( "vk_hdr_extension", vk_core.hdr ? "1" : "0", FCVAR_READ_ONLY, "" ); - if (vk_core.hdr) { - vk_hdr = gEngine.Cvar_Get( "vk_hdr", "0", FCVAR_GLCONFIG, "EXPERIMENTAL: Enable or disable High Dynamic Range output (RESTART REQUIRED)" ); + vk_hdr_output_extension = gEngine.Cvar_Get( "vk_hdr_output_extension", vk_core.hdr_output ? "1" : "0", FCVAR_READ_ONLY, "" ); + //vk_hdr_output_max_luminance = gEngine.Cvar_Get( "vk_hdr_output_max_luminance", vk_core.hdr_output_max_luminance, FCVAR_READ_ONLY, "" ); + //vk_hdr_output_auto_adjust = gEngine.Cvar_Get( "vk_hdr_output_auto_adjust", "1", FCVAR_GLCONFIG, "" ); + if (vk_core.hdr_output) { + vk_hdr_output = gEngine.Cvar_Get( "vk_hdr_output", "0", FCVAR_GLCONFIG, "EXPERIMENTAL: Enable or disable High Dynamic Range output (ENABLED HDR IN OS AND RESTART REQUIRED)" ); + vk_hdr_output_manual_rtx_adjust_down = gEngine.Cvar_Get( "vk_hdr_output_manual_rtx_adjust_down", "3", FCVAR_GLCONFIG, "EXPERIMENTAL: Adjust down output HDR level for color, specular (RESTART REQUIRED)" ); + vk_hdr_output_manual_rtx_adjust_additive_down = gEngine.Cvar_Get( "vk_hdr_output_manual_rtx_adjust_additive_down", "1.5", FCVAR_GLCONFIG, "EXPERIMENTAL: Adjust down output HDR level for additive (RESTART REQUIRED)" ); + vk_hdr_output_manual_adjust_ui_down = gEngine.Cvar_Get( "vk_hdr_output_manual_adjust_ui_down", "1.8", FCVAR_GLCONFIG, "EXPERIMENTAL: Adjust down output HDR level for UI (RESTART REQUIRED)" ); + vk_hdr_output_manual_adjust_down = gEngine.Cvar_Get( "vk_hdr_output_manual_adjust_down", "1.6", FCVAR_GLCONFIG, "EXPERIMENTAL: Adjust down output HDR level without RTX (RESTART REQUIRED)" ); } else { - vk_hdr = gEngine.Cvar_Get( "vk_hdr", "0", FCVAR_READ_ONLY, "DISABLED: not supported by your hardware/software" ); + vk_hdr_output = gEngine.Cvar_Get( "vk_hdr_output", "0", FCVAR_READ_ONLY, "DISABLED: not supported by your hardware/software" ); } } diff --git a/ref_vk/vk_cvar.h b/ref_vk/vk_cvar.h index 36b123850..09afc76f4 100644 --- a/ref_vk/vk_cvar.h +++ b/ref_vk/vk_cvar.h @@ -20,8 +20,14 @@ void VK_LoadCvars( void ); X(ui_infotool) \ X(vk_rtx) \ X(vk_rtx_extension) \ - X(vk_hdr) \ - X(vk_hdr_extension) \ + X(vk_hdr_output) \ + X(vk_hdr_output_extension) \ + X(vk_hdr_output_max_luminance) \ + X(vk_hdr_output_auto_adjust) \ + X(vk_hdr_output_manual_rtx_adjust_down) \ + X(vk_hdr_output_manual_rtx_adjust_additive_down) \ + X(vk_hdr_output_manual_adjust_ui_down) \ + X(vk_hdr_output_manual_adjust_down) \ #define EXTERN_CVAR(cvar) extern cvar_t *cvar; DECLARE_CVAR(EXTERN_CVAR) diff --git a/ref_vk/vk_denoiser.c b/ref_vk/vk_denoiser.c index 0fb324341..1d4a0e809 100644 --- a/ref_vk/vk_denoiser.c +++ b/ref_vk/vk_denoiser.c @@ -85,12 +85,30 @@ static void createLayouts( void ) { VK_DescriptorsCreate(&g_denoiser.descriptors); } + static VkPipeline createPipeline( void ) { struct ShaderSpec { int hdr_output; - } spec_data = { (vk_core.hdr && CVAR_TO_BOOL(vk_hdr)) ? 1 : 0 }; + float hdr_output_max_luminance; + int hdr_output_auto_adjust; + float hdr_output_manual_adjust_down; + float hdr_output_manual_adjust_additive_down; + } spec_data = { + .hdr_output = (vk_core.hdr_output && CVAR_TO_BOOL(vk_hdr_output)) ? 1 : 0, + .hdr_output_max_luminance = 0, // TODO + .hdr_output_auto_adjust = 0, // TODO + // Auto level by VkHdrMetadataEXT.maxLuminance + // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkHdrMetadataEXT.html + // https://gpuopen.com/learn/using-amd-freesync-premium-pro-hdr-code-samples/ + .hdr_output_manual_adjust_down = vk_core.hdr_output ? vk_hdr_output_manual_rtx_adjust_down->value : 0, + .hdr_output_manual_adjust_additive_down = vk_core.hdr_output ? vk_hdr_output_manual_rtx_adjust_additive_down->value : 0, + }; const VkSpecializationMapEntry spec_map[] = { {.constantID = 0, .offset = offsetof(struct ShaderSpec, hdr_output), .size = sizeof(int) }, + {.constantID = 1, .offset = offsetof(struct ShaderSpec, hdr_output_max_luminance), .size = sizeof(int) }, + {.constantID = 2, .offset = offsetof(struct ShaderSpec, hdr_output_auto_adjust), .size = sizeof(int) }, + {.constantID = 3, .offset = offsetof(struct ShaderSpec, hdr_output_manual_adjust_down), .size = sizeof(int) }, + {.constantID = 4, .offset = offsetof(struct ShaderSpec, hdr_output_manual_adjust_additive_down), .size = sizeof(int) }, }; VkSpecializationInfo shader_spec = { .mapEntryCount = ARRAYSIZE(spec_map), diff --git a/ref_vk/vk_framectl.c b/ref_vk/vk_framectl.c index 79b5725ee..c1d5546d1 100644 --- a/ref_vk/vk_framectl.c +++ b/ref_vk/vk_framectl.c @@ -209,7 +209,7 @@ static uint32_t clamp_u32(uint32_t v, uint32_t min, uint32_t max) { static qboolean createSwapchain( void ) { VkSwapchainCreateInfoKHR *create_info = &g_frame.create_info; - qboolean hdr_output = vk_core.hdr && CVAR_TO_BOOL( vk_hdr ); + qboolean hdr_output = vk_core.hdr_output && CVAR_TO_BOOL( vk_hdr_output ); const uint32_t prev_num_images = g_frame.num_images; // recreating swapchain means invalidating any previous frames g_frame.last_frame_index = -1; @@ -527,7 +527,7 @@ static void toggleRaytracing( void ) { qboolean VK_FrameCtlInit( void ) { - qboolean hdr_output = vk_core.hdr && CVAR_TO_BOOL( vk_hdr ); + qboolean hdr_output = vk_core.hdr_output && CVAR_TO_BOOL( vk_hdr_output ); PROFILER_SCOPES(APROF_SCOPE_INIT); vk_frame.render_pass.raster = createRenderPass(false, hdr_output); if (vk_core.rtx) diff --git a/ref_vk/vk_render.c b/ref_vk/vk_render.c index 86ee949b3..35b1da736 100644 --- a/ref_vk/vk_render.c +++ b/ref_vk/vk_render.c @@ -71,11 +71,18 @@ static qboolean createPipelines( void ) float alpha_test_threshold; uint32_t max_dlights; int hdr_output; - } spec_data = { .25f, MAX_DLIGHTS, (vk_core.hdr && CVAR_TO_BOOL(vk_hdr)) ? 1 : 0 }; + float hdr_output_manual_adjust_down; + } spec_data = { + .alpha_test_threshold = .25f, + .max_dlights = MAX_DLIGHTS, + .hdr_output = (vk_core.hdr_output && CVAR_TO_BOOL(vk_hdr_output)) ? 1 : 0, + .hdr_output_manual_adjust_down = vk_core.hdr_output ? vk_hdr_output_manual_adjust_down->value : 0, + }; const VkSpecializationMapEntry spec_map[] = { {.constantID = 0, .offset = offsetof(struct ShaderSpec, alpha_test_threshold), .size = sizeof(float) }, {.constantID = 1, .offset = offsetof(struct ShaderSpec, max_dlights), .size = sizeof(uint32_t) }, {.constantID = 2, .offset = offsetof(struct ShaderSpec, hdr_output), .size = sizeof(int) }, + {.constantID = 3, .offset = offsetof(struct ShaderSpec, hdr_output_manual_adjust_down), .size = sizeof(int) }, }; VkSpecializationInfo shader_spec = { From 4ea65ad810afd78a85e4efcf94156835ad738c8d Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Sun, 23 Jan 2022 03:10:45 +0300 Subject: [PATCH 16/17] fix hdr for last vulkan branch + tuned --- ref_vk/shaders/2d.frag | 5 ++- ref_vk/shaders/additive.rahit | 3 +- ref_vk/shaders/brush.frag | 10 ++++-- ref_vk/shaders/brush.vert | 3 +- ref_vk/shaders/color_spaces.glsl | 43 ++++++++++++++++++++++++++ ref_vk/shaders/denoiser.comp | 52 +++++++++----------------------- ref_vk/shaders/ray.rchit | 16 +++++----- ref_vk/shaders/ray.rgen | 9 +++--- ref_vk/shaders/tonemapping.glsl | 11 ++++++- ref_vk/vk_core.c | 4 +-- ref_vk/vk_cvar.c | 9 +++--- ref_vk/vk_rtx.c | 3 ++ 12 files changed, 107 insertions(+), 61 deletions(-) create mode 100644 ref_vk/shaders/color_spaces.glsl diff --git a/ref_vk/shaders/2d.frag b/ref_vk/shaders/2d.frag index 496abc5e8..dd7d150c2 100644 --- a/ref_vk/shaders/2d.frag +++ b/ref_vk/shaders/2d.frag @@ -1,5 +1,7 @@ #version 450 #include "tonemapping.glsl" +#include "color_spaces.glsl" + layout(set=0,binding=0) uniform sampler2D tex; @@ -15,7 +17,8 @@ void main() { vec4 img = texture(tex, vUv) * vColor; if (hdr_output > 0) { // FIXME: Need to composite properly into the color space of the HDR scene, using scRGB backbuffer provides simple solution - img.rgb = aces_tonemap(img.rgb) / hdr_output_manual_adjust_ui_down; + //img.rgb = aces_tonemap(img.rgb) / hdr_output_manual_adjust_ui_down; + img.rgb = OECF_sRGB(aces_tonemap(img.rgb)) / hdr_output_manual_adjust_ui_down; // hack } outColor = img; } diff --git a/ref_vk/shaders/additive.rahit b/ref_vk/shaders/additive.rahit index db71122b9..c890bd294 100644 --- a/ref_vk/shaders/additive.rahit +++ b/ref_vk/shaders/additive.rahit @@ -4,6 +4,7 @@ #include "ray_common.glsl" #include "ray_kusochki.glsl" +#include "color_spaces.glsl" layout (constant_id = 6) const uint MAX_TEXTURES = 4096; layout (set = 0, binding = 6) uniform sampler2D textures[MAX_TEXTURES]; @@ -33,7 +34,7 @@ void main() { const float overshoot = gl_HitTEXT - payload_additive.ray_distance; - payload_additive.color += color * smoothstep(additive_soft_overshoot, 0., overshoot); + payload_additive.color += sRGB_OECF(color) * smoothstep(additive_soft_overshoot, 0., overshoot); ignoreIntersectionEXT; } diff --git a/ref_vk/shaders/brush.frag b/ref_vk/shaders/brush.frag index 85b2c4b6c..18211ceb3 100644 --- a/ref_vk/shaders/brush.frag +++ b/ref_vk/shaders/brush.frag @@ -1,5 +1,7 @@ #version 450 #include "tonemapping.glsl" +#include "color_spaces.glsl" + layout (constant_id = 0) const float alpha_test_threshold = 0.; layout (constant_id = 1) const uint max_dlights = 1; @@ -39,7 +41,7 @@ void main() { if (hdr_output > 0) { // FIXME: Need an operator that understands the output luminance and middle gray stays at a reasonable level (400 vs 1000 nit display on 0.5) - baseColor.rgb = aces_tonemap(baseColor.rgb); + baseColor.rgb = OECF_sRGB(aces_tonemap(baseColor.rgb)); } if (baseColor.a < alpha_test_threshold) @@ -50,7 +52,11 @@ void main() { if ((vFlags & FLAG_VERTEX_LIGHTING) == 0) { vec3 lightmap = texture(sLightmap, vLightmapUV).rgb; if (hdr_output > 0) { - lightmap = aces_tonemap(lightmap); + //lightmap = OECF_sRGB(aces_tonemap(lightmap)); // best LDR (lightmap) in HDR + //lightmap = OECF_sRGB(lightmap); + //lightmap = lightmap; + //lightmap = OECF_sRGB(reinhard02(lightmap,vec3(.600))); + lightmap = OECF_sRGB(reinhard02(lightmap,vec3(.400)) * aces_tonemap(lightmap)); // TODO: histogram equalization } outColor.rgb += baseColor.rgb * lightmap; } else { diff --git a/ref_vk/shaders/brush.vert b/ref_vk/shaders/brush.vert index 7a00bd442..a1fcc1410 100644 --- a/ref_vk/shaders/brush.vert +++ b/ref_vk/shaders/brush.vert @@ -1,5 +1,6 @@ #version 450 #include "tonemapping.glsl" +#include "color_spaces.glsl" layout (constant_id = 2) const int hdr_output = 0; layout (constant_id = 3) const float hdr_output_manual_adjust_down = 1.6; @@ -34,7 +35,7 @@ void main() { vec4 lightmap = aLightColor; if (hdr_output > 0) { // FIXME: Avoid tone mapping "fix-ups", ideally done in scene-referred space - vColor.rgb = aces_tonemap(vColor.rgb) / hdr_output_manual_adjust_down; + vColor.rgb = OECF_sRGB(aces_tonemap(vColor.rgb)) / hdr_output_manual_adjust_down; lightmap.rgb /= hdr_output_manual_adjust_down; } diff --git a/ref_vk/shaders/color_spaces.glsl b/ref_vk/shaders/color_spaces.glsl new file mode 100644 index 000000000..4cb54dc61 --- /dev/null +++ b/ref_vk/shaders/color_spaces.glsl @@ -0,0 +1,43 @@ +// based on https://github.com/OGRECave/ogre/blob/f49bc9be79f6711a88f01892711120da717f6148/Samples/Media/PBR/filament/pbr_filament.frag.glsl#L108-L124 +float sRGB_OECF(const float sRGB) { + // IEC 61966-2-1:1999 + float linearLow = sRGB / 12.92; + float linearHigh = pow((sRGB + 0.055) / 1.055, 2.4); + return sRGB <= 0.04045 ? linearLow : linearHigh; +} +/** + * Reverse opto-electronic conversion function to the one that filament + * provides. Filament version has LDR RGB linear color -> LDR RGB non-linear + * color in sRGB space. This function will thus provide LDR RGB non-linear + * color in sRGB space -> LDR RGB linear color conversion. + */ +vec3 sRGB_OECF(const vec3 sRGB) +{ + return vec3(sRGB_OECF(sRGB.r), sRGB_OECF(sRGB.g), sRGB_OECF(sRGB.b)); +} +vec4 sRGB_OECF(const vec4 sRGB) +{ + return vec4(sRGB_OECF(sRGB.r), sRGB_OECF(sRGB.g), sRGB_OECF(sRGB.b), sRGB.w); +} + +// based on https://github.com/abhirocks1211/filament/blob/3e97ac5268a47d5625c7d166eb7dda0bbba14a4d/shaders/src/conversion_functions.fs#L20-L55 +//------------------------------------------------------------------------------ +// Opto-electronic conversion functions (linear to non-linear) +//------------------------------------------------------------------------------ + +float OECF_sRGB(const float linear) { + // IEC 61966-2-1:1999 + float sRGBLow = linear * 12.92; + float sRGBHigh = (pow(linear, 1.0 / 2.4) * 1.055) - 0.055; + return linear <= 0.0031308 ? sRGBLow : sRGBHigh; +} + +vec3 OECF_sRGB(const vec3 linear) { + return vec3(OECF_sRGB(linear.r), OECF_sRGB(linear.g), OECF_sRGB(linear.b)); +} +vec4 OECF_sRGB(const vec4 linear) { + return vec4(OECF_sRGB(linear.r), OECF_sRGB(linear.g), OECF_sRGB(linear.b), linear.w); +} +vec3 OECF_sRGBFast(const vec3 linear) { + return pow(linear, vec3(1.0 / 2.2)); +} diff --git a/ref_vk/shaders/denoiser.comp b/ref_vk/shaders/denoiser.comp index baa220c04..945565f10 100644 --- a/ref_vk/shaders/denoiser.comp +++ b/ref_vk/shaders/denoiser.comp @@ -2,6 +2,7 @@ #include "noise.glsl" #include "tonemapping.glsl" +#include "color_spaces.glsl" layout(local_size_x = 16, local_size_y = 8, local_size_z = 1) in; @@ -21,33 +22,6 @@ layout (constant_id = 0) const int hdr_output = 0; layout (constant_id = 3) const float hdr_manual_adjust_down = 3; layout (constant_id = 4) const float hdr_manual_adjust_additive_down = 1.5; -// Blatantly copypasted from https://www.shadertoy.com/view/XsGfWV -vec3 aces_tonemap(vec3 color){ - mat3 m1 = mat3( - 0.59719, 0.07600, 0.02840, - 0.35458, 0.90834, 0.13383, - 0.04823, 0.01566, 0.83777 - ); - mat3 m2 = mat3( - 1.60475, -0.10208, -0.00327, - -0.53108, 1.10813, -0.07276, - -0.07367, -0.00605, 1.07602 - ); - vec3 v = m1 * color; - vec3 a = v * (v + 0.0245786) - 0.000090537; - vec3 b = v * (0.983729 * v + 0.4329510) + 0.238081; - //return pow(clamp(m2 * (a / b), 0.0, 1.0), vec3(1.0 / 2.2)); - return clamp(m2 * (a / b), 0.0, 1.0); -} - -vec3 reinhard(vec3 color){ - return color / (color + 1.0); -} - -vec3 reinhard02(vec3 c, vec3 Cwhite2) { - return c * (1. + c / Cwhite2) / (1. + c); -} - float normpdf2(in float x2, in float sigma) { return 0.39894*exp(-0.5*x2/(sigma*sigma))/sigma; } float normpdf(in float x, in float sigma) { return normpdf2(x*x, sigma); } @@ -129,11 +103,16 @@ void main() { additivour = imageLoad(src_additive, pix).rgb; + //#define TONEMAPPER aces_tonemap + #define TONEMAPPER reinhard + + #ifdef TONEMAPPER if (hdr_output > 0) { - colour = aces_tonemap(colour) / hdr_manual_adjust_down; - speculour = aces_tonemap(speculour) / hdr_manual_adjust_down; - additivour = aces_tonemap(additivour) / hdr_manual_adjust_additive_down; + colour = TONEMAPPER(colour) / hdr_manual_adjust_down; + speculour = TONEMAPPER(speculour) / hdr_manual_adjust_down; + additivour = TONEMAPPER(additivour) / hdr_manual_adjust_additive_down; } + #endif if (specular_total_scale > 0.) { speculour /= specular_total_scale; @@ -143,13 +122,12 @@ void main() { //colour += imageLoad(src_specular, pix).rgb; colour += additivour; - - // HACK: exposure - // TODO: should be dynamic based on previous frames brightness - colour *= 4.; - - //colour = aces_tonemap(colour); - colour = reinhard02(colour, vec3(400.)); + + if (hdr_output < 0) { + //colour = TONEMAPPER(colour); + //colour = reinhard02(colour, vec3(400.)); + } + colour = OECF_sRGB(colour); // gamma-correction imageStore(dest, pix, vec4(colour, 0.)); //imageStore(dest, pix, imageLoad(src_diffuse_gi, pix)); diff --git a/ref_vk/shaders/ray.rchit b/ref_vk/shaders/ray.rchit index 1f39a6c9a..844dfe25d 100644 --- a/ref_vk/shaders/ray.rchit +++ b/ref_vk/shaders/ray.rchit @@ -4,8 +4,10 @@ #include "ray_kusochki.glsl" #include "ray_common.glsl" +#include "color_spaces.glsl" layout (constant_id = 6) const uint MAX_TEXTURES = 4096; +layout (constant_id = 8) const int hdr_output = 0; layout (set = 0, binding = 6) uniform sampler2D textures[MAX_TEXTURES]; layout (set = 0, binding = 13) uniform samplerCube skybox; @@ -102,10 +104,7 @@ void main() { payload.metalness = 0.; payload.material_index = tex_base_color; - // HACK: skyboxes are LDR now. They will look really dull after tonemapping - // We need to remaster them into HDR. While that is not done, we just tune them with pow(x, 2.2) which looks okay-ish - // See #230 - payload.emissive = pow(texture(skybox, gl_WorldRayDirectionEXT).rgb, vec3(2.2)); + payload.emissive = sRGB_OECF(texture(skybox, gl_WorldRayDirectionEXT).rgb); return; } @@ -164,10 +163,11 @@ void main() { payload.emissive = vec3(0.); if (any(greaterThan(kusok.emissive, vec3(0.)))) { - const vec3 emissive_color = base_color; - //const vec3 emissive_color = pow(base_color, vec3(2.2)); - //const float max_color = max(max(emissive_color.r, emissive_color.g), emissive_color.b); - payload.emissive = normalize(kusok.emissive) * emissive_color;// * mix(vec3(1.), kusok.emissive, smoothstep(.3, .6, max_color)); + if (hdr_output > 0) { + payload.emissive = kusok.emissive / (1.0/3.0) / 200 * sRGB_OECF(base_color); + } else { + payload.emissive = clamp(kusok.emissive / (1.0/3.0) / 50, 0, 1.5) * sRGB_OECF(base_color); + } } payload.kusok_index = kusok_index; diff --git a/ref_vk/shaders/ray.rgen b/ref_vk/shaders/ray.rgen index 5af6d8d34..cf0e93c70 100644 --- a/ref_vk/shaders/ray.rgen +++ b/ref_vk/shaders/ray.rgen @@ -173,7 +173,8 @@ void sampleSurfaceTriangle( const float area = 1.;//.5 * length(cross(v1 - v2, v1 - v3)); const float light_dist2 = dot(light_dir, light_dir); //float pdf = /*light_dist2 */ 1./ (area * light_dot); - float pdf = TWO_PI / triangleSolidAngle(payload_opaque.hit_pos_t.xyz, v1, v2, v3); + //float pdf = TWO_PI / triangleSolidAngle(payload_opaque.hit_pos_t.xyz, v1, v2, v3); + float pdf = PI / triangleSolidAngle(payload_opaque.hit_pos_t.xyz, v1, v2, v3); if (pdf > pdf_culling_threshold) #ifdef DEBUG_LIGHT_CULLING @@ -283,7 +284,7 @@ void computePointLights(uint cluster_index, vec3 throughput, vec3 view_dir, Mate //const float pdf = 1.f / (fdist * light_dot * spot_attenuation); //const float pdf = TWO_PI / asin(radius / dist); const float pdf = 1. / ((1. - sqrt(d2 - r2) / dist) * spot_attenuation); - color /= pdf; + color /= pdf * 2; } // if (dot(color,color) < color_culling_threshold) @@ -291,7 +292,7 @@ void computePointLights(uint cluster_index, vec3 throughput, vec3 view_dir, Mate vec3 ldiffuse, lspecular; evalSplitBRDF(payload_opaque.normal, light_dir_norm, view_dir, material, ldiffuse, lspecular); - ldiffuse *= color; + ldiffuse *= color / 2; lspecular *= color; vec3 combined = ldiffuse + lspecular; @@ -439,7 +440,7 @@ void main() { vec3 additive = traceAdditive(origin, direction, payload_opaque.hit_pos_t.w <= 0. ? L : payload_opaque.hit_pos_t.w); // Sky/envmap/emissive - if ((payload_opaque.kusok_index < 0) || any(greaterThan(payload_opaque.emissive, vec3(0.)))) { + if (payload_opaque.kusok_index < 0) { if (bounce == 0) { out_additive += payload_opaque.emissive * color_factor + additive; } else { diff --git a/ref_vk/shaders/tonemapping.glsl b/ref_vk/shaders/tonemapping.glsl index 51d535f21..8672aaa08 100644 --- a/ref_vk/shaders/tonemapping.glsl +++ b/ref_vk/shaders/tonemapping.glsl @@ -13,5 +13,14 @@ vec3 aces_tonemap(vec3 color){ vec3 v = m1 * color; vec3 a = v * (v + 0.0245786) - 0.000090537; vec3 b = v * (0.983729 * v + 0.4329510) + 0.238081; - return pow(clamp(m2 * (a / b), 0.0, 1.0), vec3(1.0 / 2.2)); // TODO: 2.2 is gamma, need to support the gamma console command, but smart because default gamma 2.5 (WHY?) https://github.com/FWGS/xash3d-fwgs/blob/master/engine/client/vid_common.c#L182 + //return pow(clamp(m2 * (a / b), 0.0, 1.0), vec3(1.0 / 2.2)); + return clamp(m2 * (a / b), 0.0, 1.0); +} + +vec3 reinhard(vec3 color){ + return color / (color + 1.0); +} + +vec3 reinhard02(vec3 c, vec3 Cwhite2) { + return c * (1. + c / Cwhite2) / (1. + c); } diff --git a/ref_vk/vk_core.c b/ref_vk/vk_core.c index 5f1a4c850..fbbaa2f7e 100644 --- a/ref_vk/vk_core.c +++ b/ref_vk/vk_core.c @@ -738,11 +738,11 @@ qboolean R_VkInit( void ) if (!createDevice()) return false; - VK_LoadCvarsAfterInit(); - if (!initSurface()) return false; + VK_LoadCvarsAfterInit(); + if (!createCommandPool()) return false; diff --git a/ref_vk/vk_cvar.c b/ref_vk/vk_cvar.c index 21011f5fd..8738ab015 100644 --- a/ref_vk/vk_cvar.c +++ b/ref_vk/vk_cvar.c @@ -17,6 +17,7 @@ void VK_LoadCvars( void ) ui_infotool = gEngine.Cvar_Get( "ui_infotool", "0", FCVAR_CHEAT, "DEBUG: print entity info under crosshair" ); vk_only = gEngine.Cvar_Get( "vk_only", "0", FCVAR_GLCONFIG, "Full disable Ray Tracing pipeline" ); vk_device_target_id = gEngine.Cvar_Get( "vk_device_target_id", "", FCVAR_GLCONFIG, "Selected video device id" ); + } void VK_LoadCvarsAfterInit( void ) { @@ -34,11 +35,11 @@ void VK_LoadCvarsAfterInit( void ) //vk_hdr_output_auto_adjust = gEngine.Cvar_Get( "vk_hdr_output_auto_adjust", "1", FCVAR_GLCONFIG, "" ); if (vk_core.hdr_output) { vk_hdr_output = gEngine.Cvar_Get( "vk_hdr_output", "0", FCVAR_GLCONFIG, "EXPERIMENTAL: Enable or disable High Dynamic Range output (ENABLED HDR IN OS AND RESTART REQUIRED)" ); - vk_hdr_output_manual_rtx_adjust_down = gEngine.Cvar_Get( "vk_hdr_output_manual_rtx_adjust_down", "3", FCVAR_GLCONFIG, "EXPERIMENTAL: Adjust down output HDR level for color, specular (RESTART REQUIRED)" ); - vk_hdr_output_manual_rtx_adjust_additive_down = gEngine.Cvar_Get( "vk_hdr_output_manual_rtx_adjust_additive_down", "1.5", FCVAR_GLCONFIG, "EXPERIMENTAL: Adjust down output HDR level for additive (RESTART REQUIRED)" ); - vk_hdr_output_manual_adjust_ui_down = gEngine.Cvar_Get( "vk_hdr_output_manual_adjust_ui_down", "1.8", FCVAR_GLCONFIG, "EXPERIMENTAL: Adjust down output HDR level for UI (RESTART REQUIRED)" ); - vk_hdr_output_manual_adjust_down = gEngine.Cvar_Get( "vk_hdr_output_manual_adjust_down", "1.6", FCVAR_GLCONFIG, "EXPERIMENTAL: Adjust down output HDR level without RTX (RESTART REQUIRED)" ); } else { vk_hdr_output = gEngine.Cvar_Get( "vk_hdr_output", "0", FCVAR_READ_ONLY, "DISABLED: not supported by your hardware/software" ); } + vk_hdr_output_manual_rtx_adjust_down = gEngine.Cvar_Get( "vk_hdr_output_manual_rtx_adjust_down", "3", FCVAR_GLCONFIG, "EXPERIMENTAL: Adjust down output HDR level for color, specular (RESTART REQUIRED)" ); + vk_hdr_output_manual_rtx_adjust_additive_down = gEngine.Cvar_Get( "vk_hdr_output_manual_rtx_adjust_additive_down", "1.5", FCVAR_GLCONFIG, "EXPERIMENTAL: Adjust down output HDR level for additive (RESTART REQUIRED)" ); + vk_hdr_output_manual_adjust_ui_down = gEngine.Cvar_Get( "vk_hdr_output_manual_adjust_ui_down", "1.8", FCVAR_GLCONFIG, "EXPERIMENTAL: Adjust down output HDR level for UI (RESTART REQUIRED)" ); + vk_hdr_output_manual_adjust_down = gEngine.Cvar_Get( "vk_hdr_output_manual_adjust_down", "1.6", FCVAR_GLCONFIG, "EXPERIMENTAL: Adjust down output HDR level without RTX (RESTART REQUIRED)" ); } diff --git a/ref_vk/vk_rtx.c b/ref_vk/vk_rtx.c index 1c2f11587..cda685337 100644 --- a/ref_vk/vk_rtx.c +++ b/ref_vk/vk_rtx.c @@ -360,6 +360,7 @@ static void createPipeline( void ) int max_light_clusters; uint32_t max_textures; uint32_t sbt_record_size; + int hdr_output; } spec_data = { .max_point_lights = MAX_POINT_LIGHTS, .max_emissive_kusochki = MAX_EMISSIVE_KUSOCHKI, @@ -369,6 +370,7 @@ static void createPipeline( void ) .max_light_clusters = MAX_LIGHT_CLUSTERS, .max_textures = MAX_TEXTURES, .sbt_record_size = g_rtx.sbt_record_size, + .hdr_output = (vk_core.hdr_output && CVAR_TO_BOOL(vk_hdr_output)) ? 1 : 0, }; const VkSpecializationMapEntry spec_map[] = { {.constantID = 0, .offset = offsetof(struct RayShaderSpec, max_point_lights), .size = sizeof(int) }, @@ -379,6 +381,7 @@ static void createPipeline( void ) {.constantID = 5, .offset = offsetof(struct RayShaderSpec, max_light_clusters), .size = sizeof(int) }, {.constantID = 6, .offset = offsetof(struct RayShaderSpec, max_textures), .size = sizeof(uint32_t) }, {.constantID = 7, .offset = offsetof(struct RayShaderSpec, sbt_record_size), .size = sizeof(uint32_t) }, + {.constantID = 8, .offset = offsetof(struct RayShaderSpec, hdr_output), .size = sizeof(int) }, }; VkSpecializationInfo spec = { From 30a57d0d4a178381354ed8b929faf839019e8af8 Mon Sep 17 00:00:00 2001 From: NightFox <0x4E69676874466F78@users.noreply.github.com> Date: Mon, 24 Jan 2022 00:18:05 +0300 Subject: [PATCH 17/17] tune hdr for classic render mode --- ref_vk/shaders/brush.frag | 17 ++++++++---- ref_vk/shaders/brush.vert | 18 ++++++++++--- ref_vk/shaders/denoiser.comp | 10 ++++--- ref_vk/shaders/tonemapping.glsl | 48 +++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 12 deletions(-) diff --git a/ref_vk/shaders/brush.frag b/ref_vk/shaders/brush.frag index 18211ceb3..6e632094b 100644 --- a/ref_vk/shaders/brush.frag +++ b/ref_vk/shaders/brush.frag @@ -37,11 +37,14 @@ const float dlight_attenuation_const = 5000.; void main() { outColor = vec4(0.); - vec4 baseColor = vColor * texture(sTexture0, vTexture0); + vec4 baseColor = vColor * texture(sTexture0, vTexture0); // base color for all if (hdr_output > 0) { // FIXME: Need an operator that understands the output luminance and middle gray stays at a reasonable level (400 vs 1000 nit display on 0.5) - baseColor.rgb = OECF_sRGB(aces_tonemap(baseColor.rgb)); + //baseColor.rgb = OECF_sRGB(aces_tonemap(baseColor.rgb)); + //baseColor.rgb = OECF_sRGB(baseColor.rgb); + //baseColor.rgb = vec3(0.5); + baseColor.rgb = OECF_sRGB(aces_tonemap(baseColor.rgb) / 1.5); } if (baseColor.a < alpha_test_threshold) @@ -50,13 +53,17 @@ void main() { outColor.a = baseColor.a; if ((vFlags & FLAG_VERTEX_LIGHTING) == 0) { - vec3 lightmap = texture(sLightmap, vLightmapUV).rgb; + vec3 lightmap = texture(sLightmap, vLightmapUV).rgb; // lightmap for brush if (hdr_output > 0) { - //lightmap = OECF_sRGB(aces_tonemap(lightmap)); // best LDR (lightmap) in HDR + //lightmap = vec3(1); + lightmap = OECF_sRGB(aces_tonemap(lightmap) * 1.5); //lightmap = OECF_sRGB(lightmap); //lightmap = lightmap; //lightmap = OECF_sRGB(reinhard02(lightmap,vec3(.600))); - lightmap = OECF_sRGB(reinhard02(lightmap,vec3(.400)) * aces_tonemap(lightmap)); // TODO: histogram equalization + //lightmap = OECF_sRGB(reinhard02(lightmap,vec3(.400)) * aces_tonemap(lightmap)); // TODO: histogram equalization + //lightmap = OECF_sRGB(TonemapMGS5(lightmap)); // TODO: histogram equalization + //lightmap = TonemapMGS5(lightmap * 2); // TODO: histogram equalization + //lightmap = OECF_sRGB(uncharted2Tonemap(pow(lightmap,vec3(2)) * 2)); // TODO: histogram equalization } outColor.rgb += baseColor.rgb * lightmap; } else { diff --git a/ref_vk/shaders/brush.vert b/ref_vk/shaders/brush.vert index a1fcc1410..265a53058 100644 --- a/ref_vk/shaders/brush.vert +++ b/ref_vk/shaders/brush.vert @@ -31,12 +31,22 @@ void main() { vNormal = aNormal; vTexture0 = aTexture0; vLightmapUV = aLightmapUV; - vColor = ubo.color; - vec4 lightmap = aLightColor; + vColor = ubo.color; // pre-base color (filter) for all + vec4 lightmap = aLightColor; // lightmap for studiomodel (if FLAG_VERTEX_LIGHTING) if (hdr_output > 0) { // FIXME: Avoid tone mapping "fix-ups", ideally done in scene-referred space - vColor.rgb = OECF_sRGB(aces_tonemap(vColor.rgb)) / hdr_output_manual_adjust_down; - lightmap.rgb /= hdr_output_manual_adjust_down; + //vColor.rgb = OECF_sRGB(aces_tonemap(vColor.rgb)) / hdr_output_manual_adjust_down; + //lightmap.rgb /= hdr_output_manual_adjust_down; + //vColor.rgb = OECF_sRGB(vColor.rgb) / hdr_output_manual_adjust_down; + //lightmap.rgb = OECF_sRGB(lightmap.rgb) / hdr_output_manual_adjust_down; + //vColor.rgb = OECF_sRGB(uncharted2Tonemap(vColor.rgb * 2)); + //lightmap.rgb = OECF_sRGB(uncharted2Tonemap(lightmap.rgb * 2)); // additive (sprites) + //vColor.rgb = vec3(0.5); + + //lightmap.rgb = vec3(1); + + //lightmap.rgb = lightmap.rgb * 0.5; + lightmap.rgb = lightmap.rgb * 1.0; } if ((aFlags & FLAG_VERTEX_LIGHTING) != 0) { diff --git a/ref_vk/shaders/denoiser.comp b/ref_vk/shaders/denoiser.comp index 945565f10..4dd169c92 100644 --- a/ref_vk/shaders/denoiser.comp +++ b/ref_vk/shaders/denoiser.comp @@ -105,6 +105,8 @@ void main() { //#define TONEMAPPER aces_tonemap #define TONEMAPPER reinhard + //#define TONEMAPPER TonemapMGS5 + //#define TONEMAPPER uncharted2Tonemap #ifdef TONEMAPPER if (hdr_output > 0) { @@ -123,10 +125,12 @@ void main() { //colour += imageLoad(src_specular, pix).rgb; colour += additivour; - if (hdr_output < 0) { - //colour = TONEMAPPER(colour); - //colour = reinhard02(colour, vec3(400.)); + #ifdef TONEMAPPER + if (hdr_output == 0) { + colour = TONEMAPPER(colour); + //colour = reinhard02(colour, vec3(1.0)); } + #endif colour = OECF_sRGB(colour); // gamma-correction imageStore(dest, pix, vec4(colour, 0.)); diff --git a/ref_vk/shaders/tonemapping.glsl b/ref_vk/shaders/tonemapping.glsl index 8672aaa08..9cf0f2b8a 100644 --- a/ref_vk/shaders/tonemapping.glsl +++ b/ref_vk/shaders/tonemapping.glsl @@ -24,3 +24,51 @@ vec3 reinhard(vec3 color){ vec3 reinhard02(vec3 c, vec3 Cwhite2) { return c * (1. + c / Cwhite2) / (1. + c); } + +// https://github.com/SNMetamorph/PrimeXT/blob/7e3ac4bd6924c42e1d8f467dcf88fc8b6f105ca5/game_dir/glsl/postfx/tonemap_fp.glsl#L52-L60 +vec3 TonemapMGS5(vec3 source) +{ + const float a = 0.6; + const float b = 0.45333; + vec3 t = step(a, source); + vec3 f1 = source; + vec3 f2 = min(vec3(1.0), a + b - (b*b) / (f1 - a + b)); + return mix(f1, f2, t); +} + + +// https://github.com/dmnsgn/glsl-tone-map/blob/master/uncharted2.glsl +vec3 uncharted2Tonemap(vec3 x) { + float A = 0.15; + float B = 0.50; + float C = 0.10; + float D = 0.20; + float E = 0.02; + float F = 0.30; + float W = 11.2; + return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; +} +vec3 uncharted2(vec3 color) { + const float W = 11.2; + float exposureBias = 2.0; + vec3 curr = uncharted2Tonemap(exposureBias * color); + vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W)); + return curr * whiteScale; +} +float uncharted2Tonemap(float x) { + float A = 0.15; + float B = 0.50; + float C = 0.10; + float D = 0.20; + float E = 0.02; + float F = 0.30; + float W = 11.2; + return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; +} +float uncharted2(float color) { + const float W = 11.2; + const float exposureBias = 2.0; + float curr = uncharted2Tonemap(exposureBias * color); + float whiteScale = 1.0 / uncharted2Tonemap(W); + return curr * whiteScale; +} \ No newline at end of file