From f36c03f02e7b86b703016fe7db80a8d561bc8545 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 14:24:05 -0500 Subject: [PATCH 01/26] Add GTAO ambient occlusion --- Quake/gl_rmain.c | 31 + Quake/gl_rmisc.c | 329 ++++++- Quake/gl_texmgr.c | 14 +- Quake/gl_vidsdl.c | 880 +++++++++++++++++- Quake/glquake.h | 40 + Quake/r_alias.c | 8 +- Shaders/gtao.comp | 514 ++++++++++ Shaders/gtao_denoise.comp | 73 ++ Shaders/gtao_depth.comp | 78 ++ Shaders/screen_effects.inc | 114 ++- Shaders/shaders.h | 6 + Windows/VisualStudio/embedded.vcxproj | 45 + Windows/VisualStudio/embedded.vcxproj.filters | 11 +- Windows/VisualStudio/vkquake.vcxproj | 64 +- Windows/VisualStudio/vkquake.vcxproj.filters | 38 +- meson.build | 6 + 16 files changed, 2231 insertions(+), 20 deletions(-) create mode 100644 Shaders/gtao.comp create mode 100644 Shaders/gtao_denoise.comp create mode 100644 Shaders/gtao_depth.comp diff --git a/Quake/gl_rmain.c b/Quake/gl_rmain.c index cae7c244d..0b1378830 100644 --- a/Quake/gl_rmain.c +++ b/Quake/gl_rmain.c @@ -115,6 +115,25 @@ float map_fallbackalpha; qboolean r_drawworld_cheatsafe, r_fullbright_cheatsafe, r_lightmap_cheatsafe; // johnfitz cvar_t r_scale = {"r_scale", "1", CVAR_ARCHIVE}; +cvar_t r_gtao = {"r_gtao", "1", CVAR_ARCHIVE}; +cvar_t r_gtao_radius = {"r_gtao_radius", "24", CVAR_ARCHIVE}; +cvar_t r_gtao_radius_multiplier = {"r_gtao_radius_multiplier", "1.457", CVAR_ARCHIVE}; +cvar_t r_gtao_falloff = {"r_gtao_falloff", "0.615", CVAR_ARCHIVE}; +cvar_t r_gtao_thickness = {"r_gtao_thickness", "0", CVAR_ARCHIVE}; +cvar_t r_gtao_strength = {"r_gtao_strength", "0.6", CVAR_ARCHIVE}; +cvar_t r_gtao_debug = {"r_gtao_debug", "0", CVAR_NONE}; +cvar_t r_gtao_normal_mode = {"r_gtao_normal_mode", "2", CVAR_ARCHIVE}; +cvar_t r_gtao_quality = {"r_gtao_quality", "2", CVAR_ARCHIVE}; +cvar_t r_gtao_noise_mode = {"r_gtao_noise_mode", "0", CVAR_ARCHIVE}; +cvar_t r_gtao_depth_prefilter = {"r_gtao_depth_prefilter", "1", CVAR_ARCHIVE}; +cvar_t r_gtao_depth_mip_offset = {"r_gtao_depth_mip_offset", "3.30", CVAR_ARCHIVE}; +cvar_t r_gtao_denoise = {"r_gtao_denoise", "2", CVAR_ARCHIVE}; +cvar_t r_gtao_bias = {"r_gtao_bias", "0", CVAR_ARCHIVE}; +cvar_t r_gtao_bent_normals = {"r_gtao_bent_normals", "0", CVAR_NONE}; +cvar_t r_gtao_multibounce = {"r_gtao_multibounce", "1", CVAR_ARCHIVE}; +cvar_t r_gtao_temporal = {"r_gtao_temporal", "0", CVAR_ARCHIVE}; +cvar_t r_gtao_temporal_blend = {"r_gtao_temporal_blend", "0.85", CVAR_ARCHIVE}; +cvar_t r_gtao_halfres = {"r_gtao_halfres", "1", CVAR_ARCHIVE}; cvar_t r_gpulightmapupdate = {"r_gpulightmapupdate", "1", CVAR_NONE}; cvar_t r_rtshadows = {"r_rtshadows", "2", CVAR_ARCHIVE}; @@ -417,6 +436,15 @@ static void R_SetupViewBeforeMark (void *unused) R_SetFrustum (r_fovx, r_fovy); // johnfitz -- use r_fov* vars R_SetupMatrices (); + vulkan_globals.gtao_viewport_x = r_refdef.vrect.x; + vulkan_globals.gtao_viewport_y = r_refdef.vrect.y; + vulkan_globals.gtao_viewport_width = r_refdef.vrect.width; + vulkan_globals.gtao_viewport_height = r_refdef.vrect.height; + vulkan_globals.gtao_projection[0] = 1.0f / vulkan_globals.projection_matrix[0]; + vulkan_globals.gtao_projection[1] = 1.0f / (-vulkan_globals.projection_matrix[5]); + vulkan_globals.gtao_projection[2] = vulkan_globals.projection_matrix[10]; + vulkan_globals.gtao_projection[3] = vulkan_globals.projection_matrix[14]; + memcpy (vulkan_globals.gtao_view_projection, vulkan_globals.view_projection_matrix, sizeof (vulkan_globals.gtao_view_projection)); // johnfitz -- cheat-protect some draw modes r_fullbright_cheatsafe = false; @@ -1153,6 +1181,9 @@ void R_RenderView (qboolean use_tasks, task_handle_t begin_rendering_task, task_ { task_handle_t before_mark = Task_AllocateAndAssignFunc (R_SetupViewBeforeMark, NULL, 0); Task_AddDependency (setup_frame_task, before_mark); + // Keep frame-local projection/viewport state from being overwritten while + // the previous frame's asynchronous end-render task is consuming it. + Task_AddDependency (begin_rendering_task, before_mark); task_handle_t store_efrags = INVALID_TASK_HANDLE; task_handle_t cull_surfaces = INVALID_TASK_HANDLE; diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index 1115497c5..2b2f2f4d0 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -60,6 +60,25 @@ extern cvar_t r_indirect; extern cvar_t r_tasks; extern cvar_t r_parallelmark; extern cvar_t r_usesops; +extern cvar_t r_gtao; +extern cvar_t r_gtao_radius; +extern cvar_t r_gtao_radius_multiplier; +extern cvar_t r_gtao_falloff; +extern cvar_t r_gtao_thickness; +extern cvar_t r_gtao_strength; +extern cvar_t r_gtao_debug; +extern cvar_t r_gtao_normal_mode; +extern cvar_t r_gtao_quality; +extern cvar_t r_gtao_noise_mode; +extern cvar_t r_gtao_depth_prefilter; +extern cvar_t r_gtao_depth_mip_offset; +extern cvar_t r_gtao_denoise; +extern cvar_t r_gtao_bias; +extern cvar_t r_gtao_bent_normals; +extern cvar_t r_gtao_multibounce; +extern cvar_t r_gtao_temporal; +extern cvar_t r_gtao_temporal_blend; +extern cvar_t r_gtao_halfres; #if defined(USE_SIMD) extern cvar_t r_simd; @@ -1436,7 +1455,7 @@ void R_CreateDescriptorSetLayouts () } { - ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, screen_effects_layout_bindings, 5); + ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, screen_effects_layout_bindings, 8); screen_effects_layout_bindings[0].binding = 0; screen_effects_layout_bindings[0].descriptorCount = 1; screen_effects_layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; @@ -1457,12 +1476,24 @@ void R_CreateDescriptorSetLayouts () screen_effects_layout_bindings[4].descriptorCount = 1; screen_effects_layout_bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; screen_effects_layout_bindings[4].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + screen_effects_layout_bindings[5].binding = 5; + screen_effects_layout_bindings[5].descriptorCount = 1; + screen_effects_layout_bindings[5].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + screen_effects_layout_bindings[5].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + screen_effects_layout_bindings[6].binding = 6; + screen_effects_layout_bindings[6].descriptorCount = 1; + screen_effects_layout_bindings[6].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + screen_effects_layout_bindings[6].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + screen_effects_layout_bindings[7].binding = 7; + screen_effects_layout_bindings[7].descriptorCount = 1; + screen_effects_layout_bindings[7].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + screen_effects_layout_bindings[7].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; descriptor_set_layout_create_info.bindingCount = countof (screen_effects_layout_bindings); descriptor_set_layout_create_info.pBindings = screen_effects_layout_bindings; memset (&vulkan_globals.screen_effects_set_layout, 0, sizeof (vulkan_globals.screen_effects_set_layout)); - vulkan_globals.screen_effects_set_layout.num_combined_image_samplers = 2; + vulkan_globals.screen_effects_set_layout.num_combined_image_samplers = 5; vulkan_globals.screen_effects_set_layout.num_storage_images = 1; err = vkCreateDescriptorSetLayout (vulkan_globals.device, &descriptor_set_layout_create_info, NULL, &vulkan_globals.screen_effects_set_layout.handle); @@ -1471,6 +1502,96 @@ void R_CreateDescriptorSetLayouts () GL_SetObjectName ((uint64_t)vulkan_globals.screen_effects_set_layout.handle, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, "screen effects"); } + { + ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, gtao_layout_bindings, 6); + gtao_layout_bindings[0].binding = 0; + gtao_layout_bindings[0].descriptorCount = 1; + gtao_layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + gtao_layout_bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + gtao_layout_bindings[1].binding = 1; + gtao_layout_bindings[1].descriptorCount = 1; + gtao_layout_bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + gtao_layout_bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + gtao_layout_bindings[2].binding = 2; + gtao_layout_bindings[2].descriptorCount = 1; + gtao_layout_bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + gtao_layout_bindings[2].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + gtao_layout_bindings[3].binding = 3; + gtao_layout_bindings[3].descriptorCount = 1; + gtao_layout_bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + gtao_layout_bindings[3].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + gtao_layout_bindings[4].binding = 4; + gtao_layout_bindings[4].descriptorCount = 1; + gtao_layout_bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + gtao_layout_bindings[4].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + gtao_layout_bindings[5].binding = 5; + gtao_layout_bindings[5].descriptorCount = 1; + gtao_layout_bindings[5].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + gtao_layout_bindings[5].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + + descriptor_set_layout_create_info.bindingCount = countof (gtao_layout_bindings); + descriptor_set_layout_create_info.pBindings = gtao_layout_bindings; + + memset (&vulkan_globals.gtao_set_layout, 0, sizeof (vulkan_globals.gtao_set_layout)); + vulkan_globals.gtao_set_layout.num_combined_image_samplers = 5; + vulkan_globals.gtao_set_layout.num_storage_images = 1; + + err = vkCreateDescriptorSetLayout (vulkan_globals.device, &descriptor_set_layout_create_info, NULL, &vulkan_globals.gtao_set_layout.handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateDescriptorSetLayout failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)vulkan_globals.gtao_set_layout.handle, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, "gtao"); + } + + { + ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, bindings, 3); + bindings[0].binding = 0; + bindings[0].descriptorCount = 1; + bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bindings[1].binding = 1; + bindings[1].descriptorCount = 1; + bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bindings[2].binding = 2; + bindings[2].descriptorCount = 1; + bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + bindings[2].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + descriptor_set_layout_create_info.bindingCount = countof (bindings); + descriptor_set_layout_create_info.pBindings = bindings; + memset (&vulkan_globals.gtao_depth_set_layout, 0, sizeof (vulkan_globals.gtao_depth_set_layout)); + vulkan_globals.gtao_depth_set_layout.num_combined_image_samplers = 2; + vulkan_globals.gtao_depth_set_layout.num_storage_images = 1; + err = vkCreateDescriptorSetLayout (vulkan_globals.device, &descriptor_set_layout_create_info, NULL, &vulkan_globals.gtao_depth_set_layout.handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateDescriptorSetLayout failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)vulkan_globals.gtao_depth_set_layout.handle, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, "gtao depth"); + } + + { + ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, bindings, 3); + bindings[0].binding = 0; + bindings[0].descriptorCount = 1; + bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bindings[1].binding = 1; + bindings[1].descriptorCount = 1; + bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bindings[2].binding = 2; + bindings[2].descriptorCount = 1; + bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + bindings[2].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + descriptor_set_layout_create_info.bindingCount = countof (bindings); + descriptor_set_layout_create_info.pBindings = bindings; + memset (&vulkan_globals.gtao_denoise_set_layout, 0, sizeof (vulkan_globals.gtao_denoise_set_layout)); + vulkan_globals.gtao_denoise_set_layout.num_combined_image_samplers = 2; + vulkan_globals.gtao_denoise_set_layout.num_storage_images = 1; + err = vkCreateDescriptorSetLayout (vulkan_globals.device, &descriptor_set_layout_create_info, NULL, &vulkan_globals.gtao_denoise_set_layout.handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateDescriptorSetLayout failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)vulkan_globals.gtao_denoise_set_layout.handle, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, "gtao denoise"); + } + { ZEROED_STRUCT (VkDescriptorSetLayoutBinding, single_texture_cs_write_layout_binding); single_texture_cs_write_layout_binding.binding = 0; @@ -1921,7 +2042,7 @@ void R_CreatePipelineLayouts () ZEROED_STRUCT (VkPushConstantRange, push_constant_range); push_constant_range.offset = 0; - push_constant_range.size = 3 * sizeof (uint32_t) + 8 * sizeof (float); + push_constant_range.size = 6 * sizeof (uint32_t) + 10 * sizeof (float); push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; ZEROED_STRUCT (VkPipelineLayoutCreateInfo, pipeline_layout_create_info); @@ -1942,6 +2063,73 @@ void R_CreatePipelineLayouts () vulkan_globals.screen_effects_scale_sops_pipeline.layout.push_constant_range = push_constant_range; } + { + // GTAO + VkDescriptorSetLayout gtao_descriptor_set_layouts[1] = { + vulkan_globals.gtao_set_layout.handle, + }; + + ZEROED_STRUCT (VkPushConstantRange, push_constant_range); + push_constant_range.offset = 0; + push_constant_range.size = 12 * sizeof (uint32_t) + 20 * sizeof (float); + push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + + ZEROED_STRUCT (VkPipelineLayoutCreateInfo, pipeline_layout_create_info); + pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_create_info.setLayoutCount = 1; + pipeline_layout_create_info.pSetLayouts = gtao_descriptor_set_layouts; + pipeline_layout_create_info.pushConstantRangeCount = 1; + pipeline_layout_create_info.pPushConstantRanges = &push_constant_range; + + err = vkCreatePipelineLayout (vulkan_globals.device, &pipeline_layout_create_info, NULL, &vulkan_globals.gtao_pipeline.layout.handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreatePipelineLayout failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)vulkan_globals.gtao_pipeline.layout.handle, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "gtao_pipeline_layout"); + vulkan_globals.gtao_pipeline.layout.push_constant_range = push_constant_range; + vulkan_globals.gtao_msaa_pipeline.layout.handle = vulkan_globals.gtao_pipeline.layout.handle; + vulkan_globals.gtao_msaa_pipeline.layout.push_constant_range = push_constant_range; + } + + { + VkDescriptorSetLayout set_layouts[1] = {vulkan_globals.gtao_depth_set_layout.handle}; + ZEROED_STRUCT (VkPushConstantRange, push_constant_range); + push_constant_range.offset = 0; + push_constant_range.size = 6 * sizeof (uint32_t); + push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + ZEROED_STRUCT (VkPipelineLayoutCreateInfo, pipeline_layout_create_info); + pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_create_info.setLayoutCount = 1; + pipeline_layout_create_info.pSetLayouts = set_layouts; + pipeline_layout_create_info.pushConstantRangeCount = 1; + pipeline_layout_create_info.pPushConstantRanges = &push_constant_range; + err = vkCreatePipelineLayout (vulkan_globals.device, &pipeline_layout_create_info, NULL, &vulkan_globals.gtao_depth_pipeline.layout.handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreatePipelineLayout failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)vulkan_globals.gtao_depth_pipeline.layout.handle, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "gtao_depth_pipeline_layout"); + vulkan_globals.gtao_depth_pipeline.layout.push_constant_range = push_constant_range; + vulkan_globals.gtao_depth_msaa_pipeline.layout = vulkan_globals.gtao_depth_pipeline.layout; + vulkan_globals.gtao_depth_downsample_pipeline.layout = vulkan_globals.gtao_depth_pipeline.layout; + } + + { + VkDescriptorSetLayout set_layouts[1] = {vulkan_globals.gtao_denoise_set_layout.handle}; + ZEROED_STRUCT (VkPushConstantRange, push_constant_range); + push_constant_range.offset = 0; + push_constant_range.size = 4 * sizeof (uint32_t); + push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + ZEROED_STRUCT (VkPipelineLayoutCreateInfo, pipeline_layout_create_info); + pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_create_info.setLayoutCount = 1; + pipeline_layout_create_info.pSetLayouts = set_layouts; + pipeline_layout_create_info.pushConstantRangeCount = 1; + pipeline_layout_create_info.pPushConstantRanges = &push_constant_range; + err = vkCreatePipelineLayout (vulkan_globals.device, &pipeline_layout_create_info, NULL, &vulkan_globals.gtao_denoise_pipeline.layout.handle); + if (err != VK_SUCCESS) + Sys_Error ("vkCreatePipelineLayout failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)vulkan_globals.gtao_denoise_pipeline.layout.handle, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "gtao_denoise_pipeline_layout"); + vulkan_globals.gtao_denoise_pipeline.layout.push_constant_range = push_constant_range; + } + { // Texture warp VkDescriptorSetLayout tex_warp_descriptor_set_layouts[2] = { @@ -2403,6 +2591,12 @@ DECLARE_SHADER_MODULE (screen_effects_8bit_scale_sops_comp); DECLARE_SHADER_MODULE (screen_effects_10bit_comp); DECLARE_SHADER_MODULE (screen_effects_10bit_scale_comp); DECLARE_SHADER_MODULE (screen_effects_10bit_scale_sops_comp); +DECLARE_SHADER_MODULE (gtao_comp); +DECLARE_SHADER_MODULE (gtao_msaa_comp); +DECLARE_SHADER_MODULE (gtao_depth_comp); +DECLARE_SHADER_MODULE (gtao_depth_msaa_comp); +DECLARE_SHADER_MODULE (gtao_depth_downsample_comp); +DECLARE_SHADER_MODULE (gtao_denoise_comp); DECLARE_SHADER_MODULE (cs_tex_warp_comp); DECLARE_SHADER_MODULE (indirect_comp); DECLARE_SHADER_MODULE (indirect_clear_comp); @@ -3134,6 +3328,14 @@ static void R_CreateSkyPipelines () base.depth_stencil_state.depthTestEnable = VK_TRUE; base.depth_stencil_state.depthWriteEnable = VK_TRUE; + base.depth_stencil_state.stencilTestEnable = VK_TRUE; + base.depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS; + base.depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP; + base.depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP; + base.depth_stencil_state.front.passOp = VK_STENCIL_OP_REPLACE; + base.depth_stencil_state.front.compareMask = 0x1; + base.depth_stencil_state.front.writeMask = 0x1; + base.depth_stencil_state.front.reference = 0x1; pipeline_create_infos_t infos; for (int variant = 0; variant < MAIN_RENDER_PASS_VARIANT_COUNT; ++variant) @@ -3151,7 +3353,7 @@ static void R_CreateSkyPipelines () infos.depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP; infos.depth_stencil_state.front.passOp = VK_STENCIL_OP_REPLACE; infos.depth_stencil_state.front.compareMask = 0xFF; - infos.depth_stencil_state.front.writeMask = 0xFF; + infos.depth_stencil_state.front.writeMask = 0x1; infos.depth_stencil_state.front.reference = 0x1; infos.blend_attachment_states[0].colorWriteMask = 0; // We only want to write stencil R_CreateGraphicsPipeline ( @@ -3412,6 +3614,19 @@ static void R_CreateWorldPipelines () } } +static void R_SetViewModelStencilState (pipeline_create_infos_t *infos) +{ + infos->depth_stencil_state.stencilTestEnable = VK_TRUE; + infos->depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS; + infos->depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP; + infos->depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP; + infos->depth_stencil_state.front.passOp = VK_STENCIL_OP_REPLACE; + infos->depth_stencil_state.front.compareMask = 0x2; + infos->depth_stencil_state.front.writeMask = 0x2; + infos->depth_stencil_state.front.reference = 0x2; + infos->depth_stencil_state.back = infos->depth_stencil_state.front; +} + /* =============== R_CreateAliasPipelines @@ -3449,6 +3664,10 @@ static void R_CreateAliasPipelines () infos.depth_stencil_state.depthWriteEnable = alpha_blend ? VK_FALSE : VK_TRUE; R_CreateGraphicsPipeline ( &vulkan_globals.alias_pipelines[variant][pipeline_index], &infos, layout, va (variant ? "alias_main_oit %d" : "alias %d", pipeline_index)); + R_SetViewModelStencilState (&infos); + R_CreateGraphicsPipeline ( + &vulkan_globals.alias_viewmodel_pipelines[variant][pipeline_index], &infos, layout, + va (variant ? "alias_viewmodel_main_oit %d" : "alias_viewmodel %d", pipeline_index)); } if (alpha_blend) @@ -3548,6 +3767,10 @@ static void R_CreateMD5Pipelines () infos.depth_stencil_state.depthWriteEnable = alpha_blend ? VK_FALSE : VK_TRUE; R_CreateGraphicsPipeline ( &vulkan_globals.md5_pipelines[variant][pipeline_index], &infos, layout, va (variant ? "md5_main_oit %d" : "md5 %d", pipeline_index)); + R_SetViewModelStencilState (&infos); + R_CreateGraphicsPipeline ( + &vulkan_globals.md5_viewmodel_pipelines[variant][pipeline_index], &infos, layout, + va (variant ? "md5_viewmodel_main_oit %d" : "md5_viewmodel %d", pipeline_index)); } if (alpha_blend) @@ -3686,6 +3909,21 @@ static void R_CreateScreenEffectsPipelines () "screen_effects_scale_sops"); } +/* +=============== +R_CreateGTAOPipelines +=============== +*/ +static void R_CreateGTAOPipelines () +{ + R_CreateComputePipeline (&vulkan_globals.gtao_pipeline, gtao_comp_module, 0, NULL, "gtao"); + R_CreateComputePipeline (&vulkan_globals.gtao_msaa_pipeline, gtao_msaa_comp_module, 0, NULL, "gtao_msaa"); + R_CreateComputePipeline (&vulkan_globals.gtao_depth_pipeline, gtao_depth_comp_module, 0, NULL, "gtao_depth"); + R_CreateComputePipeline (&vulkan_globals.gtao_depth_msaa_pipeline, gtao_depth_msaa_comp_module, 0, NULL, "gtao_depth_msaa"); + R_CreateComputePipeline (&vulkan_globals.gtao_depth_downsample_pipeline, gtao_depth_downsample_comp_module, 0, NULL, "gtao_depth_downsample"); + R_CreateComputePipeline (&vulkan_globals.gtao_denoise_pipeline, gtao_denoise_comp_module, 0, NULL, "gtao_denoise"); +} + /* =============== R_CreateUpdateLightmapPipelines @@ -3781,6 +4019,12 @@ static void R_CreateShaderModules () CREATE_SHADER_MODULE (screen_effects_10bit_comp); CREATE_SHADER_MODULE (screen_effects_10bit_scale_comp); CREATE_SHADER_MODULE_COND (screen_effects_10bit_scale_sops_comp, vulkan_globals.screen_effects_sops); + CREATE_SHADER_MODULE (gtao_comp); + CREATE_SHADER_MODULE (gtao_msaa_comp); + CREATE_SHADER_MODULE (gtao_depth_comp); + CREATE_SHADER_MODULE (gtao_depth_msaa_comp); + CREATE_SHADER_MODULE (gtao_depth_downsample_comp); + CREATE_SHADER_MODULE (gtao_denoise_comp); CREATE_SHADER_MODULE (cs_tex_warp_comp); CREATE_SHADER_MODULE (indirect_comp); CREATE_SHADER_MODULE (indirect_clear_comp); @@ -3851,6 +4095,12 @@ static void R_DestroyShaderModules () DESTROY_SHADER_MODULE (screen_effects_10bit_comp); DESTROY_SHADER_MODULE (screen_effects_10bit_scale_comp); DESTROY_SHADER_MODULE (screen_effects_10bit_scale_sops_comp); + DESTROY_SHADER_MODULE (gtao_comp); + DESTROY_SHADER_MODULE (gtao_msaa_comp); + DESTROY_SHADER_MODULE (gtao_depth_comp); + DESTROY_SHADER_MODULE (gtao_depth_msaa_comp); + DESTROY_SHADER_MODULE (gtao_depth_downsample_comp); + DESTROY_SHADER_MODULE (gtao_denoise_comp); DESTROY_SHADER_MODULE (cs_tex_warp_comp); DESTROY_SHADER_MODULE (indirect_comp); DESTROY_SHADER_MODULE (indirect_clear_comp); @@ -3889,6 +4139,7 @@ void R_CreatePipelines () R_CreateMD5Pipelines (); R_CreatePostprocessPipelines (); R_CreateScreenEffectsPipelines (); + R_CreateGTAOPipelines (); R_CreateUpdateLightmapPipelines (); R_CreateIndirectComputePipelines (); R_CreateRayDebugPipelines (); @@ -4012,8 +4263,12 @@ void R_DestroyPipelines (void) { vkDestroyPipeline (vulkan_globals.device, vulkan_globals.alias_pipelines[variant][i].handle, NULL); vulkan_globals.alias_pipelines[variant][i].handle = VK_NULL_HANDLE; + vkDestroyPipeline (vulkan_globals.device, vulkan_globals.alias_viewmodel_pipelines[variant][i].handle, NULL); + vulkan_globals.alias_viewmodel_pipelines[variant][i].handle = VK_NULL_HANDLE; vkDestroyPipeline (vulkan_globals.device, vulkan_globals.md5_pipelines[variant][i].handle, NULL); vulkan_globals.md5_pipelines[variant][i].handle = VK_NULL_HANDLE; + vkDestroyPipeline (vulkan_globals.device, vulkan_globals.md5_viewmodel_pipelines[variant][i].handle, NULL); + vulkan_globals.md5_viewmodel_pipelines[variant][i].handle = VK_NULL_HANDLE; } vkDestroyPipeline (vulkan_globals.device, vulkan_globals.alias_wboit_pipelines[i].handle, NULL); vulkan_globals.alias_wboit_pipelines[i].handle = VK_NULL_HANDLE; @@ -4049,6 +4304,18 @@ void R_DestroyPipelines (void) vkDestroyPipeline (vulkan_globals.device, vulkan_globals.screen_effects_scale_sops_pipeline.handle, NULL); vulkan_globals.screen_effects_scale_sops_pipeline.handle = VK_NULL_HANDLE; } + vkDestroyPipeline (vulkan_globals.device, vulkan_globals.gtao_pipeline.handle, NULL); + vulkan_globals.gtao_pipeline.handle = VK_NULL_HANDLE; + vkDestroyPipeline (vulkan_globals.device, vulkan_globals.gtao_depth_pipeline.handle, NULL); + vulkan_globals.gtao_depth_pipeline.handle = VK_NULL_HANDLE; + vkDestroyPipeline (vulkan_globals.device, vulkan_globals.gtao_depth_msaa_pipeline.handle, NULL); + vulkan_globals.gtao_depth_msaa_pipeline.handle = VK_NULL_HANDLE; + vkDestroyPipeline (vulkan_globals.device, vulkan_globals.gtao_depth_downsample_pipeline.handle, NULL); + vulkan_globals.gtao_depth_downsample_pipeline.handle = VK_NULL_HANDLE; + vkDestroyPipeline (vulkan_globals.device, vulkan_globals.gtao_denoise_pipeline.handle, NULL); + vulkan_globals.gtao_denoise_pipeline.handle = VK_NULL_HANDLE; + vkDestroyPipeline (vulkan_globals.device, vulkan_globals.gtao_msaa_pipeline.handle, NULL); + vulkan_globals.gtao_msaa_pipeline.handle = VK_NULL_HANDLE; vkDestroyPipeline (vulkan_globals.device, vulkan_globals.cs_tex_warp_pipeline.handle, NULL); vulkan_globals.cs_tex_warp_pipeline.handle = VK_NULL_HANDLE; if (vulkan_globals.showtris_pipeline[MAIN_RENDER_PASS_STANDARD].handle != VK_NULL_HANDLE) @@ -4105,6 +4372,40 @@ static void R_ScaleChanged_f (cvar_t *var) R_InitSamplers (); } +/* +=================== +R_GTAODefaults_f +=================== +*/ +static void R_GTAODefaults_f (void) +{ + cvar_t *gtao_cvars[] = { + &r_gtao, + &r_gtao_radius, + &r_gtao_radius_multiplier, + &r_gtao_falloff, + &r_gtao_thickness, + &r_gtao_strength, + &r_gtao_debug, + &r_gtao_normal_mode, + &r_gtao_quality, + &r_gtao_noise_mode, + &r_gtao_depth_prefilter, + &r_gtao_depth_mip_offset, + &r_gtao_denoise, + &r_gtao_bias, + &r_gtao_bent_normals, + &r_gtao_multibounce, + &r_gtao_temporal, + &r_gtao_temporal_blend, + &r_gtao_halfres, + }; + + for (uint32_t i = 0; i < countof (gtao_cvars); ++i) + Cvar_SetQuick (gtao_cvars[i], gtao_cvars[i]->default_string); + Con_Printf ("GTAO settings restored to renderer defaults\n"); +} + /* =============== R_Init @@ -4116,6 +4417,7 @@ void R_Init (void) Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); Cmd_AddCommand ("pointfile", R_ReadPointFile_f); + Cmd_AddCommand ("gtao_defaults", R_GTAODefaults_f); cmd = Cmd_AddCommand ("r_showbboxes_filter", R_ShowbboxesFilter_f); if (cmd) @@ -4176,6 +4478,25 @@ void R_Init (void) Cvar_RegisterVariable (&r_telealpha); Cvar_RegisterVariable (&r_slimealpha); Cvar_RegisterVariable (&r_scale); + Cvar_RegisterVariable (&r_gtao); + Cvar_RegisterVariable (&r_gtao_radius); + Cvar_RegisterVariable (&r_gtao_radius_multiplier); + Cvar_RegisterVariable (&r_gtao_falloff); + Cvar_RegisterVariable (&r_gtao_thickness); + Cvar_RegisterVariable (&r_gtao_strength); + Cvar_RegisterVariable (&r_gtao_debug); + Cvar_RegisterVariable (&r_gtao_normal_mode); + Cvar_RegisterVariable (&r_gtao_quality); + Cvar_RegisterVariable (&r_gtao_noise_mode); + Cvar_RegisterVariable (&r_gtao_depth_prefilter); + Cvar_RegisterVariable (&r_gtao_depth_mip_offset); + Cvar_RegisterVariable (&r_gtao_denoise); + Cvar_RegisterVariable (&r_gtao_bias); + Cvar_RegisterVariable (&r_gtao_bent_normals); + Cvar_RegisterVariable (&r_gtao_multibounce); + Cvar_RegisterVariable (&r_gtao_temporal); + Cvar_RegisterVariable (&r_gtao_temporal_blend); + Cvar_RegisterVariable (&r_gtao_halfres); Cvar_RegisterVariable (&r_lodbias); Cvar_RegisterVariable (&gl_lodbias); Cvar_SetCallback (&r_scale, R_ScaleChanged_f); diff --git a/Quake/gl_texmgr.c b/Quake/gl_texmgr.c index 3ea46e6a3..f023668eb 100644 --- a/Quake/gl_texmgr.c +++ b/Quake/gl_texmgr.c @@ -806,8 +806,18 @@ void TexMgr_Init (void) TEMP_ALLOC (byte, bluenoise_rgba, sizeof (bluenoise_data) * 4); for (i = 0; i < sizeof (bluenoise_data); ++i) - for (int j = 0; j < 3; ++j) - bluenoise_rgba[i * 4 + j] = bluenoise_data[i]; + { + const int x = i & 63; + const int y = i >> 6; + const int second_x = (y + 17) & 63; + const int second_y = (63 - x + 31) & 63; + bluenoise_rgba[i * 4 + 0] = bluenoise_data[i]; + // Preserve the blue-noise spectrum while providing a decorrelated + // second dimension for spatial-only GTAO sampling. + bluenoise_rgba[i * 4 + 1] = bluenoise_data[second_y * 64 + second_x]; + bluenoise_rgba[i * 4 + 2] = bluenoise_data[i]; + bluenoise_rgba[i * 4 + 3] = 255; + } bluenoisetexture = TexMgr_LoadImage ( NULL, "bluenoise", 64, 64, SRC_RGBA, bluenoise_rgba, "", (src_offset_t)greytexture_data, TEXPREF_NEAREST | TEXPREF_PERSIST | TEXPREF_NOPICMIP); TEMP_FREE (bluenoise_rgba); diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index ffb51aaf3..ebd085e66 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -92,9 +92,11 @@ static void GL_CreateFrameBuffers (void); static void GL_CreateMainFrameBuffers (void); static void GL_DestroyMainFrameBuffers (void); static void GL_CreateOITBuffers (void); +static void GL_CreateGTAOBuffer (void); static void GL_DestroyOITBuffers (void); static void GL_DestroyMainRenderPasses (void); static void GL_DestroyRenderResources (void); +static qboolean GL_GTAOEnabled (void); viddef_t vid; // global video state modestate_t modestate = MS_UNINIT; @@ -152,6 +154,48 @@ static VkImageView swapchain_images_views[MAX_SWAP_CHAIN_IMAGES]; static VkImage depth_buffer; static vulkan_memory_t depth_buffer_memory; static VkImageView depth_buffer_view; +static VkImageView stencil_buffer_view; +static VkImage gtao_buffer; +static vulkan_memory_t gtao_buffer_memory; +static VkImageView gtao_buffer_view; +static qboolean gtao_buffer_initialized; +static VkImage gtao_denoise_buffer; +static vulkan_memory_t gtao_denoise_buffer_memory; +static VkImageView gtao_denoise_buffer_view; +static qboolean gtao_denoise_buffer_initialized; +static VkImage gtao_history_buffer; +static vulkan_memory_t gtao_history_buffer_memory; +static VkImageView gtao_history_buffer_view; +static qboolean gtao_history_initialized; +static float gtao_previous_view_projection[16]; +static vec3_t gtao_previous_origin; +static vec3_t gtao_previous_forward; +static int32_t gtao_previous_viewport_x; +static int32_t gtao_previous_viewport_y; +static uint32_t gtao_previous_viewport_width; +static uint32_t gtao_previous_viewport_height; +static float gtao_previous_projection[2]; +static qboolean gtao_previous_halfres; +static uint32_t gtao_previous_debug_mode; +typedef struct gtao_history_settings_s +{ + float radius; + float thickness; + float depth_mip_offset; + float bias; + float radius_multiplier; + float falloff_range; + uint32_t normal_mode; + uint32_t quality; + uint32_t noise_mode; +} gtao_history_settings_t; +static gtao_history_settings_t gtao_previous_history_settings; +static VkImage gtao_depth_pyramid; +static vulkan_memory_t gtao_depth_pyramid_memory; +static VkImageView gtao_depth_pyramid_view; +static VkImageView gtao_depth_mip_views[GTAO_DEPTH_MIP_LEVELS]; +static qboolean gtao_depth_pyramid_initialized; +static qboolean gtao_supported; static vulkan_memory_t color_buffers_memory[NUM_COLOR_BUFFERS]; static VkImageView color_buffers_view[NUM_COLOR_BUFFERS]; static vulkan_memory_t oit_accum_buffer_memory; @@ -1435,6 +1479,18 @@ static void GL_InitDevice (void) Sys_Error ("Cannot find VK_FORMAT_D24_UNORM_S8_UINT or VK_FORMAT_D32_SFLOAT_S8_UINT depth buffer format"); } + vkGetPhysicalDeviceFormatProperties (vulkan_physical_device, vulkan_globals.depth_format, &format_properties); + const VkFormatFeatureFlags depth_sampling_features = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT; + qboolean depth_sampling_support = (format_properties.optimalTilingFeatures & depth_sampling_features) == depth_sampling_features; + vkGetPhysicalDeviceFormatProperties (vulkan_physical_device, VK_FORMAT_R8G8B8A8_UNORM, &format_properties); + const VkFormatFeatureFlags gtao_image_features = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT; + qboolean gtao_image_support = (format_properties.optimalTilingFeatures & gtao_image_features) == gtao_image_features; + vkGetPhysicalDeviceFormatProperties (vulkan_physical_device, VK_FORMAT_R32_SFLOAT, &format_properties); + qboolean gtao_depth_pyramid_support = (format_properties.optimalTilingFeatures & gtao_image_features) == gtao_image_features; + gtao_supported = depth_sampling_support && gtao_image_support && gtao_depth_pyramid_support; + if (!gtao_supported) + Con_Printf ("GTAO unavailable: selected depth or AO image format is not sampleable/storage-capable\n"); + Con_Printf ("\n"); GET_GLOBAL_DEVICE_PROC_ADDR (vk_cmd_bind_pipeline, vkCmdBindPipeline); @@ -1597,9 +1653,9 @@ static void GL_CreateRenderPasses () attachment_descriptions[1].samples = vulkan_globals.sample_count; attachment_descriptions[1].format = vulkan_globals.depth_format; attachment_descriptions[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachment_descriptions[1].storeOp = use_oit ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment_descriptions[1].storeOp = (gtao_supported || use_oit) ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; attachment_descriptions[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachment_descriptions[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment_descriptions[1].stencilStoreOp = gtao_supported ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; if (resolve) { @@ -2016,7 +2072,7 @@ static void GL_CreateDepthBuffer (void) image_create_info.arrayLayers = 1; image_create_info.samples = vulkan_globals.sample_count; image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; - image_create_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + image_create_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | (gtao_supported ? VK_IMAGE_USAGE_SAMPLED_BIT : 0); assert (depth_buffer == VK_NULL_HANDLE); err = vkCreateImage (vulkan_globals.device, &image_create_info, NULL, &depth_buffer); @@ -2066,6 +2122,168 @@ static void GL_CreateDepthBuffer (void) Sys_Error ("vkCreateImageView failed with code %i", (int)err); GL_SetObjectName ((uint64_t)depth_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "Depth Buffer View"); + + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + assert (stencil_buffer_view == VK_NULL_HANDLE); + err = vkCreateImageView (vulkan_globals.device, &image_view_create_info, NULL, &stencil_buffer_view); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateImageView failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)stencil_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "Stencil Buffer View"); +} + +/* +=============== +GL_CreateGTAOBuffer +=============== +*/ +static void GL_CreateGTAOBuffer (void) +{ + Sys_Printf ("Creating GTAO buffer\n"); + + if (gtao_buffer != VK_NULL_HANDLE) + return; + + VkResult err; + + ZEROED_STRUCT (VkImageCreateInfo, image_create_info); + image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_create_info.imageType = VK_IMAGE_TYPE_2D; + image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; + image_create_info.extent.width = vid.width; + image_create_info.extent.height = vid.height; + image_create_info.extent.depth = 1; + image_create_info.mipLevels = 1; + image_create_info.arrayLayers = 1; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + + err = vkCreateImage (vulkan_globals.device, &image_create_info, NULL, >ao_buffer); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateImage failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)gtao_buffer, VK_OBJECT_TYPE_IMAGE, "GTAO Buffer"); + + VkMemoryRequirements memory_requirements; + vkGetImageMemoryRequirements (vulkan_globals.device, gtao_buffer, &memory_requirements); + + ZEROED_STRUCT (VkMemoryDedicatedAllocateInfoKHR, dedicated_allocation_info); + dedicated_allocation_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR; + dedicated_allocation_info.image = gtao_buffer; + + ZEROED_STRUCT (VkMemoryAllocateInfo, memory_allocate_info); + memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memory_allocate_info.allocationSize = memory_requirements.size; + memory_allocate_info.memoryTypeIndex = GL_MemoryTypeFromProperties (memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); + if (vulkan_globals.dedicated_allocation) + memory_allocate_info.pNext = &dedicated_allocation_info; + + assert (gtao_buffer_memory.handle == VK_NULL_HANDLE); + R_AllocateVulkanMemory (>ao_buffer_memory, &memory_allocate_info, VULKAN_MEMORY_TYPE_DEVICE, &num_vulkan_misc_allocations); + GL_SetObjectName ((uint64_t)gtao_buffer_memory.handle, VK_OBJECT_TYPE_DEVICE_MEMORY, "GTAO Buffer"); + + err = vkBindImageMemory (vulkan_globals.device, gtao_buffer, gtao_buffer_memory.handle, 0); + if (err != VK_SUCCESS) + Sys_Error ("vkBindImageMemory failed with code %i", (int)err); + + ZEROED_STRUCT (VkImageViewCreateInfo, image_view_create_info); + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; + image_view_create_info.image = gtao_buffer; + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_view_create_info.subresourceRange.baseMipLevel = 0; + image_view_create_info.subresourceRange.levelCount = 1; + image_view_create_info.subresourceRange.baseArrayLayer = 0; + image_view_create_info.subresourceRange.layerCount = 1; + image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + + err = vkCreateImageView (vulkan_globals.device, &image_view_create_info, NULL, >ao_buffer_view); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateImageView failed with code %i", (int)err); + + GL_SetObjectName ((uint64_t)gtao_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "GTAO Buffer View"); + gtao_buffer_initialized = false; + + image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + err = vkCreateImage (vulkan_globals.device, &image_create_info, NULL, >ao_denoise_buffer); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateImage failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)gtao_denoise_buffer, VK_OBJECT_TYPE_IMAGE, "GTAO Denoise Buffer"); + vkGetImageMemoryRequirements (vulkan_globals.device, gtao_denoise_buffer, &memory_requirements); + dedicated_allocation_info.image = gtao_denoise_buffer; + memory_allocate_info.allocationSize = memory_requirements.size; + memory_allocate_info.memoryTypeIndex = GL_MemoryTypeFromProperties (memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); + assert (gtao_denoise_buffer_memory.handle == VK_NULL_HANDLE); + R_AllocateVulkanMemory (>ao_denoise_buffer_memory, &memory_allocate_info, VULKAN_MEMORY_TYPE_DEVICE, &num_vulkan_misc_allocations); + GL_SetObjectName ((uint64_t)gtao_denoise_buffer_memory.handle, VK_OBJECT_TYPE_DEVICE_MEMORY, "GTAO Denoise Buffer"); + err = vkBindImageMemory (vulkan_globals.device, gtao_denoise_buffer, gtao_denoise_buffer_memory.handle, 0); + if (err != VK_SUCCESS) + Sys_Error ("vkBindImageMemory failed with code %i", (int)err); + image_view_create_info.image = gtao_denoise_buffer; + err = vkCreateImageView (vulkan_globals.device, &image_view_create_info, NULL, >ao_denoise_buffer_view); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateImageView failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)gtao_denoise_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "GTAO Denoise Buffer View"); + gtao_denoise_buffer_initialized = false; + + image_create_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + err = vkCreateImage (vulkan_globals.device, &image_create_info, NULL, >ao_history_buffer); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateImage failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)gtao_history_buffer, VK_OBJECT_TYPE_IMAGE, "GTAO History Buffer"); + + vkGetImageMemoryRequirements (vulkan_globals.device, gtao_history_buffer, &memory_requirements); + dedicated_allocation_info.image = gtao_history_buffer; + memory_allocate_info.allocationSize = memory_requirements.size; + memory_allocate_info.memoryTypeIndex = GL_MemoryTypeFromProperties (memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); + assert (gtao_history_buffer_memory.handle == VK_NULL_HANDLE); + R_AllocateVulkanMemory (>ao_history_buffer_memory, &memory_allocate_info, VULKAN_MEMORY_TYPE_DEVICE, &num_vulkan_misc_allocations); + GL_SetObjectName ((uint64_t)gtao_history_buffer_memory.handle, VK_OBJECT_TYPE_DEVICE_MEMORY, "GTAO History Buffer"); + err = vkBindImageMemory (vulkan_globals.device, gtao_history_buffer, gtao_history_buffer_memory.handle, 0); + if (err != VK_SUCCESS) + Sys_Error ("vkBindImageMemory failed with code %i", (int)err); + + image_view_create_info.image = gtao_history_buffer; + err = vkCreateImageView (vulkan_globals.device, &image_view_create_info, NULL, >ao_history_buffer_view); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateImageView failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)gtao_history_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "GTAO History Buffer View"); + gtao_history_initialized = false; + + image_create_info.format = VK_FORMAT_R32_SFLOAT; + image_create_info.mipLevels = GTAO_DEPTH_MIP_LEVELS; + image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + err = vkCreateImage (vulkan_globals.device, &image_create_info, NULL, >ao_depth_pyramid); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateImage failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)gtao_depth_pyramid, VK_OBJECT_TYPE_IMAGE, "GTAO Depth Pyramid"); + vkGetImageMemoryRequirements (vulkan_globals.device, gtao_depth_pyramid, &memory_requirements); + dedicated_allocation_info.image = gtao_depth_pyramid; + memory_allocate_info.allocationSize = memory_requirements.size; + memory_allocate_info.memoryTypeIndex = GL_MemoryTypeFromProperties (memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); + assert (gtao_depth_pyramid_memory.handle == VK_NULL_HANDLE); + R_AllocateVulkanMemory (>ao_depth_pyramid_memory, &memory_allocate_info, VULKAN_MEMORY_TYPE_DEVICE, &num_vulkan_misc_allocations); + GL_SetObjectName ((uint64_t)gtao_depth_pyramid_memory.handle, VK_OBJECT_TYPE_DEVICE_MEMORY, "GTAO Depth Pyramid"); + err = vkBindImageMemory (vulkan_globals.device, gtao_depth_pyramid, gtao_depth_pyramid_memory.handle, 0); + if (err != VK_SUCCESS) + Sys_Error ("vkBindImageMemory failed with code %i", (int)err); + + image_view_create_info.format = VK_FORMAT_R32_SFLOAT; + image_view_create_info.image = gtao_depth_pyramid; + image_view_create_info.subresourceRange.baseMipLevel = 0; + image_view_create_info.subresourceRange.levelCount = GTAO_DEPTH_MIP_LEVELS; + err = vkCreateImageView (vulkan_globals.device, &image_view_create_info, NULL, >ao_depth_pyramid_view); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateImageView failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)gtao_depth_pyramid_view, VK_OBJECT_TYPE_IMAGE_VIEW, "GTAO Depth Pyramid View"); + for (uint32_t mip = 0; mip < GTAO_DEPTH_MIP_LEVELS; ++mip) + { + image_view_create_info.subresourceRange.baseMipLevel = mip; + image_view_create_info.subresourceRange.levelCount = 1; + err = vkCreateImageView (vulkan_globals.device, &image_view_create_info, NULL, >ao_depth_mip_views[mip]); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateImageView failed with code %i", (int)err); + } + gtao_depth_pyramid_initialized = false; } /* @@ -2628,6 +2846,22 @@ void GL_UpdateDescriptorSets (void) R_FreeDescriptorSet (vulkan_globals.screen_effects_desc_set, &vulkan_globals.screen_effects_set_layout); vulkan_globals.screen_effects_desc_set = R_AllocateDescriptorSet (&vulkan_globals.screen_effects_set_layout); + if (vulkan_globals.gtao_desc_set != VK_NULL_HANDLE) + R_FreeDescriptorSet (vulkan_globals.gtao_desc_set, &vulkan_globals.gtao_set_layout); + vulkan_globals.gtao_desc_set = VK_NULL_HANDLE; + for (uint32_t pass = 0; pass < 2; ++pass) + { + if (vulkan_globals.gtao_denoise_desc_sets[pass] != VK_NULL_HANDLE) + R_FreeDescriptorSet (vulkan_globals.gtao_denoise_desc_sets[pass], &vulkan_globals.gtao_denoise_set_layout); + vulkan_globals.gtao_denoise_desc_sets[pass] = VK_NULL_HANDLE; + } + for (uint32_t mip = 0; mip < GTAO_DEPTH_MIP_LEVELS; ++mip) + { + if (vulkan_globals.gtao_depth_desc_sets[mip] != VK_NULL_HANDLE) + R_FreeDescriptorSet (vulkan_globals.gtao_depth_desc_sets[mip], &vulkan_globals.gtao_depth_set_layout); + vulkan_globals.gtao_depth_desc_sets[mip] = VK_NULL_HANDLE; + } + ZEROED_STRUCT (VkDescriptorImageInfo, input_image_info); input_image_info.imageView = color_buffers_view[1]; input_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -2647,7 +2881,146 @@ void GL_UpdateDescriptorSets (void) blue_noise_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; blue_noise_image_info.sampler = vulkan_globals.linear_sampler; - ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, screen_effects_writes, 5); + VkImageView gtao_input_view = color_buffers_view[1]; + if (gtao_supported) + { + vulkan_globals.gtao_desc_set = R_AllocateDescriptorSet (&vulkan_globals.gtao_set_layout); + + ZEROED_STRUCT (VkDescriptorImageInfo, gtao_depth_image_info); + gtao_depth_image_info.imageView = depth_buffer_view; + gtao_depth_image_info.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + gtao_depth_image_info.sampler = vulkan_globals.point_sampler; + + ZEROED_STRUCT (VkDescriptorImageInfo, gtao_output_image_info); + gtao_output_image_info.imageView = gtao_buffer_view; + gtao_output_image_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + + ZEROED_STRUCT (VkDescriptorImageInfo, gtao_stencil_image_info); + gtao_stencil_image_info.imageView = stencil_buffer_view; + gtao_stencil_image_info.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + gtao_stencil_image_info.sampler = vulkan_globals.point_sampler; + + ZEROED_STRUCT (VkDescriptorImageInfo, gtao_history_image_info); + gtao_history_image_info.imageView = gtao_history_buffer_view; + gtao_history_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + gtao_history_image_info.sampler = vulkan_globals.point_sampler; + + ZEROED_STRUCT (VkDescriptorImageInfo, gtao_depth_pyramid_image_info); + gtao_depth_pyramid_image_info.imageView = gtao_depth_pyramid_view; + gtao_depth_pyramid_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + gtao_depth_pyramid_image_info.sampler = vulkan_globals.point_sampler; + + ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, gtao_writes, 6); + gtao_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + gtao_writes[0].dstBinding = 0; + gtao_writes[0].descriptorCount = 1; + gtao_writes[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + gtao_writes[0].dstSet = vulkan_globals.gtao_desc_set; + gtao_writes[0].pImageInfo = >ao_depth_image_info; + + gtao_writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + gtao_writes[1].dstBinding = 1; + gtao_writes[1].descriptorCount = 1; + gtao_writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + gtao_writes[1].dstSet = vulkan_globals.gtao_desc_set; + gtao_writes[1].pImageInfo = &blue_noise_image_info; + + gtao_writes[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + gtao_writes[2].dstBinding = 2; + gtao_writes[2].descriptorCount = 1; + gtao_writes[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + gtao_writes[2].dstSet = vulkan_globals.gtao_desc_set; + gtao_writes[2].pImageInfo = >ao_output_image_info; + + gtao_writes[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + gtao_writes[3].dstBinding = 3; + gtao_writes[3].descriptorCount = 1; + gtao_writes[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + gtao_writes[3].dstSet = vulkan_globals.gtao_desc_set; + gtao_writes[3].pImageInfo = >ao_stencil_image_info; + + gtao_writes[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + gtao_writes[4].dstBinding = 4; + gtao_writes[4].descriptorCount = 1; + gtao_writes[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + gtao_writes[4].dstSet = vulkan_globals.gtao_desc_set; + gtao_writes[4].pImageInfo = >ao_history_image_info; + + gtao_writes[5].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + gtao_writes[5].dstBinding = 5; + gtao_writes[5].descriptorCount = 1; + gtao_writes[5].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + gtao_writes[5].dstSet = vulkan_globals.gtao_desc_set; + gtao_writes[5].pImageInfo = >ao_depth_pyramid_image_info; + + vkUpdateDescriptorSets (vulkan_globals.device, countof (gtao_writes), gtao_writes, 0, NULL); + + for (uint32_t mip = 0; mip < GTAO_DEPTH_MIP_LEVELS; ++mip) + { + vulkan_globals.gtao_depth_desc_sets[mip] = R_AllocateDescriptorSet (&vulkan_globals.gtao_depth_set_layout); + ZEROED_STRUCT_ARRAY (VkDescriptorImageInfo, image_infos, 3); + image_infos[0].imageView = mip == 0 ? depth_buffer_view : gtao_depth_mip_views[mip - 1]; + image_infos[0].imageLayout = mip == 0 ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_infos[0].sampler = vulkan_globals.point_sampler; + image_infos[1].imageView = gtao_depth_mip_views[mip]; + image_infos[1].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + image_infos[2].imageView = stencil_buffer_view; + image_infos[2].imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + image_infos[2].sampler = vulkan_globals.point_sampler; + ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, writes, 3); + for (uint32_t binding = 0; binding < 3; ++binding) + { + writes[binding].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[binding].dstSet = vulkan_globals.gtao_depth_desc_sets[mip]; + writes[binding].dstBinding = binding; + writes[binding].descriptorCount = 1; + writes[binding].descriptorType = binding == 1 ? VK_DESCRIPTOR_TYPE_STORAGE_IMAGE : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writes[binding].pImageInfo = &image_infos[binding]; + } + vkUpdateDescriptorSets (vulkan_globals.device, countof (writes), writes, 0, NULL); + } + + for (uint32_t pass = 0; pass < 2; ++pass) + { + vulkan_globals.gtao_denoise_desc_sets[pass] = R_AllocateDescriptorSet (&vulkan_globals.gtao_denoise_set_layout); + ZEROED_STRUCT_ARRAY (VkDescriptorImageInfo, image_infos, 3); + image_infos[0].imageView = pass == 0 ? gtao_buffer_view : gtao_denoise_buffer_view; + image_infos[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_infos[0].sampler = vulkan_globals.point_sampler; + image_infos[1].imageView = gtao_depth_pyramid_view; + image_infos[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_infos[1].sampler = vulkan_globals.point_sampler; + image_infos[2].imageView = pass == 0 ? gtao_denoise_buffer_view : gtao_buffer_view; + image_infos[2].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, writes, 3); + for (uint32_t binding = 0; binding < 3; ++binding) + { + writes[binding].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[binding].dstSet = vulkan_globals.gtao_denoise_desc_sets[pass]; + writes[binding].dstBinding = binding; + writes[binding].descriptorCount = 1; + writes[binding].descriptorType = binding == 2 ? VK_DESCRIPTOR_TYPE_STORAGE_IMAGE : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writes[binding].pImageInfo = &image_infos[binding]; + } + vkUpdateDescriptorSets (vulkan_globals.device, countof (writes), writes, 0, NULL); + } + gtao_input_view = gtao_buffer_view; + } + + ZEROED_STRUCT (VkDescriptorImageInfo, gtao_input_image_info); + gtao_input_image_info.imageView = gtao_input_view; + gtao_input_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + gtao_input_image_info.sampler = vulkan_globals.point_sampler; + ZEROED_STRUCT (VkDescriptorImageInfo, gtao_resolve_depth_image_info); + gtao_resolve_depth_image_info.imageView = gtao_supported ? gtao_depth_pyramid_view : color_buffers_view[1]; + gtao_resolve_depth_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + gtao_resolve_depth_image_info.sampler = vulkan_globals.point_sampler; + ZEROED_STRUCT (VkDescriptorImageInfo, gtao_denoise_image_info); + gtao_denoise_image_info.imageView = gtao_supported ? gtao_denoise_buffer_view : color_buffers_view[1]; + gtao_denoise_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + gtao_denoise_image_info.sampler = vulkan_globals.point_sampler; + + ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, screen_effects_writes, 8); screen_effects_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; screen_effects_writes[0].dstBinding = 0; screen_effects_writes[0].dstArrayElement = 0; @@ -2688,6 +3061,30 @@ void GL_UpdateDescriptorSets (void) screen_effects_writes[4].dstSet = vulkan_globals.screen_effects_desc_set; screen_effects_writes[4].pBufferInfo = &palette_octree_info; + screen_effects_writes[5].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + screen_effects_writes[5].dstBinding = 5; + screen_effects_writes[5].dstArrayElement = 0; + screen_effects_writes[5].descriptorCount = 1; + screen_effects_writes[5].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + screen_effects_writes[5].dstSet = vulkan_globals.screen_effects_desc_set; + screen_effects_writes[5].pImageInfo = >ao_input_image_info; + + screen_effects_writes[6].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + screen_effects_writes[6].dstBinding = 6; + screen_effects_writes[6].dstArrayElement = 0; + screen_effects_writes[6].descriptorCount = 1; + screen_effects_writes[6].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + screen_effects_writes[6].dstSet = vulkan_globals.screen_effects_desc_set; + screen_effects_writes[6].pImageInfo = >ao_resolve_depth_image_info; + + screen_effects_writes[7].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + screen_effects_writes[7].dstBinding = 7; + screen_effects_writes[7].dstArrayElement = 0; + screen_effects_writes[7].descriptorCount = 1; + screen_effects_writes[7].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + screen_effects_writes[7].dstSet = vulkan_globals.screen_effects_desc_set; + screen_effects_writes[7].pImageInfo = >ao_denoise_image_info; + vkUpdateDescriptorSets (vulkan_globals.device, countof (screen_effects_writes), screen_effects_writes, 0, NULL); #if defined(_DEBUG) @@ -3130,6 +3527,8 @@ static void GL_CreateRenderResources (void) GL_CreateColorBuffer (); GL_CreateDepthBuffer (); + if (gtao_supported) + GL_CreateGTAOBuffer (); GL_CreateRenderPasses (); GL_CreateFrameBuffers (); R_CreatePipelines (); @@ -3195,6 +3594,23 @@ static void GL_DestroyRenderResources (void) R_FreeDescriptorSet (vulkan_globals.screen_effects_desc_set, &vulkan_globals.screen_effects_set_layout); vulkan_globals.screen_effects_desc_set = VK_NULL_HANDLE; } + if (vulkan_globals.gtao_desc_set != VK_NULL_HANDLE) + { + R_FreeDescriptorSet (vulkan_globals.gtao_desc_set, &vulkan_globals.gtao_set_layout); + vulkan_globals.gtao_desc_set = VK_NULL_HANDLE; + } + for (uint32_t mip = 0; mip < GTAO_DEPTH_MIP_LEVELS; ++mip) + { + if (vulkan_globals.gtao_depth_desc_sets[mip] != VK_NULL_HANDLE) + R_FreeDescriptorSet (vulkan_globals.gtao_depth_desc_sets[mip], &vulkan_globals.gtao_depth_set_layout); + vulkan_globals.gtao_depth_desc_sets[mip] = VK_NULL_HANDLE; + } + for (uint32_t pass = 0; pass < 2; ++pass) + { + if (vulkan_globals.gtao_denoise_desc_sets[pass] != VK_NULL_HANDLE) + R_FreeDescriptorSet (vulkan_globals.gtao_denoise_desc_sets[pass], &vulkan_globals.gtao_denoise_set_layout); + vulkan_globals.gtao_denoise_desc_sets[pass] = VK_NULL_HANDLE; + } GL_DestroyMainFrameBuffers (); @@ -3220,13 +3636,49 @@ static void GL_DestroyRenderResources (void) vulkan_globals.color_buffers[i] = VK_NULL_HANDLE; } + vkDestroyImageView (vulkan_globals.device, stencil_buffer_view, NULL); vkDestroyImageView (vulkan_globals.device, depth_buffer_view, NULL); vkDestroyImage (vulkan_globals.device, depth_buffer, NULL); R_FreeVulkanMemory (&depth_buffer_memory, &num_vulkan_misc_allocations); + stencil_buffer_view = VK_NULL_HANDLE; depth_buffer_view = VK_NULL_HANDLE; depth_buffer = VK_NULL_HANDLE; + vkDestroyImageView (vulkan_globals.device, gtao_buffer_view, NULL); + vkDestroyImage (vulkan_globals.device, gtao_buffer, NULL); + R_FreeVulkanMemory (>ao_buffer_memory, &num_vulkan_misc_allocations); + + gtao_buffer_view = VK_NULL_HANDLE; + gtao_buffer = VK_NULL_HANDLE; + gtao_buffer_initialized = false; + + vkDestroyImageView (vulkan_globals.device, gtao_denoise_buffer_view, NULL); + vkDestroyImage (vulkan_globals.device, gtao_denoise_buffer, NULL); + R_FreeVulkanMemory (>ao_denoise_buffer_memory, &num_vulkan_misc_allocations); + gtao_denoise_buffer_view = VK_NULL_HANDLE; + gtao_denoise_buffer = VK_NULL_HANDLE; + gtao_denoise_buffer_initialized = false; + + vkDestroyImageView (vulkan_globals.device, gtao_history_buffer_view, NULL); + vkDestroyImage (vulkan_globals.device, gtao_history_buffer, NULL); + R_FreeVulkanMemory (>ao_history_buffer_memory, &num_vulkan_misc_allocations); + gtao_history_buffer_view = VK_NULL_HANDLE; + gtao_history_buffer = VK_NULL_HANDLE; + gtao_history_initialized = false; + + for (uint32_t mip = 0; mip < GTAO_DEPTH_MIP_LEVELS; ++mip) + { + vkDestroyImageView (vulkan_globals.device, gtao_depth_mip_views[mip], NULL); + gtao_depth_mip_views[mip] = VK_NULL_HANDLE; + } + vkDestroyImageView (vulkan_globals.device, gtao_depth_pyramid_view, NULL); + vkDestroyImage (vulkan_globals.device, gtao_depth_pyramid, NULL); + R_FreeVulkanMemory (>ao_depth_pyramid_memory, &num_vulkan_misc_allocations); + gtao_depth_pyramid_view = VK_NULL_HANDLE; + gtao_depth_pyramid = VK_NULL_HANDLE; + gtao_depth_pyramid_initialized = false; + for (uint32_t i = 0; i < num_swap_chain_images; ++i) { vkDestroyImageView (vulkan_globals.device, swapchain_images_views[i], NULL); @@ -3333,7 +3785,7 @@ void GL_BeginRenderingTask (void *unused) if (scbx_index <= SCBX_OIT_RESOLVE) { - const int main_render_pass_stencil = Sky_NeedStencil () ? MAIN_RENDER_PASS_STENCIL_CLEAR : MAIN_RENDER_PASS_NO_STENCIL; + const int main_render_pass_stencil = (Sky_NeedStencil () || GL_GTAOEnabled ()) ? MAIN_RENDER_PASS_STENCIL_CLEAR : MAIN_RENDER_PASS_NO_STENCIL; cbx->render_pass = vulkan_globals.main_render_pass [R_UseMBOIT () ? MAIN_RENDER_PASS_MBOIT : R_UseWBOIT () ? MAIN_RENDER_PASS_OIT @@ -3571,8 +4023,40 @@ typedef struct screen_effect_constants_s float poly_blend_g; float poly_blend_b; float poly_blend_a; + float gtao_strength; + uint32_t gtao_debug_mode; + uint32_t gtao_denoise; + float gtao_multibounce; + uint32_t gtao_halfres; } screen_effect_constants_t; +typedef struct gtao_constants_s +{ + int32_t viewport_x; + int32_t viewport_y; + uint32_t viewport_width; + uint32_t viewport_height; + float projection_x; + float projection_y; + float projection_z; + float projection_w; + float radius; + float thickness; + uint32_t normal_mode; + uint32_t debug_mode; + uint32_t quality; + float depth_mip_offset; + float bias; + uint32_t flags; + float radius_multiplier; + float falloff_range; + float temporal_blend; + uint32_t padding; + float current_to_previous_clip_x[4]; + float current_to_previous_clip_y[4]; + float current_to_previous_clip_w[4]; +} gtao_constants_t; + typedef struct ray_debug_constants_s { float screen_size_rcp_x; @@ -3621,6 +4105,378 @@ typedef struct end_rendering_parms_s #define SCREEN_EFFECT_FLAG_WATER_WARP 0x4 #define SCREEN_EFFECT_FLAG_PALETTIZE 0x8 #define SCREEN_EFFECT_FLAG_MENU 0x10 +#define SCREEN_EFFECT_FLAG_GTAO 0x20 + +static qboolean GL_GTAOEnabled (void) +{ + return gtao_supported && (r_gtao.value > 0.0f || r_gtao_debug.value > 0.0f); +} + +typedef struct gtao_depth_constants_s +{ + uint32_t width; + uint32_t height; + float projection_z; + float projection_w; + float effect_radius; + float falloff_range; +} gtao_depth_constants_t; + +static void GL_GTAODepthPyramid (cb_context_t *cbx, end_rendering_parms_t *parms) +{ + R_BeginDebugUtilsLabel (cbx, "GTAO Depth Pyramid"); + uint32_t width = parms->vid_width; + uint32_t height = parms->vid_height; + for (uint32_t mip = 0; mip < GTAO_DEPTH_MIP_LEVELS; ++mip) + { + ZEROED_STRUCT (VkImageMemoryBarrier, barrier); + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.srcAccessMask = gtao_depth_pyramid_initialized ? VK_ACCESS_SHADER_READ_BIT : 0; + barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barrier.oldLayout = gtao_depth_pyramid_initialized ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = gtao_depth_pyramid; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = mip; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier); + + vulkan_pipeline_t *pipeline; + if (mip == 0) + pipeline = vulkan_globals.sample_count == VK_SAMPLE_COUNT_1_BIT ? &vulkan_globals.gtao_depth_pipeline : &vulkan_globals.gtao_depth_msaa_pipeline; + else + pipeline = &vulkan_globals.gtao_depth_downsample_pipeline; + R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); + vkCmdBindDescriptorSets (cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->layout.handle, 0, 1, &vulkan_globals.gtao_depth_desc_sets[mip], 0, NULL); + const gtao_depth_constants_t constants = { + width, height, vulkan_globals.gtao_projection[2], vulkan_globals.gtao_projection[3], + q_max (1.0f, r_gtao_radius.value) * CLAMP (0.3f, r_gtao_radius_multiplier.value, 3.0f), + CLAMP (0.01f, r_gtao_falloff.value, 1.0f)}; + R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); + vkCmdDispatch (cbx->cb, (width + 7) / 8, (height + 7) / 8, 1); + + barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier); + width = q_max (1u, width / 2); + height = q_max (1u, height / 2); + } + gtao_depth_pyramid_initialized = true; + R_EndDebugUtilsLabel (cbx); +} + +typedef struct gtao_denoise_constants_s +{ + uint32_t clamp_width; + uint32_t clamp_height; + uint32_t sample_stride; + float center_weight; +} gtao_denoise_constants_t; + +static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) +{ + const uint32_t debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 7); + const uint32_t pass_count = (debug_mode == 0u || debug_mode == 7u) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u; + if (pass_count == 0) + return; + + R_BeginDebugUtilsLabel (cbx, "GTAO Denoise"); + ZEROED_STRUCT (VkImageMemoryBarrier, barrier); + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.srcAccessMask = gtao_denoise_buffer_initialized ? VK_ACCESS_SHADER_READ_BIT : 0; + barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barrier.oldLayout = gtao_denoise_buffer_initialized ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = gtao_denoise_buffer; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier); + + R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_COMPUTE, vulkan_globals.gtao_denoise_pipeline); + vkCmdBindDescriptorSets (cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, vulkan_globals.gtao_denoise_pipeline.layout.handle, 0, 1, + &vulkan_globals.gtao_denoise_desc_sets[0], 0, NULL); + const uint32_t working_stride = r_gtao_halfres.value > 0.0f ? 2u : 1u; + const uint32_t working_width = (parms->vid_width + working_stride - 1) / working_stride; + const uint32_t working_height = (parms->vid_height + working_stride - 1) / working_stride; + gtao_denoise_constants_t constants = {working_width - 1, working_height - 1, 1u, pass_count == 1 ? 1.2f : 0.24f}; + R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); + vkCmdDispatch (cbx->cb, (working_width + 7) / 8, (working_height + 7) / 8, 1); + + if (pass_count >= 2) + { + ZEROED_STRUCT_ARRAY (VkImageMemoryBarrier, barriers, 2); + barriers[0] = barrier; + barriers[0].srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barriers[0].oldLayout = VK_IMAGE_LAYOUT_GENERAL; + barriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barriers[1] = barrier; + barriers[1].srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + barriers[1].dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barriers[1].oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barriers[1].newLayout = VK_IMAGE_LAYOUT_GENERAL; + barriers[1].image = gtao_buffer; + vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 2, barriers); + vkCmdBindDescriptorSets (cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, vulkan_globals.gtao_denoise_pipeline.layout.handle, 0, 1, + &vulkan_globals.gtao_denoise_desc_sets[1], 0, NULL); + constants.center_weight = pass_count == 2 ? 1.2f : 0.24f; + R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); + vkCmdDispatch (cbx->cb, (working_width + 7) / 8, (working_height + 7) / 8, 1); + barriers[1].srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barriers[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barriers[1].oldLayout = VK_IMAGE_LAYOUT_GENERAL; + barriers[1].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barriers[1]); + + if (pass_count == 3) + { + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + vkCmdPipelineBarrier ( + cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier); + vkCmdBindDescriptorSets (cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, vulkan_globals.gtao_denoise_pipeline.layout.handle, 0, 1, + &vulkan_globals.gtao_denoise_desc_sets[0], 0, NULL); + constants.center_weight = 1.2f; + R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); + vkCmdDispatch (cbx->cb, (working_width + 7) / 8, (working_height + 7) / 8, 1); + barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + vkCmdPipelineBarrier ( + cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier); + } + } + else + { + barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier); + } + gtao_denoise_buffer_initialized = true; + R_EndDebugUtilsLabel (cbx); +} + +/* +=============== +GL_GTAO +=============== +*/ +static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) +{ + R_BeginDebugUtilsLabel (cbx, "GTAO"); + + ZEROED_STRUCT_ARRAY (VkImageMemoryBarrier, image_barriers, 3); + + image_barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_barriers[0].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + image_barriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + image_barriers[0].oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + image_barriers[0].newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + image_barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_barriers[0].image = depth_buffer; + image_barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + image_barriers[0].subresourceRange.levelCount = 1; + image_barriers[0].subresourceRange.layerCount = 1; + + image_barriers[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_barriers[1].srcAccessMask = gtao_buffer_initialized ? VK_ACCESS_SHADER_READ_BIT : 0; + image_barriers[1].dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + image_barriers[1].oldLayout = gtao_buffer_initialized ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; + image_barriers[1].newLayout = VK_IMAGE_LAYOUT_GENERAL; + image_barriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_barriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_barriers[1].image = gtao_buffer; + image_barriers[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_barriers[1].subresourceRange.levelCount = 1; + image_barriers[1].subresourceRange.layerCount = 1; + + image_barriers[2].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_barriers[2].srcAccessMask = gtao_history_initialized ? VK_ACCESS_SHADER_READ_BIT : 0; + image_barriers[2].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + image_barriers[2].oldLayout = gtao_history_initialized ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; + image_barriers[2].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_barriers[2].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_barriers[2].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_barriers[2].image = gtao_history_buffer; + image_barriers[2].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_barriers[2].subresourceRange.levelCount = 1; + image_barriers[2].subresourceRange.layerCount = 1; + + vkCmdPipelineBarrier ( + cbx->cb, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, countof (image_barriers), image_barriers); + GL_GTAODepthPyramid (cbx, parms); + + GL_SetCanvas (cbx, CANVAS_NONE); + vulkan_pipeline_t *pipeline = vulkan_globals.sample_count == VK_SAMPLE_COUNT_1_BIT ? &vulkan_globals.gtao_pipeline : &vulkan_globals.gtao_msaa_pipeline; + R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); + vkCmdBindDescriptorSets (cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->layout.handle, 0, 1, &vulkan_globals.gtao_desc_set, 0, NULL); + + vec3_t origin_delta; + VectorSubtract (parms->origin, gtao_previous_origin, origin_delta); + const gtao_history_settings_t history_settings = { + .radius = q_max (1.0f, r_gtao_radius.value), + .thickness = q_max (0.0f, r_gtao_thickness.value), + .depth_mip_offset = r_gtao_depth_prefilter.value > 0.0f ? CLAMP (0.0f, r_gtao_depth_mip_offset.value, 8.0f) : 64.0f, + .bias = CLAMP (0.0f, r_gtao_bias.value, 0.5f), + .radius_multiplier = CLAMP (0.3f, r_gtao_radius_multiplier.value, 3.0f), + .falloff_range = CLAMP (0.01f, r_gtao_falloff.value, 1.0f), + .normal_mode = (uint32_t)CLAMP (0, (int)r_gtao_normal_mode.value, 2), + .quality = (uint32_t)CLAMP (0, (int)r_gtao_quality.value, 3), + .noise_mode = r_gtao_noise_mode.value > 0.0f ? 1u : 0u, + }; + const qboolean temporal_history_valid = gtao_history_initialized && + gtao_previous_viewport_x == vulkan_globals.gtao_viewport_x && gtao_previous_viewport_y == vulkan_globals.gtao_viewport_y && + gtao_previous_viewport_width == vulkan_globals.gtao_viewport_width && gtao_previous_viewport_height == vulkan_globals.gtao_viewport_height && + fabsf (gtao_previous_projection[0] - vulkan_globals.gtao_projection[0]) < 0.0001f && + fabsf (gtao_previous_projection[1] - vulkan_globals.gtao_projection[1]) < 0.0001f && + gtao_previous_halfres == (r_gtao_halfres.value > 0.0f) && + gtao_previous_debug_mode == (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 7) && + memcmp (>ao_previous_history_settings, &history_settings, sizeof (history_settings)) == 0 && + DotProduct (origin_delta, origin_delta) < 4096.0f && DotProduct (parms->forward, gtao_previous_forward) > 0.5f; + + float reprojection[3][4]; + const int previous_clip_rows[3] = {0, 1, 3}; + for (int output_row = 0; output_row < 3; ++output_row) + { + const int row = previous_clip_rows[output_row]; + vec3_t previous_row = { + gtao_previous_view_projection[0 * 4 + row], + gtao_previous_view_projection[1 * 4 + row], + gtao_previous_view_projection[2 * 4 + row], + }; + reprojection[output_row][0] = DotProduct (previous_row, parms->right); + reprojection[output_row][1] = -DotProduct (previous_row, parms->down); + reprojection[output_row][2] = -DotProduct (previous_row, parms->forward); + reprojection[output_row][3] = DotProduct (previous_row, parms->origin) + gtao_previous_view_projection[3 * 4 + row]; + } + + gtao_constants_t push_constants = { + .viewport_x = vulkan_globals.gtao_viewport_x, + .viewport_y = vulkan_globals.gtao_viewport_y, + .viewport_width = vulkan_globals.gtao_viewport_width, + .viewport_height = vulkan_globals.gtao_viewport_height, + .projection_x = vulkan_globals.gtao_projection[0], + .projection_y = vulkan_globals.gtao_projection[1], + .projection_z = vulkan_globals.gtao_projection[2], + .projection_w = vulkan_globals.gtao_projection[3], + .radius = q_max (1.0f, r_gtao_radius.value), + .thickness = q_max (0.0f, r_gtao_thickness.value), + .normal_mode = (uint32_t)CLAMP (0, (int)r_gtao_normal_mode.value, 2), + .debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 7), + .quality = (uint32_t)CLAMP (0, (int)r_gtao_quality.value, 3), + .depth_mip_offset = r_gtao_depth_prefilter.value > 0.0f ? CLAMP (0.0f, r_gtao_depth_mip_offset.value, 8.0f) : 64.0f, + .bias = CLAMP (0.0f, r_gtao_bias.value, 0.5f), + .flags = (r_gtao_bent_normals.value > 0.0f ? 1u : 0u) | + ((r_gtao_temporal.value > 0.0f && r_gtao_bent_normals.value <= 0.0f && temporal_history_valid && + (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 6 || (int)r_gtao_debug.value == 7)) ? 2u : 0u) | + (r_gtao_halfres.value > 0.0f ? 4u : 0u) | (r_gtao_noise_mode.value > 0.0f ? 8u : 0u), + .radius_multiplier = CLAMP (0.3f, r_gtao_radius_multiplier.value, 3.0f), + .falloff_range = CLAMP (0.01f, r_gtao_falloff.value, 1.0f), + .temporal_blend = CLAMP (0.0f, r_gtao_temporal_blend.value, 0.97f), + .padding = 0u, + }; + memcpy (push_constants.current_to_previous_clip_x, reprojection[0], sizeof (reprojection[0])); + memcpy (push_constants.current_to_previous_clip_y, reprojection[1], sizeof (reprojection[1])); + memcpy (push_constants.current_to_previous_clip_w, reprojection[2], sizeof (reprojection[2])); + R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (push_constants), &push_constants); + const uint32_t gtao_stride = r_gtao_halfres.value > 0.0f ? 2u : 1u; + vkCmdDispatch (cbx->cb, (parms->vid_width + 8 * gtao_stride - 1) / (8 * gtao_stride), (parms->vid_height + 8 * gtao_stride - 1) / (8 * gtao_stride), 1); + + const qboolean store_temporal_history = r_gtao_temporal.value > 0.0f && r_gtao_bent_normals.value <= 0.0f && + (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 6 || (int)r_gtao_debug.value == 7); + if (store_temporal_history) + { + ZEROED_STRUCT_ARRAY (VkImageMemoryBarrier, copy_barriers, 2); + copy_barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + copy_barriers[0].srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + copy_barriers[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + copy_barriers[0].oldLayout = VK_IMAGE_LAYOUT_GENERAL; + copy_barriers[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + copy_barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copy_barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copy_barriers[0].image = gtao_buffer; + copy_barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_barriers[0].subresourceRange.levelCount = 1; + copy_barriers[0].subresourceRange.layerCount = 1; + copy_barriers[1] = copy_barriers[0]; + copy_barriers[1].srcAccessMask = gtao_history_initialized ? VK_ACCESS_SHADER_READ_BIT : 0; + copy_barriers[1].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + copy_barriers[1].oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + copy_barriers[1].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + copy_barriers[1].image = gtao_history_buffer; + vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 2, copy_barriers); + + const uint32_t history_stride = r_gtao_halfres.value > 0.0f ? 2u : 1u; + ZEROED_STRUCT (VkImageCopy, history_copy); + history_copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + history_copy.srcSubresource.layerCount = 1; + history_copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + history_copy.dstSubresource.layerCount = 1; + history_copy.extent.width = (parms->vid_width + history_stride - 1) / history_stride; + history_copy.extent.height = (parms->vid_height + history_stride - 1) / history_stride; + history_copy.extent.depth = 1; + vkCmdCopyImage (cbx->cb, gtao_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, gtao_history_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &history_copy); + + copy_barriers[0].srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + copy_barriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + copy_barriers[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + copy_barriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + copy_barriers[1].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + copy_barriers[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + copy_barriers[1].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + copy_barriers[1].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 2, copy_barriers); + } + else + { + ZEROED_STRUCT (VkImageMemoryBarrier, ao_barrier); + ao_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + ao_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + ao_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + ao_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + ao_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + ao_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + ao_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + ao_barrier.image = gtao_buffer; + ao_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + ao_barrier.subresourceRange.levelCount = 1; + ao_barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier ( + cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &ao_barrier); + } + GL_GTAODenoise (cbx, parms); + + gtao_buffer_initialized = true; + gtao_history_initialized = store_temporal_history; + memcpy (gtao_previous_view_projection, vulkan_globals.gtao_view_projection, sizeof (gtao_previous_view_projection)); + VectorCopy (parms->origin, gtao_previous_origin); + VectorCopy (parms->forward, gtao_previous_forward); + gtao_previous_viewport_x = vulkan_globals.gtao_viewport_x; + gtao_previous_viewport_y = vulkan_globals.gtao_viewport_y; + gtao_previous_viewport_width = vulkan_globals.gtao_viewport_width; + gtao_previous_viewport_height = vulkan_globals.gtao_viewport_height; + gtao_previous_projection[0] = vulkan_globals.gtao_projection[0]; + gtao_previous_projection[1] = vulkan_globals.gtao_projection[1]; + gtao_previous_halfres = r_gtao_halfres.value > 0.0f; + gtao_previous_debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 7); + gtao_previous_history_settings = history_settings; + R_EndDebugUtilsLabel (cbx); +} /* =============== @@ -3708,6 +4564,8 @@ static void GL_ScreenEffects (cb_context_t *cbx, qboolean enabled, end_rendering screen_effect_flags |= SCREEN_EFFECT_FLAG_PALETTIZE; if (parms->menu) screen_effect_flags |= SCREEN_EFFECT_FLAG_MENU; + if (GL_GTAOEnabled ()) + screen_effect_flags |= SCREEN_EFFECT_FLAG_GTAO; const screen_effect_constants_t push_constants = { parms->vid_width - 1, @@ -3721,6 +4579,11 @@ static void GL_ScreenEffects (cb_context_t *cbx, qboolean enabled, end_rendering (float)parms->v_blend[1] / 255.0f, (float)parms->v_blend[2] / 255.0f, (float)parms->v_blend[3] / 255.0f, + r_gtao_strength.value, + (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 7), + (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 7) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u, + CLAMP (0.0f, r_gtao_multibounce.value, 1.0f), + r_gtao_halfres.value > 0.0f ? 1u : 0u, }; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (push_constants), &push_constants); } @@ -4027,7 +4890,7 @@ static void GL_EndRenderingTask (end_rendering_parms_t *parms) depth_clear_value.depthStencil.stencil = 0; const qboolean screen_effects = - parms->render_warp || (parms->render_scale >= 2) || parms->vid_palettize || (parms->polyblend && parms->v_blend[3]) || parms->menu || parms->ray_debug; + parms->render_warp || (parms->render_scale >= 2) || parms->vid_palettize || (parms->polyblend && parms->v_blend[3]) || parms->menu || parms->ray_debug || GL_GTAOEnabled (); { const qboolean resolve = (vulkan_globals.sample_count != VK_SAMPLE_COUNT_1_BIT); const qboolean use_mboit = parms->use_mboit; @@ -4070,7 +4933,7 @@ static void GL_EndRenderingTask (end_rendering_parms_t *parms) vulkan_globals.main_render_pass [use_mboit ? MAIN_RENDER_PASS_MBOIT : use_wboit ? MAIN_RENDER_PASS_OIT - : MAIN_RENDER_PASS_STANDARD][Sky_NeedStencil () ? MAIN_RENDER_PASS_STENCIL_CLEAR : MAIN_RENDER_PASS_NO_STENCIL]; + : MAIN_RENDER_PASS_STANDARD][(Sky_NeedStencil () || GL_GTAOEnabled ()) ? MAIN_RENDER_PASS_STENCIL_CLEAR : MAIN_RENDER_PASS_NO_STENCIL]; render_pass_begin_info.framebuffer = main_framebuffers[screen_effects ? 1 : 0]; render_pass_begin_info.renderArea = render_area; render_pass_begin_info.clearValueCount = use_mboit ? (resolve ? 6 : 5) : resolve ? (use_wboit ? 5 : 3) : (use_wboit ? 4 : 2); @@ -4104,6 +4967,9 @@ static void GL_EndRenderingTask (end_rendering_parms_t *parms) vkCmdEndRenderPass (render_passes_cb); } + if (GL_GTAOEnabled ()) + GL_GTAO (&vulkan_globals.primary_cb_contexts[PCBX_RENDER_PASSES], parms); + GL_ScreenEffects (&vulkan_globals.primary_cb_contexts[PCBX_RENDER_PASSES], screen_effects, parms); { diff --git a/Quake/glquake.h b/Quake/glquake.h index d9dd9ca67..48e948c4a 100644 --- a/Quake/glquake.h +++ b/Quake/glquake.h @@ -58,6 +58,7 @@ extern int glwidth, glheight; #define MIN_NB_DESCRIPTORS_PER_TYPE 32 #define NUM_COLOR_BUFFERS 2 +#define GTAO_DEPTH_MIP_LEVELS 5 #define INITIAL_STAGING_BUFFER_SIZE_KB 16384 #define FAN_INDEX_BUFFER_SIZE 126 @@ -428,10 +429,12 @@ typedef struct vulkan_pipeline_t sky_cube_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT][2]; vulkan_pipeline_t sky_layer_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT][2]; vulkan_pipeline_t alias_pipelines[MAIN_RENDER_PASS_VARIANT_COUNT][MODEL_PIPELINE_COUNT]; + vulkan_pipeline_t alias_viewmodel_pipelines[MAIN_RENDER_PASS_VARIANT_COUNT][MODEL_PIPELINE_COUNT]; vulkan_pipeline_t alias_wboit_pipelines[MODEL_PIPELINE_COUNT]; vulkan_pipeline_t alias_mboit_moment_pipelines[MODEL_PIPELINE_COUNT]; vulkan_pipeline_t alias_mboit_composite_pipelines[MODEL_PIPELINE_COUNT]; vulkan_pipeline_t md5_pipelines[MAIN_RENDER_PASS_VARIANT_COUNT][MODEL_PIPELINE_COUNT]; + vulkan_pipeline_t md5_viewmodel_pipelines[MAIN_RENDER_PASS_VARIANT_COUNT][MODEL_PIPELINE_COUNT]; vulkan_pipeline_t md5_wboit_pipelines[MODEL_PIPELINE_COUNT]; vulkan_pipeline_t md5_mboit_moment_pipelines[MODEL_PIPELINE_COUNT]; vulkan_pipeline_t md5_mboit_composite_pipelines[MODEL_PIPELINE_COUNT]; @@ -441,6 +444,12 @@ typedef struct vulkan_pipeline_t screen_effects_pipeline; vulkan_pipeline_t screen_effects_scale_pipeline; vulkan_pipeline_t screen_effects_scale_sops_pipeline; + vulkan_pipeline_t gtao_pipeline; + vulkan_pipeline_t gtao_msaa_pipeline; + vulkan_pipeline_t gtao_depth_pipeline; + vulkan_pipeline_t gtao_depth_msaa_pipeline; + vulkan_pipeline_t gtao_depth_downsample_pipeline; + vulkan_pipeline_t gtao_denoise_pipeline; vulkan_pipeline_t cs_tex_warp_pipeline; vulkan_pipeline_t showtris_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT]; vulkan_pipeline_t showtris_indirect_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT]; @@ -470,6 +479,12 @@ typedef struct VkDescriptorSet mboit_input_attachment_descriptor_set; VkDescriptorSet screen_effects_desc_set; vulkan_desc_set_layout_t screen_effects_set_layout; + VkDescriptorSet gtao_desc_set; + vulkan_desc_set_layout_t gtao_set_layout; + VkDescriptorSet gtao_depth_desc_sets[GTAO_DEPTH_MIP_LEVELS]; + vulkan_desc_set_layout_t gtao_depth_set_layout; + VkDescriptorSet gtao_denoise_desc_sets[2]; + vulkan_desc_set_layout_t gtao_denoise_set_layout; vulkan_desc_set_layout_t single_texture_cs_write_set_layout; vulkan_desc_set_layout_t lightmap_compute_set_layout; VkDescriptorSet indirect_compute_desc_set; @@ -495,6 +510,12 @@ typedef struct float projection_matrix[16]; float view_matrix[16]; float view_projection_matrix[16]; + int32_t gtao_viewport_x; + int32_t gtao_viewport_y; + uint32_t gtao_viewport_width; + uint32_t gtao_viewport_height; + float gtao_projection[4]; + float gtao_view_projection[16]; // Dispatch table PFN_vkCmdBindPipeline vk_cmd_bind_pipeline; @@ -568,6 +589,25 @@ extern cvar_t r_slimealpha; extern cvar_t r_dynamic; extern cvar_t r_novis; extern cvar_t r_scale; +extern cvar_t r_gtao; +extern cvar_t r_gtao_radius; +extern cvar_t r_gtao_radius_multiplier; +extern cvar_t r_gtao_falloff; +extern cvar_t r_gtao_thickness; +extern cvar_t r_gtao_strength; +extern cvar_t r_gtao_debug; +extern cvar_t r_gtao_normal_mode; +extern cvar_t r_gtao_quality; +extern cvar_t r_gtao_noise_mode; +extern cvar_t r_gtao_depth_prefilter; +extern cvar_t r_gtao_depth_mip_offset; +extern cvar_t r_gtao_denoise; +extern cvar_t r_gtao_bias; +extern cvar_t r_gtao_bent_normals; +extern cvar_t r_gtao_multibounce; +extern cvar_t r_gtao_temporal; +extern cvar_t r_gtao_temporal_blend; +extern cvar_t r_gtao_halfres; extern cvar_t gl_polyblend; extern cvar_t gl_nocolors; diff --git a/Quake/r_alias.c b/Quake/r_alias.c index bb519b457..e798614b3 100644 --- a/Quake/r_alias.c +++ b/Quake/r_alias.c @@ -109,15 +109,19 @@ static void GL_DrawAliasFrame ( cbx->render_pass_index == RENDER_PASS_INDEX_MBOIT_COMPOSITE; if (oit_pass && (showtris != 0 || !has_alpha)) return; + const main_render_pass_variant_t main_variant = R_MainPassPipelineVariant (cbx->render_pass_index); + const qboolean viewmodel = e == &cl.viewent && showtris == 0 && !oit_pass; if (paliashdr->poseverttype == PV_MD5) pipeline = R_PipelineForRenderPass ( - cbx->render_pass_index, vulkan_globals.md5_pipelines[R_MainPassPipelineVariant (cbx->render_pass_index)][pipeline_index], + cbx->render_pass_index, + viewmodel ? vulkan_globals.md5_viewmodel_pipelines[main_variant][pipeline_index] : vulkan_globals.md5_pipelines[main_variant][pipeline_index], vulkan_globals.md5_wboit_pipelines[pipeline_index], vulkan_globals.md5_mboit_moment_pipelines[pipeline_index], vulkan_globals.md5_mboit_composite_pipelines[pipeline_index]); else pipeline = R_PipelineForRenderPass ( - cbx->render_pass_index, vulkan_globals.alias_pipelines[R_MainPassPipelineVariant (cbx->render_pass_index)][pipeline_index], + cbx->render_pass_index, + viewmodel ? vulkan_globals.alias_viewmodel_pipelines[main_variant][pipeline_index] : vulkan_globals.alias_pipelines[main_variant][pipeline_index], vulkan_globals.alias_wboit_pipelines[pipeline_index], vulkan_globals.alias_mboit_moment_pipelines[pipeline_index], vulkan_globals.alias_mboit_composite_pipelines[pipeline_index]); diff --git a/Shaders/gtao.comp b/Shaders/gtao.comp new file mode 100644 index 000000000..db6c95a53 --- /dev/null +++ b/Shaders/gtao.comp @@ -0,0 +1,514 @@ +#version 460 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +#ifdef GTAO_MSAA +layout (set = 0, binding = 0) uniform sampler2DMS depth_tex; +layout (set = 0, binding = 3) uniform usampler2DMS stencil_tex; +#else +layout (set = 0, binding = 0) uniform sampler2D depth_tex; +layout (set = 0, binding = 3) uniform usampler2D stencil_tex; +#endif +layout (set = 0, binding = 1) uniform sampler2D blue_noise_tex; +layout (set = 0, binding = 2, rgba8) uniform writeonly image2D ao_image; +layout (set = 0, binding = 4) uniform sampler2D ao_history_tex; +layout (set = 0, binding = 5) uniform sampler2D view_depth_pyramid_tex; + +layout (push_constant) uniform GTAOConsts +{ + ivec2 viewport_origin; + uvec2 viewport_size; + vec4 projection; + float radius; + float thickness; + uint normal_mode; + uint debug_mode; + uint quality; + float depth_mip_offset; + float bias; + uint flags; + float radius_multiplier; + float falloff_range; + float temporal_blend; + uint padding; + vec4 current_to_previous_clip_x; + vec4 current_to_previous_clip_y; + vec4 current_to_previous_clip_w; +} +push_constants; + +layout (local_size_x = 8, local_size_y = 8) in; + +const float PI = 3.14159265358979323846f; +const float HALF_PI = 1.57079632679489661923f; +const uint STENCIL_MASK_SKY = 0x1u; +const uint STENCIL_MASK_VIEWMODEL = 0x2u; +const uint GTAO_FLAG_BENT_NORMALS = 0x1u; +const uint GTAO_FLAG_TEMPORAL = 0x2u; +const uint GTAO_FLAG_HALFRES = 0x4u; +const uint GTAO_FLAG_HILBERT_NOISE = 0x8u; + +float sample_depth (ivec2 pixel) +{ +#ifdef GTAO_MSAA + float depth = 0.0f; + for (int sample_index = 0; sample_index < textureSamples (depth_tex); ++sample_index) + depth = max (depth, texelFetch (depth_tex, pixel, sample_index).r); + return depth; +#else + return texelFetch (depth_tex, pixel, 0).r; +#endif +} + +ivec2 depth_texture_size () +{ +#ifdef GTAO_MSAA + return textureSize (depth_tex); +#else + return textureSize (depth_tex, 0); +#endif +} + +uint sample_stencil (ivec2 pixel) +{ +#ifdef GTAO_MSAA + uint stencil = 0u; + for (int sample_index = 0; sample_index < textureSamples (stencil_tex); ++sample_index) + stencil |= texelFetch (stencil_tex, pixel, sample_index).r; + return stencil; +#else + return texelFetch (stencil_tex, pixel, 0).r; +#endif +} + +bool inside_viewport (ivec2 pixel) +{ + ivec2 viewport_max = push_constants.viewport_origin + ivec2 (push_constants.viewport_size); + return all (greaterThanEqual (pixel, push_constants.viewport_origin)) && all (lessThan (pixel, viewport_max)); +} + +vec3 reconstruct_position (ivec2 pixel, float depth) +{ + vec2 viewport_pixel = vec2 (pixel - push_constants.viewport_origin) + 0.5f; + vec2 ndc = viewport_pixel / vec2 (push_constants.viewport_size) * 2.0f - 1.0f; + float view_z = -push_constants.projection.w / (depth + push_constants.projection.z); + + return vec3 ( + ndc.x * (-view_z) * push_constants.projection.x, + ndc.y * view_z * push_constants.projection.y, + view_z); +} + +bool valid_surface_sample (ivec2 pixel, float depth) +{ + return inside_viewport (pixel) && depth > 0.00001f && (sample_stencil (pixel) & (STENCIL_MASK_SKY | STENCIL_MASK_VIEWMODEL)) == 0u; +} + +float sample_horizon_depth (ivec2 pixel, float offset_length) +{ + float depth = sample_depth (pixel); + if (push_constants.depth_mip_offset > 32.0f || !valid_surface_sample (pixel, depth)) + return depth; + + float mip_level = clamp (log2 (max (offset_length, 1.0f)) - push_constants.depth_mip_offset, 0.0f, float (textureQueryLevels (view_depth_pyramid_tex) - 1)); + int mip_0 = int (floor (mip_level)); + int mip_1 = min (mip_0 + 1, textureQueryLevels (view_depth_pyramid_tex) - 1); + vec2 uv = (vec2 (pixel) + 0.5f) / vec2 (textureSize (view_depth_pyramid_tex, 0)); + ivec2 size_0 = textureSize (view_depth_pyramid_tex, mip_0); + ivec2 size_1 = textureSize (view_depth_pyramid_tex, mip_1); + ivec2 coord_0 = clamp (ivec2 (uv * vec2 (size_0)), ivec2 (0), size_0 - 1); + ivec2 coord_1 = clamp (ivec2 (uv * vec2 (size_1)), ivec2 (0), size_1 - 1); + float depth_0 = texelFetch (view_depth_pyramid_tex, coord_0, mip_0).r; + float depth_1 = texelFetch (view_depth_pyramid_tex, coord_1, mip_1).r; + float view_depth = mix (depth_0, depth_1, fract (mip_level)); + return view_depth > 0.00001f ? push_constants.projection.w / view_depth - push_constants.projection.z : depth; +} + +vec4 calculate_edges (float center_z, float left_z, float right_z, float top_z, float bottom_z) +{ + vec4 edges = vec4 (left_z, right_z, top_z, bottom_z) - center_z; + float slope_lr = (edges.y - edges.x) * 0.5f; + float slope_tb = (edges.w - edges.z) * 0.5f; + vec4 slope_adjusted = edges + vec4 (slope_lr, -slope_lr, slope_tb, -slope_tb); + edges = min (abs (edges), abs (slope_adjusted)); + return clamp (1.25f - edges / max (center_z * 0.011f, 0.00001f), 0.0f, 1.0f); +} + +float pack_edges (vec4 edges) +{ + vec4 quantized = round (clamp (edges, 0.0f, 1.0f) * 2.9f); + return dot (quantized, vec4 (64.0f, 16.0f, 4.0f, 1.0f)) / 255.0f; +} + +vec3 safe_normalize (vec3 value) +{ + return value * inversesqrt (max (dot (value, value), 0.00000001f)); +} + +uint hilbert_index (uint pos_x, uint pos_y) +{ + uint index = 0u; + for (uint level = 32u; level > 0u; level /= 2u) + { + uint region_x = (pos_x & level) != 0u ? 1u : 0u; + uint region_y = (pos_y & level) != 0u ? 1u : 0u; + index += level * level * ((3u * region_x) ^ region_y); + if (region_y == 0u) + { + if (region_x != 0u) + { + pos_x = 63u - pos_x; + pos_y = 63u - pos_y; + } + uint temp = pos_x; + pos_x = pos_y; + pos_y = temp; + } + } + return index; +} + +mat3 rotation_from_to (vec3 from, vec3 to) +{ + float c = dot (from, to); + if (abs (c) > 0.9997f) + return mat3 (1.0f); + vec3 v = cross (from, to); + float h = 1.0f / (1.0f + c); + mat3 rows = mat3 ( + c + h * v.x * v.x, h * v.x * v.y - v.z, h * v.x * v.z + v.y, + h * v.x * v.y + v.z, c + h * v.y * v.y, h * v.y * v.z - v.x, + h * v.x * v.z - v.y, h * v.y * v.z + v.x, c + h * v.z * v.z); + return transpose (rows); +} + +vec2 oct_encode (vec3 normal) +{ + normal /= abs (normal.x) + abs (normal.y) + abs (normal.z); + vec2 encoded = normal.xy; + if (normal.z < 0.0f) + encoded = (1.0f - abs (encoded.yx)) * sign (encoded.xy); + return encoded * 0.5f + 0.5f; +} + +vec4 classification_color (uint stencil, bool has_depth, bool in_viewport) +{ + if (!in_viewport) + return vec4 (1.0f); + if ((stencil & STENCIL_MASK_SKY) != 0u) + return vec4 (1.0f, 0.0f, 0.0f, 1.0f); + if ((stencil & STENCIL_MASK_VIEWMODEL) != 0u) + return vec4 (0.0f, 0.25f, 1.0f, 1.0f); + if (!has_depth) + return vec4 (0.0f, 0.0f, 0.0f, 1.0f); + return vec4 (0.0f, 1.0f, 0.0f, 1.0f); +} + +void main () +{ + bool half_res = (push_constants.flags & GTAO_FLAG_HALFRES) != 0u; + int working_stride = half_res ? 2 : 1; + ivec2 ao_pixel = ivec2 (gl_GlobalInvocationID.xy); + ivec2 working_size = (depth_texture_size () + working_stride - 1) / working_stride; + if (any (greaterThanEqual (ao_pixel, working_size)) || any (greaterThanEqual (ao_pixel, imageSize (ao_image)))) + return; + ivec2 pixel = ao_pixel * working_stride; + + bool in_viewport = inside_viewport (pixel); + float center_depth = in_viewport ? sample_depth (pixel) : 0.0f; + uint center_stencil = in_viewport ? sample_stencil (pixel) : 0u; + bool has_depth = center_depth > 0.00001f; + if (!in_viewport || !has_depth || (center_stencil & (STENCIL_MASK_SKY | STENCIL_MASK_VIEWMODEL)) != 0u) + { + imageStore ( + ao_image, ao_pixel, + push_constants.debug_mode == 3u ? classification_color (center_stencil, has_depth, in_viewport) : vec4 (1.0f)); + return; + } + + ivec2 viewport_min = push_constants.viewport_origin; + ivec2 viewport_max = push_constants.viewport_origin + ivec2 (push_constants.viewport_size) - ivec2 (1); + vec3 center = reconstruct_position (pixel, center_depth); + + // XeGTAO reconstructs normals from immediate full-resolution depth + // neighbours. Keep that footprint independent of the AO working grid. + ivec2 normal_pixel_left = max (pixel - ivec2 (1, 0), viewport_min); + ivec2 normal_pixel_right = min (pixel + ivec2 (1, 0), viewport_max); + ivec2 normal_pixel_top = max (pixel - ivec2 (0, 1), viewport_min); + ivec2 normal_pixel_bottom = min (pixel + ivec2 (0, 1), viewport_max); + float normal_depth_left = sample_depth (normal_pixel_left); + float normal_depth_right = sample_depth (normal_pixel_right); + float normal_depth_top = sample_depth (normal_pixel_top); + float normal_depth_bottom = sample_depth (normal_pixel_bottom); + bool normal_left_valid = any (notEqual (normal_pixel_left, pixel)) && valid_surface_sample (normal_pixel_left, normal_depth_left); + bool normal_right_valid = any (notEqual (normal_pixel_right, pixel)) && valid_surface_sample (normal_pixel_right, normal_depth_right); + bool normal_top_valid = any (notEqual (normal_pixel_top, pixel)) && valid_surface_sample (normal_pixel_top, normal_depth_top); + bool normal_bottom_valid = any (notEqual (normal_pixel_bottom, pixel)) && valid_surface_sample (normal_pixel_bottom, normal_depth_bottom); + vec3 normal_left = normal_left_valid ? reconstruct_position (normal_pixel_left, normal_depth_left) : center; + vec3 normal_right = normal_right_valid ? reconstruct_position (normal_pixel_right, normal_depth_right) : center; + vec3 normal_top = normal_top_valid ? reconstruct_position (normal_pixel_top, normal_depth_top) : center; + vec3 normal_bottom = normal_bottom_valid ? reconstruct_position (normal_pixel_bottom, normal_depth_bottom) : center; + vec4 normal_edges_lrtb = calculate_edges (-center.z, -normal_left.z, -normal_right.z, -normal_top.z, -normal_bottom.z); + normal_edges_lrtb *= vec4 (normal_left_valid, normal_right_valid, normal_top_valid, normal_bottom_valid); + + // Edge metadata belongs to the AO grid because the denoiser consumes + // adjacent AO texels. At half resolution those are two source pixels apart. + vec3 edge_left = normal_left; + vec3 edge_right = normal_right; + vec3 edge_top = normal_top; + vec3 edge_bottom = normal_bottom; + bool edge_left_valid = normal_left_valid; + bool edge_right_valid = normal_right_valid; + bool edge_top_valid = normal_top_valid; + bool edge_bottom_valid = normal_bottom_valid; + if (half_res) + { + ivec2 edge_pixel_left = max (pixel - ivec2 (2, 0), viewport_min); + ivec2 edge_pixel_right = min (pixel + ivec2 (2, 0), viewport_max); + ivec2 edge_pixel_top = max (pixel - ivec2 (0, 2), viewport_min); + ivec2 edge_pixel_bottom = min (pixel + ivec2 (0, 2), viewport_max); + float edge_depth_left = sample_depth (edge_pixel_left); + float edge_depth_right = sample_depth (edge_pixel_right); + float edge_depth_top = sample_depth (edge_pixel_top); + float edge_depth_bottom = sample_depth (edge_pixel_bottom); + edge_left_valid = any (notEqual (edge_pixel_left, pixel)) && valid_surface_sample (edge_pixel_left, edge_depth_left); + edge_right_valid = any (notEqual (edge_pixel_right, pixel)) && valid_surface_sample (edge_pixel_right, edge_depth_right); + edge_top_valid = any (notEqual (edge_pixel_top, pixel)) && valid_surface_sample (edge_pixel_top, edge_depth_top); + edge_bottom_valid = any (notEqual (edge_pixel_bottom, pixel)) && valid_surface_sample (edge_pixel_bottom, edge_depth_bottom); + edge_left = edge_left_valid ? reconstruct_position (edge_pixel_left, edge_depth_left) : center; + edge_right = edge_right_valid ? reconstruct_position (edge_pixel_right, edge_depth_right) : center; + edge_top = edge_top_valid ? reconstruct_position (edge_pixel_top, edge_depth_top) : center; + edge_bottom = edge_bottom_valid ? reconstruct_position (edge_pixel_bottom, edge_depth_bottom) : center; + } + vec4 edges_lrtb = calculate_edges (-center.z, -edge_left.z, -edge_right.z, -edge_top.z, -edge_bottom.z); + edges_lrtb *= vec4 (edge_left_valid, edge_right_valid, edge_top_valid, edge_bottom_valid); + float packed_edges = pack_edges (edges_lrtb); + + vec3 dx; + vec3 dy; + if (push_constants.normal_mode == 0u) + { + dx = normal_right - normal_left; + dy = normal_top - normal_bottom; + } + else if (push_constants.normal_mode == 1u) + { + if (normal_left_valid && normal_right_valid) + dx = abs (normal_right.z - center.z) <= abs (normal_left.z - center.z) ? normal_right - center : center - normal_left; + else if (normal_right_valid) + dx = normal_right - center; + else + dx = center - normal_left; + + if (normal_top_valid && normal_bottom_valid) + dy = abs (normal_top.z - center.z) <= abs (normal_bottom.z - center.z) ? normal_top - center : center - normal_bottom; + else if (normal_top_valid) + dy = normal_top - center; + else + dy = center - normal_bottom; + } + else + { + vec4 accepted = clamp (vec4 ( + normal_edges_lrtb.x * normal_edges_lrtb.z, normal_edges_lrtb.z * normal_edges_lrtb.y, + normal_edges_lrtb.y * normal_edges_lrtb.w, normal_edges_lrtb.w * normal_edges_lrtb.x) + 0.01f, 0.0f, 1.0f); + vec3 dir_left = safe_normalize (normal_left - center); + vec3 dir_right = safe_normalize (normal_right - center); + vec3 dir_top = safe_normalize (normal_top - center); + vec3 dir_bottom = safe_normalize (normal_bottom - center); + vec3 xe_normal = accepted.x * cross (dir_left, dir_top) + accepted.y * cross (dir_top, dir_right) + + accepted.z * cross (dir_right, dir_bottom) + accepted.w * cross (dir_bottom, dir_left); + dx = xe_normal; + dy = vec3 (0.0f, 0.0f, 1.0f); + } + + vec3 normal_cross = push_constants.normal_mode == 2u ? dx : cross (dx, dy); + vec3 view_vector = normalize (-center); + vec3 normal = dot (normal_cross, normal_cross) > 0.00000001f ? normalize (normal_cross) : view_vector; + if (dot (normal, view_vector) < 0.0f) + normal = -normal; + center *= 0.99999f; + + if (push_constants.debug_mode == 2u) + { + imageStore (ao_image, ao_pixel, vec4 (normal * 0.5f + 0.5f, 1.0f)); + return; + } + if (push_constants.debug_mode == 3u) + { + imageStore (ao_image, ao_pixel, classification_color (center_stencil, true, true)); + return; + } + + // Noise belongs to the compact AO working grid. + ivec2 noise_pixel = ao_pixel; + noise_pixel &= ivec2 (63); + vec2 noise; + if ((push_constants.flags & GTAO_FLAG_HILBERT_NOISE) != 0u) + { + uint noise_index = hilbert_index (uint (noise_pixel.x), uint (noise_pixel.y)); + noise = fract (vec2 (0.5f) + float (noise_index) * vec2 (0.7548776662466928f, 0.5698402909980533f)); + } + else + { + noise = texelFetch (blue_noise_tex, noise_pixel, 0).rg; + } + // vkQuake has no TAA to absorb an animated sampling pattern. Reprojection may + // accumulate a fixed spatial pattern, but must not make the raw AO vary at rest. + float noise_slice = noise.x; + float noise_sample = noise.y; + float effect_radius = max (push_constants.radius, 1.0f) * push_constants.radius_multiplier; + float falloff_range = effect_radius * push_constants.falloff_range; + float falloff_from = effect_radius - falloff_range; + float falloff_mul = -1.0f / falloff_range; + float falloff_add = falloff_from / falloff_range + 1.0f; + float pixel_size_x = max ((-center.z) * push_constants.projection.x * 2.0f / float (push_constants.viewport_size.x), 0.00001f); + float pixel_size_y = max ((-center.z) * push_constants.projection.y * 2.0f / float (push_constants.viewport_size.y), 0.00001f); + if (push_constants.debug_mode == 5u) + { + float footprint = effect_radius / min (pixel_size_x, pixel_size_y); + float mip = float (clamp (int (floor (log2 (max (footprint, 1.0f)))) - 1, 0, textureQueryLevels (view_depth_pyramid_tex) - 1)); + vec3 mip_color = vec3 (mip / float (textureQueryLevels (view_depth_pyramid_tex) - 1), 1.0f - mip / float (textureQueryLevels (view_depth_pyramid_tex) - 1), 0.2f); + imageStore (ao_image, ao_pixel, vec4 (mip_color, 1.0f)); + return; + } + float screen_space_radius = effect_radius / pixel_size_x; + float visibility = clamp ((10.0f - screen_space_radius) / 100.0f, 0.0f, 1.0f) * 0.5f; + bool compute_bent_normal = (push_constants.flags & GTAO_FLAG_BENT_NORMALS) != 0u || push_constants.debug_mode == 4u; + vec3 bent_sum = vec3 (0.0f); + + // Use a denser 4x4 spatial tier for vkQuake's non-TAA default; retain + // XeGTAO's low, medium and ultra tiers at 1x2, 2x2 and 9x3 respectively. + int slice_count = push_constants.quality == 0u ? 1 : (push_constants.quality == 1u ? 2 : (push_constants.quality == 2u ? 4 : 9)); + int steps_per_slice = push_constants.quality < 2u ? 2 : (push_constants.quality == 2u ? 4 : 3); + for (int slice = 0; slice < slice_count; ++slice) + { + float phi = (float (slice) + noise_slice) * (PI / float (slice_count)); + float cos_phi = cos (phi); + float sin_phi = sin (phi); + vec3 direction_vector = vec3 (cos_phi, sin_phi, 0.0f); + vec3 ortho_direction = direction_vector - dot (direction_vector, view_vector) * view_vector; + vec3 axis_vector = normalize (cross (ortho_direction, view_vector)); + vec3 projected_normal = normal - axis_vector * dot (normal, axis_vector); + float projected_normal_length = max (length (projected_normal), 0.00001f); + float sign_normal = dot (ortho_direction, projected_normal) >= 0.0f ? 1.0f : -1.0f; + float cos_normal = clamp (dot (projected_normal, view_vector) / projected_normal_length, 0.0f, 1.0f); + float n = sign_normal * acos (cos_normal); + float low_horizon_cos_0 = cos (n + HALF_PI); + float low_horizon_cos_1 = cos (n - HALF_PI); + float horizon_cos_0 = low_horizon_cos_0; + float horizon_cos_1 = low_horizon_cos_1; + + vec2 radius_pixels = vec2 (cos_phi * effect_radius / pixel_size_x, -sin_phi * effect_radius / pixel_size_y); + float screen_radius = max (length (radius_pixels), 0.00001f); + float minimum_s = 1.3f / screen_radius; + + for (int step = 0; step < steps_per_slice; ++step) + { + float step_noise = fract (noise_sample + float (slice + step * steps_per_slice) * 0.618033989f); + float s = (float (step) + step_noise) / float (steps_per_slice); + s = s * s + minimum_s; + vec2 sample_offset_float = s * radius_pixels; + float sample_offset_length = length (sample_offset_float); + ivec2 sample_offset = ivec2 (round (sample_offset_float)); + if (all (equal (sample_offset, ivec2 (0)))) + continue; + + ivec2 sample_pixel_0 = pixel + sample_offset; + if (inside_viewport (sample_pixel_0)) + { + float sample_depth_0 = sample_horizon_depth (sample_pixel_0, sample_offset_length); + if (valid_surface_sample (sample_pixel_0, sample_depth_0)) + { + vec3 sample_delta_0 = reconstruct_position (sample_pixel_0, sample_depth_0) - center; + float sample_distance_0 = length (sample_delta_0); + if (sample_distance_0 > 0.00001f) + { + float falloff_base_0 = length (vec3 (sample_delta_0.xy, sample_delta_0.z * (1.0f + max (push_constants.thickness, 0.0f)))); + float weight_0 = clamp (falloff_base_0 * falloff_mul + falloff_add, 0.0f, 1.0f); + float sample_horizon_cos_0 = dot (sample_delta_0 / sample_distance_0, view_vector) - clamp (push_constants.bias, 0.0f, 0.5f); + sample_horizon_cos_0 = mix (low_horizon_cos_0, sample_horizon_cos_0, weight_0); + horizon_cos_0 = max (horizon_cos_0, sample_horizon_cos_0); + } + } + } + + ivec2 sample_pixel_1 = pixel - sample_offset; + if (inside_viewport (sample_pixel_1)) + { + float sample_depth_1 = sample_horizon_depth (sample_pixel_1, sample_offset_length); + if (valid_surface_sample (sample_pixel_1, sample_depth_1)) + { + vec3 sample_delta_1 = reconstruct_position (sample_pixel_1, sample_depth_1) - center; + float sample_distance_1 = length (sample_delta_1); + if (sample_distance_1 > 0.00001f) + { + float falloff_base_1 = length (vec3 (sample_delta_1.xy, sample_delta_1.z * (1.0f + max (push_constants.thickness, 0.0f)))); + float weight_1 = clamp (falloff_base_1 * falloff_mul + falloff_add, 0.0f, 1.0f); + float sample_horizon_cos_1 = dot (sample_delta_1 / sample_distance_1, view_vector) - clamp (push_constants.bias, 0.0f, 0.5f); + sample_horizon_cos_1 = mix (low_horizon_cos_1, sample_horizon_cos_1, weight_1); + horizon_cos_1 = max (horizon_cos_1, sample_horizon_cos_1); + } + } + } + } + + projected_normal_length = mix (projected_normal_length, 1.0f, 0.05f); + float h0 = -acos (clamp (horizon_cos_1, -1.0f, 1.0f)); + float h1 = acos (clamp (horizon_cos_0, -1.0f, 1.0f)); + float integrated_arc_0 = (cos_normal + 2.0f * h0 * sin (n) - cos (2.0f * h0 - n)) * 0.25f; + float integrated_arc_1 = (cos_normal + 2.0f * h1 * sin (n) - cos (2.0f * h1 - n)) * 0.25f; + visibility += projected_normal_length * (integrated_arc_0 + integrated_arc_1); + if (compute_bent_normal) + { + float t0 = (6.0f * sin (h0 - n) - sin (3.0f * h0 - n) + 6.0f * sin (h1 - n) - sin (3.0f * h1 - n) + + 16.0f * sin (n) - 3.0f * (sin (h0 + n) + sin (h1 + n))) / 12.0f; + float t1 = (-cos (3.0f * h0 - n) - cos (3.0f * h1 - n) + 8.0f * cos (n) - + 3.0f * (cos (h0 + n) + cos (h1 + n))) / 12.0f; + vec3 local_bent = vec3 (direction_vector.xy * t0, t1); + bent_sum += rotation_from_to (vec3 (0.0f, 0.0f, 1.0f), view_vector) * local_bent * projected_normal_length; + } + } + + visibility = pow (max (visibility / float (slice_count), 0.0f), 2.2f); + visibility = max (visibility, 0.03f); + if (push_constants.debug_mode == 4u) + { + imageStore (ao_image, ao_pixel, vec4 (safe_normalize (bent_sum) * 0.5f + 0.5f, 1.0f)); + return; + } + float packed_depth = clamp (log2 (1.0f + max (-center.z, 0.0f)) / 16.0f, 0.0f, 1.0f); + float temporal_weight = 0.0f; + if ((push_constants.flags & GTAO_FLAG_TEMPORAL) != 0u) + { + vec4 view_position = vec4 (center, 1.0f); + vec3 previous_clip = vec3 ( + dot (push_constants.current_to_previous_clip_x, view_position), + dot (push_constants.current_to_previous_clip_y, view_position), + dot (push_constants.current_to_previous_clip_w, view_position)); + if (previous_clip.z > 0.0001f) + { + vec2 previous_ndc = previous_clip.xy / previous_clip.z; + ivec2 previous_pixel = push_constants.viewport_origin + ivec2 ((previous_ndc * 0.5f + 0.5f) * vec2 (push_constants.viewport_size)); + if (inside_viewport (previous_pixel)) + { + ivec2 history_pixel = half_res ? previous_pixel / 2 : previous_pixel; + vec4 history = texelFetch (ao_history_tex, history_pixel, 0); + float previous_packed_depth = clamp (log2 (1.0f + previous_clip.z) / 16.0f, 0.0f, 1.0f); + if (abs (history.a - previous_packed_depth) < 0.004f) + { + float clamped_history = clamp (history.r, visibility - 0.15f, visibility + 0.15f); + temporal_weight = clamp (push_constants.temporal_blend, 0.0f, 0.97f); + visibility = mix (visibility, clamped_history, temporal_weight); + } + } + } + } + if (push_constants.debug_mode == 6u) + { + imageStore (ao_image, ao_pixel, vec4 (temporal_weight, 1.0f - temporal_weight, 0.0f, packed_depth)); + return; + } + vec2 packed_bent = compute_bent_normal ? oct_encode (safe_normalize (bent_sum)) : vec2 (0.0f, packed_depth); + imageStore (ao_image, ao_pixel, vec4 (visibility, packed_edges, packed_bent)); +} diff --git a/Shaders/gtao_denoise.comp b/Shaders/gtao_denoise.comp new file mode 100644 index 000000000..1364ad33e --- /dev/null +++ b/Shaders/gtao_denoise.comp @@ -0,0 +1,73 @@ +#version 460 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (set = 0, binding = 0) uniform sampler2D input_ao_tex; +layout (set = 0, binding = 1) uniform sampler2D view_depth_tex; +layout (set = 0, binding = 2, rgba8) uniform writeonly image2D output_ao_image; + +layout (push_constant) uniform DenoiseConsts +{ + uvec2 clamp_size; + uint sample_stride; + float center_weight; +} +push_constants; + +layout (local_size_x = 8, local_size_y = 8) in; + +vec4 unpack_edges (float packed_value) +{ + uint packed = uint (packed_value * 255.5f); + return vec4 (float ((packed >> 6) & 3u), float ((packed >> 4) & 3u), + float ((packed >> 2) & 3u), float (packed & 3u)) / 3.0f; +} + +void main () +{ + ivec2 pixel = ivec2 (gl_GlobalInvocationID.xy) * int (push_constants.sample_stride); + if (any (greaterThan (pixel, ivec2 (push_constants.clamp_size)))) + return; + + vec4 center = texelFetch (input_ao_tex, pixel, 0); + if (all (greaterThan (center, vec4 (0.999f)))) + { + imageStore (output_ao_image, pixel, center); + return; + } + + int stride = int (push_constants.sample_stride); + ivec2 limit = ivec2 (push_constants.clamp_size); + ivec2 p_l = clamp (pixel + ivec2 (-stride, 0), ivec2 (0), limit); + ivec2 p_r = clamp (pixel + ivec2 ( stride, 0), ivec2 (0), limit); + ivec2 p_t = clamp (pixel + ivec2 (0, -stride), ivec2 (0), limit); + ivec2 p_b = clamp (pixel + ivec2 (0, stride), ivec2 (0), limit); + ivec2 p_tl = clamp (pixel + ivec2 (-stride, -stride), ivec2 (0), limit); + ivec2 p_tr = clamp (pixel + ivec2 ( stride, -stride), ivec2 (0), limit); + ivec2 p_bl = clamp (pixel + ivec2 (-stride, stride), ivec2 (0), limit); + ivec2 p_br = clamp (pixel + ivec2 ( stride, stride), ivec2 (0), limit); + vec4 ao_l = texelFetch (input_ao_tex, p_l, 0); + vec4 ao_r = texelFetch (input_ao_tex, p_r, 0); + vec4 ao_t = texelFetch (input_ao_tex, p_t, 0); + vec4 ao_b = texelFetch (input_ao_tex, p_b, 0); + vec4 edges_l = unpack_edges (ao_l.g); + vec4 edges_r = unpack_edges (ao_r.g); + vec4 edges_t = unpack_edges (ao_t.g); + vec4 edges_b = unpack_edges (ao_b.g); + vec4 edges_c = unpack_edges (center.g); + edges_c *= vec4 (edges_l.y, edges_r.x, edges_t.w, edges_b.z); + float edginess = clamp (1.5f - dot (edges_c, vec4 (1.0f)), 0.0f, 1.5f) / 1.5f * 0.5f; + edges_c = clamp (edges_c + edginess, 0.0f, 1.0f); + const float diagonal_weight = 0.85f * 0.5f; + float weight_tl = diagonal_weight * (edges_c.x * edges_l.z + edges_c.z * edges_t.x); + float weight_tr = diagonal_weight * (edges_c.z * edges_t.y + edges_c.y * edges_r.z); + float weight_bl = diagonal_weight * (edges_c.w * edges_b.x + edges_c.x * edges_l.w); + float weight_br = diagonal_weight * (edges_c.y * edges_r.w + edges_c.w * edges_b.y); + float ao_sum = center.r * push_constants.center_weight + ao_l.r * edges_c.x + ao_r.r * edges_c.y + + ao_t.r * edges_c.z + ao_b.r * edges_c.w + texelFetch (input_ao_tex, p_tl, 0).r * weight_tl + + texelFetch (input_ao_tex, p_tr, 0).r * weight_tr + texelFetch (input_ao_tex, p_bl, 0).r * weight_bl + + texelFetch (input_ao_tex, p_br, 0).r * weight_br; + float weight_sum = push_constants.center_weight + dot (edges_c, vec4 (1.0f)) + weight_tl + weight_tr + weight_bl + weight_br; + center.r = ao_sum / max (weight_sum, 0.0001f); + imageStore (output_ao_image, pixel, center); +} diff --git a/Shaders/gtao_depth.comp b/Shaders/gtao_depth.comp new file mode 100644 index 000000000..8426ca4db --- /dev/null +++ b/Shaders/gtao_depth.comp @@ -0,0 +1,78 @@ +#version 460 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +#ifdef GTAO_DEPTH_DOWNSAMPLE +layout (set = 0, binding = 0) uniform sampler2D input_depth_tex; +layout (set = 0, binding = 2) uniform usampler2D input_stencil_tex; +#elif defined(GTAO_DEPTH_MSAA) +layout (set = 0, binding = 0) uniform sampler2DMS input_depth_tex; +layout (set = 0, binding = 2) uniform usampler2DMS input_stencil_tex; +#else +layout (set = 0, binding = 0) uniform sampler2D input_depth_tex; +layout (set = 0, binding = 2) uniform usampler2D input_stencil_tex; +#endif +layout (set = 0, binding = 1, r32f) uniform writeonly image2D output_depth_image; + +layout (push_constant) uniform DepthConsts +{ + uvec2 output_size; + vec2 projection; + float effect_radius; + float falloff_range; +} +push_constants; + +layout (local_size_x = 8, local_size_y = 8) in; + +const uint STENCIL_EXCLUDED = 0x3u; + +float depth_mip_filter (vec4 depths) +{ + float max_depth = max (max (depths.x, depths.y), max (depths.z, depths.w)); + if (max_depth <= 0.0f) + return 0.0f; + float effect_radius = 0.75f * max (push_constants.effect_radius, 0.0001f); + float falloff_range = max (push_constants.falloff_range * effect_radius, 0.0001f); + float falloff_from = effect_radius * (1.0f - push_constants.falloff_range); + float falloff_mul = -1.0f / falloff_range; + float falloff_add = falloff_from / falloff_range + 1.0f; + vec4 weights = clamp ((max_depth - depths) * falloff_mul + falloff_add, 0.0f, 1.0f); + weights *= vec4 (greaterThan (depths, vec4 (0.0f))); + return dot (weights, depths) / max (dot (weights, vec4 (1.0f)), 0.0001f); +} + +void main () +{ + ivec2 pixel = ivec2 (gl_GlobalInvocationID.xy); + if (any (greaterThanEqual (pixel, ivec2 (push_constants.output_size)))) + return; + +#ifdef GTAO_DEPTH_DOWNSAMPLE + ivec2 input_size = textureSize (input_depth_tex, 0); + ivec2 input_pixel = pixel * 2; + vec4 depths; + for (int y = 0; y < 2; ++y) + { + for (int x = 0; x < 2; ++x) + { + depths[y * 2 + x] = texelFetch (input_depth_tex, min (input_pixel + ivec2 (x, y), input_size - 1), 0).r; + } + } + imageStore (output_depth_image, pixel, vec4 (depth_mip_filter (depths))); +#else + float device_depth = 0.0f; +#ifdef GTAO_DEPTH_MSAA + for (int sample_index = 0; sample_index < textureSamples (input_depth_tex); ++sample_index) + { + if ((texelFetch (input_stencil_tex, pixel, sample_index).r & STENCIL_EXCLUDED) == 0u) + device_depth = max (device_depth, texelFetch (input_depth_tex, pixel, sample_index).r); + } +#else + if ((texelFetch (input_stencil_tex, pixel, 0).r & STENCIL_EXCLUDED) == 0u) + device_depth = texelFetch (input_depth_tex, pixel, 0).r; +#endif + float view_depth = device_depth > 0.00001f ? push_constants.projection.y / (device_depth + push_constants.projection.x) : 0.0f; + imageStore (output_depth_image, pixel, vec4 (max (view_depth, 0.0f))); +#endif +} diff --git a/Shaders/screen_effects.inc b/Shaders/screen_effects.inc index b987853cb..5a2ec5e96 100644 --- a/Shaders/screen_effects.inc +++ b/Shaders/screen_effects.inc @@ -9,6 +9,9 @@ struct OctreeNode layout (set = 0, binding = 0) uniform sampler2D input_tex; layout (set = 0, binding = 1) uniform sampler2D blue_noise_tex; +layout (set = 0, binding = 5) uniform sampler2D gtao_tex; +layout (set = 0, binding = 6) uniform sampler2D gtao_view_depth_tex; +layout (set = 0, binding = 7) uniform sampler2D gtao_denoise_tex; layout (set = 0, binding = 3) uniform samplerBuffer palette_colors; layout (set = 0, binding = 4) uniform PaletteOctree { @@ -27,6 +30,11 @@ layout (push_constant) uniform PushConsts float poly_blend_g; float poly_blend_b; float poly_blend_a; + float gtao_strength; + uint gtao_debug_mode; + uint gtao_denoise; + float gtao_multibounce; + uint gtao_halfres; } push_constants; @@ -57,6 +65,88 @@ float blue_noise (ivec2 u) return texelFetch (blue_noise_tex, ivec2 (uint (u.x) % 64, uint (u.y) % 64), 0).r; } +vec4 gtao_fetch (ivec2 pixel) +{ + return (push_constants.gtao_denoise & 1u) != 0u ? texelFetch (gtao_denoise_tex, pixel, 0) : texelFetch (gtao_tex, pixel, 0); +} + +float gtao_depth_similarity (float center_depth, float sample_depth) +{ + if (center_depth <= 0.0f || sample_depth <= 0.0f) + return center_depth == sample_depth ? 1.0f : 0.0f; + return clamp (1.25f - abs (sample_depth - center_depth) / max (center_depth * 0.011f, 0.00001f), 0.0f, 1.0f); +} + +float gtao_relative_depth_difference (float center_depth, float sample_depth) +{ + if (center_depth <= 0.0f || sample_depth <= 0.0f) + return center_depth == sample_depth ? 0.0f : 1e30f; + return abs (sample_depth - center_depth) / max (center_depth, 0.00001f); +} + +vec4 gtao_resolve (ivec2 target_pixel, vec2 target_uv, bool warped) +{ + if (warped) + target_pixel = clamp (ivec2 (target_uv / push_constants.screen_size_rcp), ivec2 (0), ivec2 (push_constants.clamp_size)); + if (push_constants.gtao_halfres == 0u) + return gtao_fetch (target_pixel); + + // Half-resolution AO is stored contiguously in the upper-left working + // region. AO texel q represents source pixel 2*q. + ivec2 source_max = ivec2 (push_constants.clamp_size); + ivec2 working_max = source_max / 2; + ivec2 base = clamp (target_pixel / 2, ivec2 (0), working_max); + if (all (equal (target_pixel, base * 2))) + return gtao_fetch (base); + ivec2 candidates[4] = ivec2[4] ( + base, min (base + ivec2 (1, 0), working_max), + min (base + ivec2 (0, 1), working_max), min (base + ivec2 (1, 1), working_max)); + float target_depth = texelFetch (gtao_view_depth_tex, target_pixel, 0).r; + vec2 fraction = clamp (vec2 (target_pixel - base * 2) * 0.5f, 0.0f, 1.0f); + vec4 spatial_weights = vec4 ((1.0f - fraction.x) * (1.0f - fraction.y), fraction.x * (1.0f - fraction.y), + (1.0f - fraction.x) * fraction.y, fraction.x * fraction.y); + vec4 closest_result = gtao_fetch (base); + vec4 best_result = closest_result; + float visibility_sum = 0.0f; + float weight_sum = 0.0f; + float best_weight = 0.0f; + float closest_depth_difference = 1e30f; + for (int i = 0; i < 4; ++i) + { + if (spatial_weights[i] <= 0.0f) + continue; + ivec2 source_pixel = min (candidates[i] * 2, source_max); + float sample_depth = texelFetch (gtao_view_depth_tex, source_pixel, 0).r; + float depth_difference = gtao_relative_depth_difference (target_depth, sample_depth); + vec4 candidate = gtao_fetch (candidates[i]); + if (depth_difference < closest_depth_difference) + { + closest_depth_difference = depth_difference; + closest_result = candidate; + } + float weight = spatial_weights[i] * gtao_depth_similarity (target_depth, sample_depth); + visibility_sum += candidate.r * weight; + weight_sum += weight; + if (weight > best_weight) + { + best_weight = weight; + best_result = candidate; + } + } + vec4 result = weight_sum > 0.0001f ? best_result : closest_result; + if (weight_sum > 0.0001f) + result.r = visibility_sum / weight_sum; + return result; +} + +vec3 gtao_multibounce_visibility (float visibility, vec3 albedo) +{ + vec3 a = 2.0404f * albedo - 0.3324f; + vec3 b = -4.7951f * albedo + 0.6417f; + vec3 c = 2.7552f * albedo + 0.6903f; + return max (vec3 (visibility), ((visibility * a + b) * visibility + c) * visibility); +} + #define SCREEN_EFFECT_FLAG_SCALE_MASK 0x3 #define SCREEN_EFFECT_FLAG_SCALE_2X 0x1 #define SCREEN_EFFECT_FLAG_SCALE_4X 0x2 @@ -64,6 +154,7 @@ float blue_noise (ivec2 u) #define SCREEN_EFFECT_FLAG_WATER_WARP 0x4 #define SCREEN_EFFECT_FLAG_PALETTIZE 0x8 #define SCREEN_EFFECT_FLAG_MENU 0x10 +#define SCREEN_EFFECT_FLAG_GTAO 0x20 #if defined(SCALING) // Vulkan guarantees 16384 bytes of shared memory, so host doesn't need to check @@ -128,6 +219,7 @@ void main () #endif vec4 color = vec4 (0.0f, 0.0f, 0.0f, 0.0f); + vec2 effect_texcoord = (vec2 (pos_x, pos_y) + 0.5f) * push_constants.screen_size_rcp; [[branch]] if ((push_constants.flags & SCREEN_EFFECT_FLAG_WATER_WARP) != 0) { @@ -142,11 +234,31 @@ void main () const float tex_x = (pos_x_norm + (sin (pos_y_norm * cycle_x + push_constants.time) * amp_x)) * (1.0f - amp_x * 2.0f) + amp_x; const float tex_y = (pos_y_norm + (sin (pos_x_norm * cycle_y + push_constants.time) * amp_y)) * (1.0f - amp_y * 2.0f) + amp_y; - color = texture (input_tex, vec2 (tex_x, tex_y)); + effect_texcoord = vec2 (tex_x, tex_y); + color = texture (input_tex, effect_texcoord); } else color = texelFetch (input_tex, ivec2 (min (push_constants.clamp_size.x, pos_x), min (push_constants.clamp_size.y, pos_y)), 0); + [[branch]] if ((push_constants.flags & SCREEN_EFFECT_FLAG_GTAO) != 0) + { + const ivec2 ao_pixel = ivec2 (min (push_constants.clamp_size.x, pos_x), min (push_constants.clamp_size.y, pos_y)); + const bool gtao_warped = (push_constants.flags & SCREEN_EFFECT_FLAG_WATER_WARP) != 0; + const vec4 ao_sample = gtao_resolve (ao_pixel, effect_texcoord, gtao_warped); + if (push_constants.gtao_debug_mode == 1u || push_constants.gtao_debug_mode == 7u) + color.rgb = vec3 (clamp (ao_sample.r, 0.0f, 1.0f)); + else if (push_constants.gtao_debug_mode > 1u) + color.rgb = ao_sample.rgb; + else + { + float ao_visibility = clamp (ao_sample.r, 0.0f, 1.0f); + float visibility = clamp (1.0f - ((1.0f - ao_visibility) * push_constants.gtao_strength), 0.0f, 1.0f); + vec3 single_bounce = color.rgb * visibility; + vec3 multi_bounce = color.rgb * gtao_multibounce_visibility (visibility, color.rgb); + color.rgb = mix (single_bounce, multi_bounce, clamp (push_constants.gtao_multibounce, 0.0f, 1.0f)); + } + } + [[branch]] if ((push_constants.flags & SCREEN_EFFECT_FLAG_PALETTIZE) != 0) { uvec3 search_color = uvec3 (color.rgb * 255.0f); diff --git a/Shaders/shaders.h b/Shaders/shaders.h index b995b78b4..e60057451 100644 --- a/Shaders/shaders.h +++ b/Shaders/shaders.h @@ -72,6 +72,12 @@ DECLARE_SHADER_SPV (screen_effects_8bit_scale_sops_comp); DECLARE_SHADER_SPV (screen_effects_10bit_comp); DECLARE_SHADER_SPV (screen_effects_10bit_scale_comp); DECLARE_SHADER_SPV (screen_effects_10bit_scale_sops_comp); +DECLARE_SHADER_SPV (gtao_comp); +DECLARE_SHADER_SPV (gtao_msaa_comp); +DECLARE_SHADER_SPV (gtao_depth_comp); +DECLARE_SHADER_SPV (gtao_depth_msaa_comp); +DECLARE_SHADER_SPV (gtao_depth_downsample_comp); +DECLARE_SHADER_SPV (gtao_denoise_comp); DECLARE_SHADER_SPV (cs_tex_warp_comp); DECLARE_SHADER_SPV (indirect_comp); DECLARE_SHADER_SPV (indirect_clear_comp); diff --git a/Windows/VisualStudio/embedded.vcxproj b/Windows/VisualStudio/embedded.vcxproj index 1d41f6406..432efade2 100644 --- a/Windows/VisualStudio/embedded.vcxproj +++ b/Windows/VisualStudio/embedded.vcxproj @@ -243,6 +243,51 @@ $(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(Solution $(SolutionDir)..\..\Shaders\Compiled\Debug\screen_effects_8bit.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\screen_effects_8bit_scale.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\screen_effects_8bit_scale_sops.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\screen_effects_10bit.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\screen_effects_10bit_scale.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\screen_effects_10bit_scale_sops.comp.c $(SolutionDir)..\..\Shaders\Compiled\Release\screen_effects_8bit.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\screen_effects_8bit_scale.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\screen_effects_8bit_scale_sops.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\screen_effects_10bit.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\screen_effects_10bit_scale.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\screen_effects_10bit_scale_sops.comp.c + + Document + $(VULKAN_SDK)\bin\glslangValidator.exe -g -V "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao.comp.spv" gtao.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao.comp.c" +$(VULKAN_SDK)\bin\glslangValidator.exe -g -V -DGTAO_MSAA=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_msaa.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_msaa.comp.spv" gtao_msaa.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_msaa.comp.c" + $(VULKAN_SDK)\bin\glslangValidator.exe -V "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.spv" +$(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.spv" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.spv" gtao.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.c" +$(VULKAN_SDK)\bin\glslangValidator.exe -V -DGTAO_MSAA=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_msaa.comp.spv" +$(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_msaa.comp.spv" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_msaa.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_msaa.comp.spv" gtao_msaa.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_msaa.comp.c" + $(SolutionDir)..\..\Shaders\Compiled\Debug\gtao.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_msaa.comp.c + $(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_msaa.comp.c + + + Document + $(VULKAN_SDK)\bin\glslangValidator.exe -g -V "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth.comp.spv" gtao_depth.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth.comp.c" +$(VULKAN_SDK)\bin\glslangValidator.exe -g -V -DGTAO_DEPTH_MSAA=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_msaa.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_msaa.comp.spv" gtao_depth_msaa.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_msaa.comp.c" +$(VULKAN_SDK)\bin\glslangValidator.exe -g -V -DGTAO_DEPTH_DOWNSAMPLE=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_downsample.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_downsample.comp.spv" gtao_depth_downsample.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_downsample.comp.c" + $(VULKAN_SDK)\bin\glslangValidator.exe -V "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth.comp.spv" +$(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth.comp.spv" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth.comp.spv" gtao_depth.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth.comp.c" +$(VULKAN_SDK)\bin\glslangValidator.exe -V -DGTAO_DEPTH_MSAA=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa.comp.spv" +$(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa.comp.spv" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa.comp.spv" gtao_depth_msaa.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa.comp.c" +$(VULKAN_SDK)\bin\glslangValidator.exe -V -DGTAO_DEPTH_DOWNSAMPLE=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_downsample.comp.spv" +$(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_downsample.comp.spv" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_downsample.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_downsample.comp.spv" gtao_depth_downsample.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_downsample.comp.c" + $(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_msaa.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_downsample.comp.c + $(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_downsample.comp.c + + + Document + $(VULKAN_SDK)\bin\glslangValidator.exe -g -V "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_denoise.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_denoise.comp.spv" gtao_denoise.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_denoise.comp.c" + $(VULKAN_SDK)\bin\glslangValidator.exe -V "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_denoise.comp.spv" +$(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_denoise.comp.spv" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_denoise.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_denoise.comp.spv" gtao_denoise.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_denoise.comp.c" + $(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_denoise.comp.c + $(SolutionDir)..\..\Shaders\Compiled\Release\gtao_denoise.comp.c + Document $(SolutionDir)..\..\Shaders\update_lightmap.inc;%(AdditionalInputs) diff --git a/Windows/VisualStudio/embedded.vcxproj.filters b/Windows/VisualStudio/embedded.vcxproj.filters index 4316f6f47..dcda30ff4 100644 --- a/Windows/VisualStudio/embedded.vcxproj.filters +++ b/Windows/VisualStudio/embedded.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -28,6 +28,15 @@ Shaders\Screen Effects + + Shaders + + + Shaders + + + Shaders + Shaders\Update Lightmap diff --git a/Windows/VisualStudio/vkquake.vcxproj b/Windows/VisualStudio/vkquake.vcxproj index 941e057de..728d446db 100644 --- a/Windows/VisualStudio/vkquake.vcxproj +++ b/Windows/VisualStudio/vkquake.vcxproj @@ -1,4 +1,4 @@ - + @@ -477,6 +477,36 @@ copy "$(SolutionDir)\..\SDL3\lib64\*.dll" "$(TargetDir)" NotUsing NotUsing + + true + NotUsing + NotUsing + + + true + NotUsing + NotUsing + + + true + NotUsing + NotUsing + + + true + NotUsing + NotUsing + + + true + NotUsing + NotUsing + + + true + NotUsing + NotUsing + true NotUsing @@ -772,6 +802,36 @@ copy "$(SolutionDir)\..\SDL3\lib64\*.dll" "$(TargetDir)" NotUsing NotUsing + + true + NotUsing + NotUsing + + + true + NotUsing + NotUsing + + + true + NotUsing + NotUsing + + + true + NotUsing + NotUsing + + + true + NotUsing + NotUsing + + + true + NotUsing + NotUsing + true NotUsing @@ -933,4 +993,4 @@ copy "$(SolutionDir)\..\SDL3\lib64\*.dll" "$(TargetDir)" - \ No newline at end of file + diff --git a/Windows/VisualStudio/vkquake.vcxproj.filters b/Windows/VisualStudio/vkquake.vcxproj.filters index 3441833cd..85b973e18 100644 --- a/Windows/VisualStudio/vkquake.vcxproj.filters +++ b/Windows/VisualStudio/vkquake.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -397,6 +397,24 @@ Shaders\Debug\Screen Effects + + Shaders\Debug + + + Shaders\Debug + + + Shaders\Debug + + + Shaders\Debug + + + Shaders\Debug + + + Shaders\Debug + Shaders\Debug @@ -495,6 +513,24 @@ Shaders\Release\Screen Effects + + + Shaders\Release + + + Shaders\Release + + + Shaders\Release + + + Shaders\Release + + + Shaders\Release + + + Shaders\Release Shaders\Release diff --git a/meson.build b/meson.build index e32e66533..a8c8a2126 100644 --- a/meson.build +++ b/meson.build @@ -150,6 +150,12 @@ shader_variants = [ ['Shaders/screen_effects.comp', 'screen_effects_10bit.comp', ['-DUSE_10BIT=1']], ['Shaders/screen_effects.comp', 'screen_effects_10bit_scale.comp', ['-DUSE_10BIT=1', '-DSCALING=1']], ['Shaders/screen_effects.comp', 'screen_effects_10bit_scale_sops.comp', ['--target-env', 'vulkan1.1', '-DUSE_10BIT=1', '-DSCALING=1', '-DUSE_SUBGROUP_OPS=1']], + ['Shaders/gtao.comp', 'gtao.comp', []], + ['Shaders/gtao.comp', 'gtao_msaa.comp', ['-DGTAO_MSAA=1']], + ['Shaders/gtao_depth.comp', 'gtao_depth.comp', []], + ['Shaders/gtao_depth.comp', 'gtao_depth_msaa.comp', ['-DGTAO_DEPTH_MSAA=1']], + ['Shaders/gtao_depth.comp', 'gtao_depth_downsample.comp', ['-DGTAO_DEPTH_DOWNSAMPLE=1']], + ['Shaders/gtao_denoise.comp', 'gtao_denoise.comp', []], ['Shaders/update_lightmap.comp', 'update_lightmap_8bit.comp', []], ['Shaders/update_lightmap.comp', 'update_lightmap_8bit_rt.comp', ['-DRAY_QUERIES=1']], ['Shaders/update_lightmap.comp', 'update_lightmap_10bit.comp', ['-DUSE_10BIT=1']], From 071e58689190e10ea33bafb890f72309b9a954ab Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 16:01:15 -0500 Subject: [PATCH 02/26] Add liquid-aware GTAO suppression --- Quake/gl_rmain.c | 4 + Quake/gl_rmisc.c | 172 +++++++++++++++++++++++-------------- Quake/gl_vidsdl.c | 89 +++++++++++++++++-- Quake/glquake.h | 14 ++- Quake/r_brush.c | 10 +++ Quake/r_world.c | 28 ++++-- Shaders/gtao_depth.comp | 13 ++- Shaders/screen_effects.inc | 50 ++++++++++- 8 files changed, 298 insertions(+), 82 deletions(-) diff --git a/Quake/gl_rmain.c b/Quake/gl_rmain.c index 0b1378830..723a0443e 100644 --- a/Quake/gl_rmain.c +++ b/Quake/gl_rmain.c @@ -134,6 +134,10 @@ cvar_t r_gtao_multibounce = {"r_gtao_multibounce", "1", CVAR_ARCHIVE}; cvar_t r_gtao_temporal = {"r_gtao_temporal", "0", CVAR_ARCHIVE}; cvar_t r_gtao_temporal_blend = {"r_gtao_temporal_blend", "0.85", CVAR_ARCHIVE}; cvar_t r_gtao_halfres = {"r_gtao_halfres", "1", CVAR_ARCHIVE}; +cvar_t r_gtao_liquid_water = {"r_gtao_liquid_water", "1", CVAR_ARCHIVE}; +cvar_t r_gtao_liquid_slime = {"r_gtao_liquid_slime", "1", CVAR_ARCHIVE}; +cvar_t r_gtao_liquid_lava = {"r_gtao_liquid_lava", "1", CVAR_ARCHIVE}; +cvar_t r_gtao_liquid_tele = {"r_gtao_liquid_tele", "1", CVAR_ARCHIVE}; cvar_t r_gpulightmapupdate = {"r_gpulightmapupdate", "1", CVAR_NONE}; cvar_t r_rtshadows = {"r_rtshadows", "2", CVAR_ARCHIVE}; diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index 2b2f2f4d0..d2a0b59d4 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -79,6 +79,10 @@ extern cvar_t r_gtao_multibounce; extern cvar_t r_gtao_temporal; extern cvar_t r_gtao_temporal_blend; extern cvar_t r_gtao_halfres; +extern cvar_t r_gtao_liquid_water; +extern cvar_t r_gtao_liquid_slime; +extern cvar_t r_gtao_liquid_lava; +extern cvar_t r_gtao_liquid_tele; #if defined(USE_SIMD) extern cvar_t r_simd; @@ -1455,7 +1459,7 @@ void R_CreateDescriptorSetLayouts () } { - ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, screen_effects_layout_bindings, 8); + ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, screen_effects_layout_bindings, 9); screen_effects_layout_bindings[0].binding = 0; screen_effects_layout_bindings[0].descriptorCount = 1; screen_effects_layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; @@ -1488,12 +1492,16 @@ void R_CreateDescriptorSetLayouts () screen_effects_layout_bindings[7].descriptorCount = 1; screen_effects_layout_bindings[7].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; screen_effects_layout_bindings[7].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + screen_effects_layout_bindings[8].binding = 8; + screen_effects_layout_bindings[8].descriptorCount = 1; + screen_effects_layout_bindings[8].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + screen_effects_layout_bindings[8].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; descriptor_set_layout_create_info.bindingCount = countof (screen_effects_layout_bindings); descriptor_set_layout_create_info.pBindings = screen_effects_layout_bindings; memset (&vulkan_globals.screen_effects_set_layout, 0, sizeof (vulkan_globals.screen_effects_set_layout)); - vulkan_globals.screen_effects_set_layout.num_combined_image_samplers = 5; + vulkan_globals.screen_effects_set_layout.num_combined_image_samplers = 6; vulkan_globals.screen_effects_set_layout.num_storage_images = 1; err = vkCreateDescriptorSetLayout (vulkan_globals.device, &descriptor_set_layout_create_info, NULL, &vulkan_globals.screen_effects_set_layout.handle); @@ -1543,7 +1551,7 @@ void R_CreateDescriptorSetLayouts () } { - ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, bindings, 3); + ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, bindings, 4); bindings[0].binding = 0; bindings[0].descriptorCount = 1; bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; @@ -1556,11 +1564,15 @@ void R_CreateDescriptorSetLayouts () bindings[2].descriptorCount = 1; bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[2].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bindings[3].binding = 3; + bindings[3].descriptorCount = 1; + bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + bindings[3].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; descriptor_set_layout_create_info.bindingCount = countof (bindings); descriptor_set_layout_create_info.pBindings = bindings; memset (&vulkan_globals.gtao_depth_set_layout, 0, sizeof (vulkan_globals.gtao_depth_set_layout)); vulkan_globals.gtao_depth_set_layout.num_combined_image_samplers = 2; - vulkan_globals.gtao_depth_set_layout.num_storage_images = 1; + vulkan_globals.gtao_depth_set_layout.num_storage_images = 2; err = vkCreateDescriptorSetLayout (vulkan_globals.device, &descriptor_set_layout_create_info, NULL, &vulkan_globals.gtao_depth_set_layout.handle); if (err != VK_SUCCESS) Sys_Error ("vkCreateDescriptorSetLayout failed with code %i", (int)err); @@ -2042,7 +2054,7 @@ void R_CreatePipelineLayouts () ZEROED_STRUCT (VkPushConstantRange, push_constant_range); push_constant_range.offset = 0; - push_constant_range.size = 6 * sizeof (uint32_t) + 10 * sizeof (float); + push_constant_range.size = 6 * sizeof (uint32_t) + 14 * sizeof (float); push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; ZEROED_STRUCT (VkPipelineLayoutCreateInfo, pipeline_layout_create_info); @@ -2504,7 +2516,7 @@ typedef struct pipeline_create_infos_s { VkPipelineShaderStageCreateInfo shader_stages[2]; VkPipelineDynamicStateCreateInfo dynamic_state; - VkDynamicState dynamic_states[3]; + VkDynamicState dynamic_states[4]; VkPipelineVertexInputStateCreateInfo vertex_input_state; VkPipelineInputAssemblyStateCreateInfo input_assembly_state; VkPipelineViewportStateCreateInfo viewport_state; @@ -3492,6 +3504,20 @@ static void R_CreateShowTrisPipelines () } } +static void R_SetLiquidStencilState (pipeline_create_infos_t *infos) +{ + infos->depth_stencil_state.stencilTestEnable = VK_TRUE; + infos->depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS; + infos->depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP; + infos->depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP; + infos->depth_stencil_state.front.passOp = VK_STENCIL_OP_REPLACE; + infos->depth_stencil_state.front.compareMask = STENCIL_MASK_LIQUID; + infos->depth_stencil_state.front.writeMask = STENCIL_MASK_LIQUID; + infos->depth_stencil_state.front.reference = STENCIL_MASK_WATER; + infos->depth_stencil_state.back = infos->depth_stencil_state.front; + infos->dynamic_states[infos->dynamic_state.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE; +} + /* =============== R_CreateWorldPipelines @@ -3545,68 +3571,80 @@ static void R_CreateWorldPipelines () base.shader_stages[1].pSpecializationInfo = &specialization_info; pipeline_create_infos_t infos; - for (int alpha_blend = 0; alpha_blend < 2; ++alpha_blend) + for (int liquid = 0; liquid < 2; ++liquid) { - for (int alpha_test = 0; alpha_test < 2; ++alpha_test) + for (int alpha_blend = 0; alpha_blend < 2; ++alpha_blend) { - for (int fullbright_enabled = 0; fullbright_enabled < 2; ++fullbright_enabled) + for (int alpha_test = 0; alpha_test < 2; ++alpha_test) { - for (int quantize_lm = 0; quantize_lm < 2; ++quantize_lm) + for (int fullbright_enabled = 0; fullbright_enabled < 2; ++fullbright_enabled) { - const int pipeline_index = fullbright_enabled + (alpha_test * 2) + (alpha_blend * 4) + (quantize_lm * 8); - - specialization_data[0] = fullbright_enabled; - specialization_data[1] = alpha_test; - specialization_data[2] = alpha_blend; - specialization_data[3] = quantize_lm; - - for (int variant = 0; variant < MAIN_RENDER_PASS_VARIANT_COUNT; ++variant) - { - R_CopyPipelineCreateInfos (&infos, &base); - infos.graphics_pipeline.renderPass = vulkan_globals.main_render_pass[variant][MAIN_RENDER_PASS_STENCIL_CLEAR]; - infos.shader_stages[1].module = world_frag_module; - infos.blend_attachment_states[0].blendEnable = alpha_blend ? VK_TRUE : VK_FALSE; - infos.depth_stencil_state.depthWriteEnable = alpha_blend ? VK_FALSE : VK_TRUE; - R_CreateGraphicsPipeline ( - &vulkan_globals.world_pipelines[variant][pipeline_index], &infos, vulkan_globals.world_pipeline_layout, - va (variant ? "world_main_oit %d" : "world %d", pipeline_index)); - } - - if (alpha_blend) + for (int quantize_lm = 0; quantize_lm < 2; ++quantize_lm) { - R_CopyPipelineCreateInfos (&infos, &base); - infos.graphics_pipeline.renderPass = vulkan_globals.main_render_pass[MAIN_RENDER_PASS_OIT][MAIN_RENDER_PASS_STENCIL_CLEAR]; - infos.graphics_pipeline.subpass = 1; - infos.color_blend_state.attachmentCount = WBOIT_COLOR_ATTACHMENT_COUNT; - infos.shader_stages[1].module = world_oit_frag_module; - infos.depth_stencil_state.depthWriteEnable = VK_FALSE; - R_SetWBOITBlend (infos.blend_attachment_states); - R_CreateGraphicsPipeline ( - &vulkan_globals.world_wboit_pipelines[pipeline_index], &infos, vulkan_globals.world_pipeline_layout, - va ("world_wboit %d", pipeline_index)); - - R_CopyPipelineCreateInfos (&infos, &base); - infos.graphics_pipeline.renderPass = vulkan_globals.main_render_pass[MAIN_RENDER_PASS_MBOIT][MAIN_RENDER_PASS_STENCIL_CLEAR]; - infos.graphics_pipeline.subpass = 1; - infos.color_blend_state.attachmentCount = MBOIT_MOMENT_COLOR_ATTACHMENT_COUNT; - infos.shader_stages[1].module = world_mboit_moment_frag_module; - infos.depth_stencil_state.depthWriteEnable = VK_FALSE; - R_SetMBOITMomentBlend (infos.blend_attachment_states); - R_CreateGraphicsPipeline ( - &vulkan_globals.world_mboit_moment_pipelines[pipeline_index], &infos, vulkan_globals.world_pipeline_layout, - va ("world_mboit_moment %d", pipeline_index)); - - R_CopyPipelineCreateInfos (&infos, &base); - infos.graphics_pipeline.renderPass = vulkan_globals.main_render_pass[MAIN_RENDER_PASS_MBOIT][MAIN_RENDER_PASS_STENCIL_CLEAR]; - infos.graphics_pipeline.subpass = 2; - infos.color_blend_state.attachmentCount = MBOIT_COMPOSITE_COLOR_ATTACHMENT_COUNT; - infos.shader_stages[1].module = - (vulkan_globals.sample_count == VK_SAMPLE_COUNT_1_BIT) ? world_mboit_composite_frag_module : world_mboit_composite_msaa_frag_module; - infos.depth_stencil_state.depthWriteEnable = VK_FALSE; - R_SetMBOITCompositeBlend (infos.blend_attachment_states); - R_CreateGraphicsPipeline ( - &vulkan_globals.world_mboit_composite_pipelines[pipeline_index], &infos, vulkan_globals.world_pipeline_layout, - va ("world_mboit_composite %d", pipeline_index)); + const int pipeline_index = + fullbright_enabled + (alpha_test * 2) + (alpha_blend * 4) + (quantize_lm * 8) + (liquid ? WORLD_PIPELINE_LIQUID_BIT : 0); + + specialization_data[0] = fullbright_enabled; + specialization_data[1] = alpha_test; + specialization_data[2] = alpha_blend; + specialization_data[3] = quantize_lm; + + for (int variant = 0; variant < MAIN_RENDER_PASS_VARIANT_COUNT; ++variant) + { + R_CopyPipelineCreateInfos (&infos, &base); + if (liquid) + R_SetLiquidStencilState (&infos); + infos.graphics_pipeline.renderPass = vulkan_globals.main_render_pass[variant][MAIN_RENDER_PASS_STENCIL_CLEAR]; + infos.shader_stages[1].module = world_frag_module; + infos.blend_attachment_states[0].blendEnable = alpha_blend ? VK_TRUE : VK_FALSE; + infos.depth_stencil_state.depthWriteEnable = alpha_blend ? VK_FALSE : VK_TRUE; + R_CreateGraphicsPipeline ( + &vulkan_globals.world_pipelines[variant][pipeline_index], &infos, vulkan_globals.world_pipeline_layout, + va (variant ? "world_main_oit %d" : "world %d", pipeline_index)); + } + + if (alpha_blend) + { + R_CopyPipelineCreateInfos (&infos, &base); + if (liquid) + R_SetLiquidStencilState (&infos); + infos.graphics_pipeline.renderPass = vulkan_globals.main_render_pass[MAIN_RENDER_PASS_OIT][MAIN_RENDER_PASS_STENCIL_CLEAR]; + infos.graphics_pipeline.subpass = 1; + infos.color_blend_state.attachmentCount = WBOIT_COLOR_ATTACHMENT_COUNT; + infos.shader_stages[1].module = world_oit_frag_module; + infos.depth_stencil_state.depthWriteEnable = VK_FALSE; + R_SetWBOITBlend (infos.blend_attachment_states); + R_CreateGraphicsPipeline ( + &vulkan_globals.world_wboit_pipelines[pipeline_index], &infos, vulkan_globals.world_pipeline_layout, + va ("world_wboit %d", pipeline_index)); + + R_CopyPipelineCreateInfos (&infos, &base); + if (liquid) + R_SetLiquidStencilState (&infos); + infos.graphics_pipeline.renderPass = vulkan_globals.main_render_pass[MAIN_RENDER_PASS_MBOIT][MAIN_RENDER_PASS_STENCIL_CLEAR]; + infos.graphics_pipeline.subpass = 1; + infos.color_blend_state.attachmentCount = MBOIT_MOMENT_COLOR_ATTACHMENT_COUNT; + infos.shader_stages[1].module = world_mboit_moment_frag_module; + infos.depth_stencil_state.depthWriteEnable = VK_FALSE; + R_SetMBOITMomentBlend (infos.blend_attachment_states); + R_CreateGraphicsPipeline ( + &vulkan_globals.world_mboit_moment_pipelines[pipeline_index], &infos, vulkan_globals.world_pipeline_layout, + va ("world_mboit_moment %d", pipeline_index)); + + R_CopyPipelineCreateInfos (&infos, &base); + if (liquid) + R_SetLiquidStencilState (&infos); + infos.graphics_pipeline.renderPass = vulkan_globals.main_render_pass[MAIN_RENDER_PASS_MBOIT][MAIN_RENDER_PASS_STENCIL_CLEAR]; + infos.graphics_pipeline.subpass = 2; + infos.color_blend_state.attachmentCount = MBOIT_COMPOSITE_COLOR_ATTACHMENT_COUNT; + infos.shader_stages[1].module = (vulkan_globals.sample_count == VK_SAMPLE_COUNT_1_BIT) ? world_mboit_composite_frag_module + : world_mboit_composite_msaa_frag_module; + infos.depth_stencil_state.depthWriteEnable = VK_FALSE; + R_SetMBOITCompositeBlend (infos.blend_attachment_states); + R_CreateGraphicsPipeline ( + &vulkan_globals.world_mboit_composite_pipelines[pipeline_index], &infos, vulkan_globals.world_pipeline_layout, + va ("world_mboit_composite %d", pipeline_index)); + } } } } @@ -4399,6 +4437,10 @@ static void R_GTAODefaults_f (void) &r_gtao_temporal, &r_gtao_temporal_blend, &r_gtao_halfres, + &r_gtao_liquid_water, + &r_gtao_liquid_slime, + &r_gtao_liquid_lava, + &r_gtao_liquid_tele, }; for (uint32_t i = 0; i < countof (gtao_cvars); ++i) @@ -4497,6 +4539,10 @@ void R_Init (void) Cvar_RegisterVariable (&r_gtao_temporal); Cvar_RegisterVariable (&r_gtao_temporal_blend); Cvar_RegisterVariable (&r_gtao_halfres); + Cvar_RegisterVariable (&r_gtao_liquid_water); + Cvar_RegisterVariable (&r_gtao_liquid_slime); + Cvar_RegisterVariable (&r_gtao_liquid_lava); + Cvar_RegisterVariable (&r_gtao_liquid_tele); Cvar_RegisterVariable (&r_lodbias); Cvar_RegisterVariable (&gl_lodbias); Cvar_SetCallback (&r_scale, R_ScaleChanged_f); diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index ebd085e66..f6643f7a4 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -195,6 +195,10 @@ static vulkan_memory_t gtao_depth_pyramid_memory; static VkImageView gtao_depth_pyramid_view; static VkImageView gtao_depth_mip_views[GTAO_DEPTH_MIP_LEVELS]; static qboolean gtao_depth_pyramid_initialized; +static VkImage gtao_liquid_mask_buffer; +static vulkan_memory_t gtao_liquid_mask_buffer_memory; +static VkImageView gtao_liquid_mask_buffer_view; +static qboolean gtao_liquid_mask_buffer_initialized; static qboolean gtao_supported; static vulkan_memory_t color_buffers_memory[NUM_COLOR_BUFFERS]; static VkImageView color_buffers_view[NUM_COLOR_BUFFERS]; @@ -2203,6 +2207,28 @@ static void GL_CreateGTAOBuffer (void) GL_SetObjectName ((uint64_t)gtao_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "GTAO Buffer View"); gtao_buffer_initialized = false; + image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + err = vkCreateImage (vulkan_globals.device, &image_create_info, NULL, >ao_liquid_mask_buffer); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateImage failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)gtao_liquid_mask_buffer, VK_OBJECT_TYPE_IMAGE, "GTAO Liquid Mask Buffer"); + vkGetImageMemoryRequirements (vulkan_globals.device, gtao_liquid_mask_buffer, &memory_requirements); + dedicated_allocation_info.image = gtao_liquid_mask_buffer; + memory_allocate_info.allocationSize = memory_requirements.size; + memory_allocate_info.memoryTypeIndex = GL_MemoryTypeFromProperties (memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); + assert (gtao_liquid_mask_buffer_memory.handle == VK_NULL_HANDLE); + R_AllocateVulkanMemory (>ao_liquid_mask_buffer_memory, &memory_allocate_info, VULKAN_MEMORY_TYPE_DEVICE, &num_vulkan_misc_allocations); + GL_SetObjectName ((uint64_t)gtao_liquid_mask_buffer_memory.handle, VK_OBJECT_TYPE_DEVICE_MEMORY, "GTAO Liquid Mask Buffer"); + err = vkBindImageMemory (vulkan_globals.device, gtao_liquid_mask_buffer, gtao_liquid_mask_buffer_memory.handle, 0); + if (err != VK_SUCCESS) + Sys_Error ("vkBindImageMemory failed with code %i", (int)err); + image_view_create_info.image = gtao_liquid_mask_buffer; + err = vkCreateImageView (vulkan_globals.device, &image_view_create_info, NULL, >ao_liquid_mask_buffer_view); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateImageView failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)gtao_liquid_mask_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "GTAO Liquid Mask Buffer View"); + gtao_liquid_mask_buffer_initialized = false; + image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; err = vkCreateImage (vulkan_globals.device, &image_create_info, NULL, >ao_denoise_buffer); if (err != VK_SUCCESS) @@ -2958,7 +2984,7 @@ void GL_UpdateDescriptorSets (void) for (uint32_t mip = 0; mip < GTAO_DEPTH_MIP_LEVELS; ++mip) { vulkan_globals.gtao_depth_desc_sets[mip] = R_AllocateDescriptorSet (&vulkan_globals.gtao_depth_set_layout); - ZEROED_STRUCT_ARRAY (VkDescriptorImageInfo, image_infos, 3); + ZEROED_STRUCT_ARRAY (VkDescriptorImageInfo, image_infos, 4); image_infos[0].imageView = mip == 0 ? depth_buffer_view : gtao_depth_mip_views[mip - 1]; image_infos[0].imageLayout = mip == 0 ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image_infos[0].sampler = vulkan_globals.point_sampler; @@ -2967,14 +2993,16 @@ void GL_UpdateDescriptorSets (void) image_infos[2].imageView = stencil_buffer_view; image_infos[2].imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; image_infos[2].sampler = vulkan_globals.point_sampler; - ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, writes, 3); - for (uint32_t binding = 0; binding < 3; ++binding) + image_infos[3].imageView = gtao_liquid_mask_buffer_view; + image_infos[3].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, writes, 4); + for (uint32_t binding = 0; binding < 4; ++binding) { writes[binding].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[binding].dstSet = vulkan_globals.gtao_depth_desc_sets[mip]; writes[binding].dstBinding = binding; writes[binding].descriptorCount = 1; - writes[binding].descriptorType = binding == 1 ? VK_DESCRIPTOR_TYPE_STORAGE_IMAGE : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writes[binding].descriptorType = binding == 1 || binding == 3 ? VK_DESCRIPTOR_TYPE_STORAGE_IMAGE : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[binding].pImageInfo = &image_infos[binding]; } vkUpdateDescriptorSets (vulkan_globals.device, countof (writes), writes, 0, NULL); @@ -3019,8 +3047,12 @@ void GL_UpdateDescriptorSets (void) gtao_denoise_image_info.imageView = gtao_supported ? gtao_denoise_buffer_view : color_buffers_view[1]; gtao_denoise_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; gtao_denoise_image_info.sampler = vulkan_globals.point_sampler; + ZEROED_STRUCT (VkDescriptorImageInfo, gtao_liquid_mask_image_info); + gtao_liquid_mask_image_info.imageView = gtao_supported ? gtao_liquid_mask_buffer_view : color_buffers_view[1]; + gtao_liquid_mask_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + gtao_liquid_mask_image_info.sampler = vulkan_globals.point_sampler; - ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, screen_effects_writes, 8); + ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, screen_effects_writes, 9); screen_effects_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; screen_effects_writes[0].dstBinding = 0; screen_effects_writes[0].dstArrayElement = 0; @@ -3085,6 +3117,14 @@ void GL_UpdateDescriptorSets (void) screen_effects_writes[7].dstSet = vulkan_globals.screen_effects_desc_set; screen_effects_writes[7].pImageInfo = >ao_denoise_image_info; + screen_effects_writes[8].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + screen_effects_writes[8].dstBinding = 8; + screen_effects_writes[8].dstArrayElement = 0; + screen_effects_writes[8].descriptorCount = 1; + screen_effects_writes[8].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + screen_effects_writes[8].dstSet = vulkan_globals.screen_effects_desc_set; + screen_effects_writes[8].pImageInfo = >ao_liquid_mask_image_info; + vkUpdateDescriptorSets (vulkan_globals.device, countof (screen_effects_writes), screen_effects_writes, 0, NULL); #if defined(_DEBUG) @@ -3653,6 +3693,13 @@ static void GL_DestroyRenderResources (void) gtao_buffer = VK_NULL_HANDLE; gtao_buffer_initialized = false; + vkDestroyImageView (vulkan_globals.device, gtao_liquid_mask_buffer_view, NULL); + vkDestroyImage (vulkan_globals.device, gtao_liquid_mask_buffer, NULL); + R_FreeVulkanMemory (>ao_liquid_mask_buffer_memory, &num_vulkan_misc_allocations); + gtao_liquid_mask_buffer_view = VK_NULL_HANDLE; + gtao_liquid_mask_buffer = VK_NULL_HANDLE; + gtao_liquid_mask_buffer_initialized = false; + vkDestroyImageView (vulkan_globals.device, gtao_denoise_buffer_view, NULL); vkDestroyImage (vulkan_globals.device, gtao_denoise_buffer, NULL); R_FreeVulkanMemory (>ao_denoise_buffer_memory, &num_vulkan_misc_allocations); @@ -4028,6 +4075,10 @@ typedef struct screen_effect_constants_s uint32_t gtao_denoise; float gtao_multibounce; uint32_t gtao_halfres; + float gtao_liquid_water; + float gtao_liquid_slime; + float gtao_liquid_lava; + float gtao_liquid_tele; } screen_effect_constants_t; typedef struct gtao_constants_s @@ -4125,6 +4176,21 @@ typedef struct gtao_depth_constants_s static void GL_GTAODepthPyramid (cb_context_t *cbx, end_rendering_parms_t *parms) { R_BeginDebugUtilsLabel (cbx, "GTAO Depth Pyramid"); + ZEROED_STRUCT (VkImageMemoryBarrier, liquid_mask_barrier); + liquid_mask_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + liquid_mask_barrier.srcAccessMask = gtao_liquid_mask_buffer_initialized ? VK_ACCESS_SHADER_READ_BIT : 0; + liquid_mask_barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + liquid_mask_barrier.oldLayout = gtao_liquid_mask_buffer_initialized ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; + liquid_mask_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + liquid_mask_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + liquid_mask_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + liquid_mask_barrier.image = gtao_liquid_mask_buffer; + liquid_mask_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + liquid_mask_barrier.subresourceRange.levelCount = 1; + liquid_mask_barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier ( + cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &liquid_mask_barrier); + uint32_t width = parms->vid_width; uint32_t height = parms->vid_height; for (uint32_t mip = 0; mip < GTAO_DEPTH_MIP_LEVELS; ++mip) @@ -4167,6 +4233,13 @@ static void GL_GTAODepthPyramid (cb_context_t *cbx, end_rendering_parms_t *parms height = q_max (1u, height / 2); } gtao_depth_pyramid_initialized = true; + liquid_mask_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + liquid_mask_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + liquid_mask_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + liquid_mask_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + vkCmdPipelineBarrier ( + cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &liquid_mask_barrier); + gtao_liquid_mask_buffer_initialized = true; R_EndDebugUtilsLabel (cbx); } @@ -4580,10 +4653,14 @@ static void GL_ScreenEffects (cb_context_t *cbx, qboolean enabled, end_rendering (float)parms->v_blend[2] / 255.0f, (float)parms->v_blend[3] / 255.0f, r_gtao_strength.value, - (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 7), + (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 8), (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 7) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u, CLAMP (0.0f, r_gtao_multibounce.value, 1.0f), r_gtao_halfres.value > 0.0f ? 1u : 0u, + CLAMP (0.0f, r_gtao_liquid_water.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_WATER), 1.0f), + CLAMP (0.0f, r_gtao_liquid_slime.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_SLIME), 1.0f), + CLAMP (0.0f, r_gtao_liquid_lava.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_LAVA), 1.0f), + CLAMP (0.0f, r_gtao_liquid_tele.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_TELE), 1.0f), }; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (push_constants), &push_constants); } diff --git a/Quake/glquake.h b/Quake/glquake.h index 48e948c4a..2a6087759 100644 --- a/Quake/glquake.h +++ b/Quake/glquake.h @@ -188,7 +188,15 @@ typedef struct vulkan_memory_s vulkan_memory_type_t type; } vulkan_memory_t; -#define WORLD_PIPELINE_COUNT 16 +#define WORLD_PIPELINE_LIQUID_BIT 16 +#define WORLD_PIPELINE_COUNT 32 +#define STENCIL_MASK_SKY 0x01u +#define STENCIL_MASK_VIEWMODEL 0x02u +#define STENCIL_MASK_WATER 0x04u +#define STENCIL_MASK_SLIME 0x08u +#define STENCIL_MASK_LAVA 0x10u +#define STENCIL_MASK_TELE 0x20u +#define STENCIL_MASK_LIQUID (STENCIL_MASK_WATER | STENCIL_MASK_SLIME | STENCIL_MASK_LAVA | STENCIL_MASK_TELE) // slot layout of the alias/md5 pipeline arrays: 0..3 encode alpha test/blend, 4..5 are the r_showtris variants #define MODEL_PIPELINE_ALPHA_TEST_BIT 1 #define MODEL_PIPELINE_ALPHA_BLEND_BIT 2 @@ -608,6 +616,10 @@ extern cvar_t r_gtao_multibounce; extern cvar_t r_gtao_temporal; extern cvar_t r_gtao_temporal_blend; extern cvar_t r_gtao_halfres; +extern cvar_t r_gtao_liquid_water; +extern cvar_t r_gtao_liquid_slime; +extern cvar_t r_gtao_liquid_lava; +extern cvar_t r_gtao_liquid_tele; extern cvar_t gl_polyblend; extern cvar_t gl_nocolors; diff --git a/Quake/r_brush.c b/Quake/r_brush.c index 33e057cce..a009dd576 100644 --- a/Quake/r_brush.c +++ b/Quake/r_brush.c @@ -978,11 +978,21 @@ void R_DrawIndirectBrushes (cb_context_t *cbx, qboolean draw_water, qboolean tra const qboolean alpha_blend = alpha < 1.0f; int pipeline_index = (fullbright_enabled ? 1 : 0) + (alpha_test ? 2 : 0) + (alpha_blend ? 4 : 0) + (vid_filter.value != 0 && vid_palettize.value != 0 ? 8 : 0); + if (draw_water) + pipeline_index |= WORLD_PIPELINE_LIQUID_BIT; vulkan_pipeline_t pipeline = R_PipelineForRenderPass ( cbx->render_pass_index, vulkan_globals.world_pipelines[R_MainPassPipelineVariant (cbx->render_pass_index)][pipeline_index], vulkan_globals.world_wboit_pipelines[pipeline_index], vulkan_globals.world_mboit_moment_pipelines[pipeline_index], vulkan_globals.world_mboit_composite_pipelines[pipeline_index]); R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + if (draw_water) + { + const uint32_t stencil_reference = texture->type == TEXTYPE_WATER ? STENCIL_MASK_WATER + : texture->type == TEXTYPE_SLIME ? STENCIL_MASK_SLIME + : texture->type == TEXTYPE_LAVA ? STENCIL_MASK_LAVA + : STENCIL_MASK_TELE; + vkCmdSetStencilReference (cbx->cb, VK_STENCIL_FACE_FRONT_AND_BACK, stencil_reference); + } qboolean use_zbias = INDIRECT_ZBIAS && gl_zfix.value && indirect_draws[i].is_bmodel; float constant_factor = 0.0f, slope_factor = 0.0f; diff --git a/Quake/r_world.c b/Quake/r_world.c index 4a9149359..964f982bd 100644 --- a/Quake/r_world.c +++ b/Quake/r_world.c @@ -1154,17 +1154,27 @@ Draw the current batch if non-empty and clears it, ready for more R_BatchSurface */ static void R_FlushBatch ( cb_context_t *cbx, qboolean fullbright_enabled, qboolean alpha_test, qboolean alpha_blend, qboolean use_zbias, gltexture_t *lightmap_texture, - uint32_t *brushpasses) + textype_t liquid_type, uint32_t *brushpasses) { if (cbx->num_vbo_indices > 0) { int pipeline_index = (fullbright_enabled ? 1 : 0) + (alpha_test ? 2 : 0) + (alpha_blend ? 4 : 0) + (vid_filter.value != 0 && vid_palettize.value != 0 ? 8 : 0); + if (TEXTYPE_ISLIQUID (liquid_type)) + pipeline_index |= WORLD_PIPELINE_LIQUID_BIT; vulkan_pipeline_t pipeline = R_PipelineForRenderPass ( cbx->render_pass_index, vulkan_globals.world_pipelines[R_MainPassPipelineVariant (cbx->render_pass_index)][pipeline_index], vulkan_globals.world_wboit_pipelines[pipeline_index], vulkan_globals.world_mboit_moment_pipelines[pipeline_index], vulkan_globals.world_mboit_composite_pipelines[pipeline_index]); R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + if (TEXTYPE_ISLIQUID (liquid_type)) + { + const uint32_t stencil_reference = liquid_type == TEXTYPE_WATER ? STENCIL_MASK_WATER + : liquid_type == TEXTYPE_SLIME ? STENCIL_MASK_SLIME + : liquid_type == TEXTYPE_LAVA ? STENCIL_MASK_LAVA + : STENCIL_MASK_TELE; + vkCmdSetStencilReference (cbx->cb, VK_STENCIL_FACE_FRONT_AND_BACK, stencil_reference); + } float constant_factor = 0.0f, slope_factor = 0.0f; if (use_zbias) @@ -1212,14 +1222,14 @@ using VBOs. */ static void R_BatchSurface ( cb_context_t *cbx, msurface_t *s, qboolean fullbright_enabled, qboolean alpha_test, qboolean alpha_blend, qboolean use_zbias, gltexture_t *lightmap_texture, - uint32_t *brushpasses) + textype_t liquid_type, uint32_t *brushpasses) { int num_surf_indices; num_surf_indices = R_NumTriangleIndicesForSurf (s); if (cbx->num_vbo_indices + num_surf_indices > MAX_BATCH_SIZE) - R_FlushBatch (cbx, fullbright_enabled, alpha_test, alpha_blend, use_zbias, lightmap_texture, brushpasses); + R_FlushBatch (cbx, fullbright_enabled, alpha_test, alpha_blend, use_zbias, lightmap_texture, liquid_type, brushpasses); R_TriangleIndicesForSurf (s, &cbx->vbo_indices[cbx->num_vbo_indices]); cbx->num_vbo_indices += num_surf_indices; @@ -1323,16 +1333,16 @@ void R_DrawTextureChains_Water (cb_context_t *cbx, qmodel_t *model, entity_t *en { if (alpha_blend) R_PushConstants (cbx, VK_SHADER_STAGE_ALL_GRAPHICS, 20 * sizeof (float), 1 * sizeof (float), &alpha); - R_FlushBatch (cbx, false, false, alpha_blend, false, lightmap_texture, &brushpasses); + R_FlushBatch (cbx, false, false, alpha_blend, false, lightmap_texture, (textype_t)type, &brushpasses); lightmap_texture = (s->lightmaptexturenum >= 0) ? lightmaps[s->lightmaptexturenum].texture : greylightmap; lastlightmap = s->lightmaptexturenum; } - R_BatchSurface (cbx, s, false, false, alpha_blend, false, lightmap_texture, &brushpasses); + R_BatchSurface (cbx, s, false, false, alpha_blend, false, lightmap_texture, (textype_t)type, &brushpasses); } if (alpha_blend) R_PushConstants (cbx, VK_SHADER_STAGE_ALL_GRAPHICS, 20 * sizeof (float), 1 * sizeof (float), &alpha); - R_FlushBatch (cbx, false, false, alpha_blend, false, lightmap_texture, &brushpasses); + R_FlushBatch (cbx, false, false, alpha_blend, false, lightmap_texture, (textype_t)type, &brushpasses); } } @@ -1408,15 +1418,15 @@ void R_DrawTextureChains_Multitexture (cb_context_t *cbx, qmodel_t *model, entit { if (s->lightmaptexturenum != lastlightmap) { - R_FlushBatch (cbx, fullbright_enabled, alpha_test, alpha_blend, use_zbias, lightmap_texture, &brushpasses); + R_FlushBatch (cbx, fullbright_enabled, alpha_test, alpha_blend, use_zbias, lightmap_texture, TEXTYPE_COUNT, &brushpasses); lightmap_texture = lightmaps[s->lightmaptexturenum].texture; } lastlightmap = s->lightmaptexturenum; - R_BatchSurface (cbx, s, fullbright_enabled, alpha_test, alpha_blend, use_zbias, lightmap_texture, &brushpasses); + R_BatchSurface (cbx, s, fullbright_enabled, alpha_test, alpha_blend, use_zbias, lightmap_texture, TEXTYPE_COUNT, &brushpasses); } - R_FlushBatch (cbx, fullbright_enabled, alpha_test, alpha_blend, use_zbias, lightmap_texture, &brushpasses); + R_FlushBatch (cbx, fullbright_enabled, alpha_test, alpha_blend, use_zbias, lightmap_texture, TEXTYPE_COUNT, &brushpasses); } Atomic_AddUInt32 (&rs_brushpasses, brushpasses); diff --git a/Shaders/gtao_depth.comp b/Shaders/gtao_depth.comp index 8426ca4db..cd9bc633e 100644 --- a/Shaders/gtao_depth.comp +++ b/Shaders/gtao_depth.comp @@ -13,6 +13,9 @@ layout (set = 0, binding = 0) uniform sampler2D input_depth_tex; layout (set = 0, binding = 2) uniform usampler2D input_stencil_tex; #endif layout (set = 0, binding = 1, r32f) uniform writeonly image2D output_depth_image; +#ifndef GTAO_DEPTH_DOWNSAMPLE +layout (set = 0, binding = 3, rgba8) uniform writeonly image2D output_liquid_mask_image; +#endif layout (push_constant) uniform DepthConsts { @@ -26,6 +29,7 @@ push_constants; layout (local_size_x = 8, local_size_y = 8) in; const uint STENCIL_EXCLUDED = 0x3u; +const uint STENCIL_MASK_LIQUID = 0x3cu; float depth_mip_filter (vec4 depths) { @@ -62,17 +66,22 @@ void main () imageStore (output_depth_image, pixel, vec4 (depth_mip_filter (depths))); #else float device_depth = 0.0f; + uint stencil = 0u; #ifdef GTAO_DEPTH_MSAA for (int sample_index = 0; sample_index < textureSamples (input_depth_tex); ++sample_index) { - if ((texelFetch (input_stencil_tex, pixel, sample_index).r & STENCIL_EXCLUDED) == 0u) + uint sample_stencil = texelFetch (input_stencil_tex, pixel, sample_index).r; + stencil |= sample_stencil; + if ((sample_stencil & STENCIL_EXCLUDED) == 0u) device_depth = max (device_depth, texelFetch (input_depth_tex, pixel, sample_index).r); } #else - if ((texelFetch (input_stencil_tex, pixel, 0).r & STENCIL_EXCLUDED) == 0u) + stencil = texelFetch (input_stencil_tex, pixel, 0).r; + if ((stencil & STENCIL_EXCLUDED) == 0u) device_depth = texelFetch (input_depth_tex, pixel, 0).r; #endif float view_depth = device_depth > 0.00001f ? push_constants.projection.y / (device_depth + push_constants.projection.x) : 0.0f; imageStore (output_depth_image, pixel, vec4 (max (view_depth, 0.0f))); + imageStore (output_liquid_mask_image, pixel, vec4 (float (stencil & STENCIL_MASK_LIQUID) / 255.0f, 0.0f, 0.0f, 1.0f)); #endif } diff --git a/Shaders/screen_effects.inc b/Shaders/screen_effects.inc index 5a2ec5e96..e03faa113 100644 --- a/Shaders/screen_effects.inc +++ b/Shaders/screen_effects.inc @@ -12,6 +12,7 @@ layout (set = 0, binding = 1) uniform sampler2D blue_noise_tex; layout (set = 0, binding = 5) uniform sampler2D gtao_tex; layout (set = 0, binding = 6) uniform sampler2D gtao_view_depth_tex; layout (set = 0, binding = 7) uniform sampler2D gtao_denoise_tex; +layout (set = 0, binding = 8) uniform sampler2D gtao_liquid_mask_tex; layout (set = 0, binding = 3) uniform samplerBuffer palette_colors; layout (set = 0, binding = 4) uniform PaletteOctree { @@ -35,9 +36,50 @@ layout (push_constant) uniform PushConsts uint gtao_denoise; float gtao_multibounce; uint gtao_halfres; + float gtao_liquid_water; + float gtao_liquid_slime; + float gtao_liquid_lava; + float gtao_liquid_tele; } push_constants; +const uint STENCIL_MASK_WATER = 0x04u; +const uint STENCIL_MASK_SLIME = 0x08u; +const uint STENCIL_MASK_LAVA = 0x10u; +const uint STENCIL_MASK_TELE = 0x20u; + +uint gtao_liquid_mask (ivec2 pixel) +{ + return uint (round (texelFetch (gtao_liquid_mask_tex, pixel, 0).r * 255.0f)); +} + +float gtao_liquid_suppression (uint mask) +{ + float suppression = 0.0f; + if ((mask & STENCIL_MASK_WATER) != 0u) + suppression = max (suppression, push_constants.gtao_liquid_water); + if ((mask & STENCIL_MASK_SLIME) != 0u) + suppression = max (suppression, push_constants.gtao_liquid_slime); + if ((mask & STENCIL_MASK_LAVA) != 0u) + suppression = max (suppression, push_constants.gtao_liquid_lava); + if ((mask & STENCIL_MASK_TELE) != 0u) + suppression = max (suppression, push_constants.gtao_liquid_tele); + return clamp (suppression, 0.0f, 1.0f); +} + +vec3 gtao_liquid_debug_color (uint mask) +{ + if ((mask & STENCIL_MASK_LAVA) != 0u) + return vec3 (1.0f, 0.15f, 0.02f); + if ((mask & STENCIL_MASK_SLIME) != 0u) + return vec3 (0.1f, 1.0f, 0.1f); + if ((mask & STENCIL_MASK_TELE) != 0u) + return vec3 (0.8f, 0.1f, 1.0f); + if ((mask & STENCIL_MASK_WATER) != 0u) + return vec3 (0.1f, 0.4f, 1.0f); + return vec3 (0.0f); +} + #if defined(USE_SUBGROUP_OPS) uint Compact1By1 (uint x) { @@ -245,7 +287,12 @@ void main () const ivec2 ao_pixel = ivec2 (min (push_constants.clamp_size.x, pos_x), min (push_constants.clamp_size.y, pos_y)); const bool gtao_warped = (push_constants.flags & SCREEN_EFFECT_FLAG_WATER_WARP) != 0; const vec4 ao_sample = gtao_resolve (ao_pixel, effect_texcoord, gtao_warped); - if (push_constants.gtao_debug_mode == 1u || push_constants.gtao_debug_mode == 7u) + const ivec2 liquid_pixel = gtao_warped ? + clamp (ivec2 (effect_texcoord / push_constants.screen_size_rcp), ivec2 (0), ivec2 (push_constants.clamp_size)) : ao_pixel; + const uint liquid_mask = gtao_liquid_mask (liquid_pixel); + if (push_constants.gtao_debug_mode == 8u) + color.rgb = gtao_liquid_debug_color (liquid_mask); + else if (push_constants.gtao_debug_mode == 1u || push_constants.gtao_debug_mode == 7u) color.rgb = vec3 (clamp (ao_sample.r, 0.0f, 1.0f)); else if (push_constants.gtao_debug_mode > 1u) color.rgb = ao_sample.rgb; @@ -253,6 +300,7 @@ void main () { float ao_visibility = clamp (ao_sample.r, 0.0f, 1.0f); float visibility = clamp (1.0f - ((1.0f - ao_visibility) * push_constants.gtao_strength), 0.0f, 1.0f); + visibility = mix (visibility, 1.0f, gtao_liquid_suppression (liquid_mask)); vec3 single_bounce = color.rgb * visibility; vec3 multi_bounce = color.rgb * gtao_multibounce_visibility (visibility, color.rgb); color.rgb = mix (single_bounce, multi_bounce, clamp (push_constants.gtao_multibounce, 0.0f, 1.0f)); From 82837f84f9bbf8398a1468ed5369faaac978337e Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 16:59:01 -0500 Subject: [PATCH 03/26] Avoid redundant GTAO stencil sampling --- Shaders/gtao.comp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/Shaders/gtao.comp b/Shaders/gtao.comp index db6c95a53..26793e276 100644 --- a/Shaders/gtao.comp +++ b/Shaders/gtao.comp @@ -104,10 +104,13 @@ bool valid_surface_sample (ivec2 pixel, float depth) return inside_viewport (pixel) && depth > 0.00001f && (sample_stencil (pixel) & (STENCIL_MASK_SKY | STENCIL_MASK_VIEWMODEL)) == 0u; } -float sample_horizon_depth (ivec2 pixel, float offset_length) +float sample_horizon_depth (ivec2 pixel, float offset_length, out bool valid) { float depth = sample_depth (pixel); - if (push_constants.depth_mip_offset > 32.0f || !valid_surface_sample (pixel, depth)) + valid = inside_viewport (pixel) && depth > 0.00001f; + if (valid) + valid = (sample_stencil (pixel) & (STENCIL_MASK_SKY | STENCIL_MASK_VIEWMODEL)) == 0u; + if (push_constants.depth_mip_offset > 32.0f || !valid) return depth; float mip_level = clamp (log2 (max (offset_length, 1.0f)) - push_constants.depth_mip_offset, 0.0f, float (textureQueryLevels (view_depth_pyramid_tex) - 1)); @@ -121,7 +124,9 @@ float sample_horizon_depth (ivec2 pixel, float offset_length) float depth_0 = texelFetch (view_depth_pyramid_tex, coord_0, mip_0).r; float depth_1 = texelFetch (view_depth_pyramid_tex, coord_1, mip_1).r; float view_depth = mix (depth_0, depth_1, fract (mip_level)); - return view_depth > 0.00001f ? push_constants.projection.w / view_depth - push_constants.projection.z : depth; + float result = view_depth > 0.00001f ? push_constants.projection.w / view_depth - push_constants.projection.z : depth; + valid = result > 0.00001f; + return result; } vec4 calculate_edges (float center_z, float left_z, float right_z, float top_z, float bottom_z) @@ -417,8 +422,9 @@ void main () ivec2 sample_pixel_0 = pixel + sample_offset; if (inside_viewport (sample_pixel_0)) { - float sample_depth_0 = sample_horizon_depth (sample_pixel_0, sample_offset_length); - if (valid_surface_sample (sample_pixel_0, sample_depth_0)) + bool sample_valid_0; + float sample_depth_0 = sample_horizon_depth (sample_pixel_0, sample_offset_length, sample_valid_0); + if (sample_valid_0) { vec3 sample_delta_0 = reconstruct_position (sample_pixel_0, sample_depth_0) - center; float sample_distance_0 = length (sample_delta_0); @@ -436,8 +442,9 @@ void main () ivec2 sample_pixel_1 = pixel - sample_offset; if (inside_viewport (sample_pixel_1)) { - float sample_depth_1 = sample_horizon_depth (sample_pixel_1, sample_offset_length); - if (valid_surface_sample (sample_pixel_1, sample_depth_1)) + bool sample_valid_1; + float sample_depth_1 = sample_horizon_depth (sample_pixel_1, sample_offset_length, sample_valid_1); + if (sample_valid_1) { vec3 sample_delta_1 = reconstruct_position (sample_pixel_1, sample_depth_1) - center; float sample_distance_1 = length (sample_delta_1); From cac3aabc6b559e0cb95f5c917042d3fe9b829507 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 17:15:33 -0500 Subject: [PATCH 04/26] Reuse half-resolution GTAO base sample --- Shaders/screen_effects.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shaders/screen_effects.inc b/Shaders/screen_effects.inc index e03faa113..47db2fe5f 100644 --- a/Shaders/screen_effects.inc +++ b/Shaders/screen_effects.inc @@ -160,7 +160,7 @@ vec4 gtao_resolve (ivec2 target_pixel, vec2 target_uv, bool warped) ivec2 source_pixel = min (candidates[i] * 2, source_max); float sample_depth = texelFetch (gtao_view_depth_tex, source_pixel, 0).r; float depth_difference = gtao_relative_depth_difference (target_depth, sample_depth); - vec4 candidate = gtao_fetch (candidates[i]); + vec4 candidate = i == 0 ? closest_result : gtao_fetch (candidates[i]); if (depth_difference < closest_depth_difference) { closest_depth_difference = depth_difference; From 43f39d1be8e2d67f8b4fdebd1da80fb2535dcbc5 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 17:24:09 -0500 Subject: [PATCH 05/26] Use gathered samples in GTAO denoiser --- Quake/gl_vidsdl.c | 6 +-- Shaders/gtao_denoise.comp | 108 ++++++++++++++++++++++++++++---------- 2 files changed, 83 insertions(+), 31 deletions(-) diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index f6643f7a4..68b37dd2a 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -4281,7 +4281,7 @@ static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) const uint32_t working_height = (parms->vid_height + working_stride - 1) / working_stride; gtao_denoise_constants_t constants = {working_width - 1, working_height - 1, 1u, pass_count == 1 ? 1.2f : 0.24f}; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); - vkCmdDispatch (cbx->cb, (working_width + 7) / 8, (working_height + 7) / 8, 1); + vkCmdDispatch (cbx->cb, (working_width + 15) / 16, (working_height + 7) / 8, 1); if (pass_count >= 2) { @@ -4302,7 +4302,7 @@ static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) &vulkan_globals.gtao_denoise_desc_sets[1], 0, NULL); constants.center_weight = pass_count == 2 ? 1.2f : 0.24f; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); - vkCmdDispatch (cbx->cb, (working_width + 7) / 8, (working_height + 7) / 8, 1); + vkCmdDispatch (cbx->cb, (working_width + 15) / 16, (working_height + 7) / 8, 1); barriers[1].srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; barriers[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; barriers[1].oldLayout = VK_IMAGE_LAYOUT_GENERAL; @@ -4321,7 +4321,7 @@ static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) &vulkan_globals.gtao_denoise_desc_sets[0], 0, NULL); constants.center_weight = 1.2f; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); - vkCmdDispatch (cbx->cb, (working_width + 7) / 8, (working_height + 7) / 8, 1); + vkCmdDispatch (cbx->cb, (working_width + 15) / 16, (working_height + 7) / 8, 1); barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; diff --git a/Shaders/gtao_denoise.comp b/Shaders/gtao_denoise.comp index 1364ad33e..811f0bb86 100644 --- a/Shaders/gtao_denoise.comp +++ b/Shaders/gtao_denoise.comp @@ -23,33 +23,13 @@ vec4 unpack_edges (float packed_value) float ((packed >> 2) & 3u), float (packed & 3u)) / 3.0f; } -void main () +vec4 denoise_value ( + vec4 center, vec4 ao_l, vec4 ao_r, vec4 ao_t, vec4 ao_b, + vec4 ao_tl, vec4 ao_tr, vec4 ao_bl, vec4 ao_br) { - ivec2 pixel = ivec2 (gl_GlobalInvocationID.xy) * int (push_constants.sample_stride); - if (any (greaterThan (pixel, ivec2 (push_constants.clamp_size)))) - return; - - vec4 center = texelFetch (input_ao_tex, pixel, 0); if (all (greaterThan (center, vec4 (0.999f)))) - { - imageStore (output_ao_image, pixel, center); - return; - } + return center; - int stride = int (push_constants.sample_stride); - ivec2 limit = ivec2 (push_constants.clamp_size); - ivec2 p_l = clamp (pixel + ivec2 (-stride, 0), ivec2 (0), limit); - ivec2 p_r = clamp (pixel + ivec2 ( stride, 0), ivec2 (0), limit); - ivec2 p_t = clamp (pixel + ivec2 (0, -stride), ivec2 (0), limit); - ivec2 p_b = clamp (pixel + ivec2 (0, stride), ivec2 (0), limit); - ivec2 p_tl = clamp (pixel + ivec2 (-stride, -stride), ivec2 (0), limit); - ivec2 p_tr = clamp (pixel + ivec2 ( stride, -stride), ivec2 (0), limit); - ivec2 p_bl = clamp (pixel + ivec2 (-stride, stride), ivec2 (0), limit); - ivec2 p_br = clamp (pixel + ivec2 ( stride, stride), ivec2 (0), limit); - vec4 ao_l = texelFetch (input_ao_tex, p_l, 0); - vec4 ao_r = texelFetch (input_ao_tex, p_r, 0); - vec4 ao_t = texelFetch (input_ao_tex, p_t, 0); - vec4 ao_b = texelFetch (input_ao_tex, p_b, 0); vec4 edges_l = unpack_edges (ao_l.g); vec4 edges_r = unpack_edges (ao_r.g); vec4 edges_t = unpack_edges (ao_t.g); @@ -64,10 +44,82 @@ void main () float weight_bl = diagonal_weight * (edges_c.w * edges_b.x + edges_c.x * edges_l.w); float weight_br = diagonal_weight * (edges_c.y * edges_r.w + edges_c.w * edges_b.y); float ao_sum = center.r * push_constants.center_weight + ao_l.r * edges_c.x + ao_r.r * edges_c.y + - ao_t.r * edges_c.z + ao_b.r * edges_c.w + texelFetch (input_ao_tex, p_tl, 0).r * weight_tl + - texelFetch (input_ao_tex, p_tr, 0).r * weight_tr + texelFetch (input_ao_tex, p_bl, 0).r * weight_bl + - texelFetch (input_ao_tex, p_br, 0).r * weight_br; + ao_t.r * edges_c.z + ao_b.r * edges_c.w + ao_tl.r * weight_tl + ao_tr.r * weight_tr + + ao_bl.r * weight_bl + ao_br.r * weight_br; float weight_sum = push_constants.center_weight + dot (edges_c, vec4 (1.0f)) + weight_tl + weight_tr + weight_bl + weight_br; center.r = ao_sum / max (weight_sum, 0.0001f); - imageStore (output_ao_image, pixel, center); + return center; +} + +vec4 denoise_fetch (ivec2 pixel) +{ + int stride = int (push_constants.sample_stride); + ivec2 limit = ivec2 (push_constants.clamp_size); + ivec2 p_l = clamp (pixel + ivec2 (-stride, 0), ivec2 (0), limit); + ivec2 p_r = clamp (pixel + ivec2 ( stride, 0), ivec2 (0), limit); + ivec2 p_t = clamp (pixel + ivec2 (0, -stride), ivec2 (0), limit); + ivec2 p_b = clamp (pixel + ivec2 (0, stride), ivec2 (0), limit); + ivec2 p_tl = clamp (pixel + ivec2 (-stride, -stride), ivec2 (0), limit); + ivec2 p_tr = clamp (pixel + ivec2 ( stride, -stride), ivec2 (0), limit); + ivec2 p_bl = clamp (pixel + ivec2 (-stride, stride), ivec2 (0), limit); + ivec2 p_br = clamp (pixel + ivec2 ( stride, stride), ivec2 (0), limit); + vec4 center = texelFetch (input_ao_tex, pixel, 0); + if (all (greaterThan (center, vec4 (0.999f)))) + return center; + return denoise_value ( + center, texelFetch (input_ao_tex, p_l, 0), texelFetch (input_ao_tex, p_r, 0), + texelFetch (input_ao_tex, p_t, 0), texelFetch (input_ao_tex, p_b, 0), + texelFetch (input_ao_tex, p_tl, 0), texelFetch (input_ao_tex, p_tr, 0), + texelFetch (input_ao_tex, p_bl, 0), texelFetch (input_ao_tex, p_br, 0)); +} + +vec4 gathered_sample (float visibility, float packed_edges) +{ + return vec4 (visibility, packed_edges, 0.0f, 0.0f); +} + +void main () +{ + int stride = int (push_constants.sample_stride); + ivec2 limit = ivec2 (push_constants.clamp_size); + ivec2 pixel_base = ivec2 (int (gl_GlobalInvocationID.x) * 2, int (gl_GlobalInvocationID.y)) * stride; + if (any (greaterThan (pixel_base, limit))) + return; + + ivec2 pixel_next = pixel_base + ivec2 (stride, 0); + bool pair_inside = stride == 1 && pixel_base.x > 0 && pixel_next.x < limit.x && pixel_base.y > 0 && pixel_base.y < limit.y; + if (!pair_inside) + { + imageStore (output_ao_image, pixel_base, denoise_fetch (pixel_base)); + if (pixel_next.x <= limit.x) + imageStore (output_ao_image, pixel_next, denoise_fetch (pixel_next)); + return; + } + + vec2 gather_center = vec2 (pixel_base) / vec2 (textureSize (input_ao_tex, 0)); + vec4 vis_q0 = textureGatherOffset (input_ao_tex, gather_center, ivec2 (0, 0), 0); + vec4 vis_q1 = textureGatherOffset (input_ao_tex, gather_center, ivec2 (2, 0), 0); + vec4 vis_q2 = textureGatherOffset (input_ao_tex, gather_center, ivec2 (0, 2), 0); + vec4 vis_q3 = textureGatherOffset (input_ao_tex, gather_center, ivec2 (2, 2), 0); + vec4 edge_q0 = textureGatherOffset (input_ao_tex, gather_center, ivec2 (0, 0), 1); + vec4 edge_q1 = textureGatherOffset (input_ao_tex, gather_center, ivec2 (2, 0), 1); + vec4 edge_q2 = textureGatherOffset (input_ao_tex, gather_center, ivec2 (1, 2), 1); + + vec4 center_0 = texelFetch (input_ao_tex, pixel_base, 0); + vec4 result_0 = denoise_value ( + center_0, + gathered_sample (vis_q0.x, edge_q0.x), gathered_sample (vis_q1.x, edge_q1.x), + gathered_sample (vis_q0.z, edge_q0.z), gathered_sample (vis_q2.z, edge_q2.w), + gathered_sample (vis_q0.w, 0.0f), gathered_sample (vis_q1.w, 0.0f), + gathered_sample (vis_q2.w, 0.0f), gathered_sample (vis_q3.w, 0.0f)); + imageStore (output_ao_image, pixel_base, result_0); + + vec4 center_1 = texelFetch (input_ao_tex, pixel_next, 0); + vec4 result_1 = denoise_value ( + center_1, + gathered_sample (vis_q0.y, edge_q0.y), gathered_sample (vis_q1.y, edge_q1.y), + gathered_sample (vis_q1.w, edge_q1.w), gathered_sample (vis_q3.w, edge_q2.z), + gathered_sample (vis_q0.z, 0.0f), gathered_sample (vis_q1.z, 0.0f), + gathered_sample (vis_q2.z, 0.0f), gathered_sample (vis_q3.z, 0.0f)); + imageStore (output_ao_image, pixel_next, result_1); } From 1e9b5f48f938ee326383b23cb50e5d4b0d5b4522 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 17:46:53 -0500 Subject: [PATCH 06/26] Use single-channel GTAO liquid mask when supported --- Quake/gl_rmisc.c | 14 ++++++++++++++ Quake/gl_vidsdl.c | 16 +++++++++++++++- Quake/glquake.h | 2 ++ Shaders/gtao_depth.comp | 4 ++++ Shaders/shaders.h | 2 ++ Windows/VisualStudio/embedded.vcxproj | 16 +++++++++++++--- Windows/VisualStudio/vkquake.vcxproj | 20 ++++++++++++++++++++ Windows/VisualStudio/vkquake.vcxproj.filters | 12 ++++++++++++ meson.build | 2 ++ 9 files changed, 84 insertions(+), 4 deletions(-) diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index d2a0b59d4..45729c299 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -2120,6 +2120,8 @@ void R_CreatePipelineLayouts () GL_SetObjectName ((uint64_t)vulkan_globals.gtao_depth_pipeline.layout.handle, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "gtao_depth_pipeline_layout"); vulkan_globals.gtao_depth_pipeline.layout.push_constant_range = push_constant_range; vulkan_globals.gtao_depth_msaa_pipeline.layout = vulkan_globals.gtao_depth_pipeline.layout; + vulkan_globals.gtao_depth_r8_pipeline.layout = vulkan_globals.gtao_depth_pipeline.layout; + vulkan_globals.gtao_depth_msaa_r8_pipeline.layout = vulkan_globals.gtao_depth_pipeline.layout; vulkan_globals.gtao_depth_downsample_pipeline.layout = vulkan_globals.gtao_depth_pipeline.layout; } @@ -2607,6 +2609,8 @@ DECLARE_SHADER_MODULE (gtao_comp); DECLARE_SHADER_MODULE (gtao_msaa_comp); DECLARE_SHADER_MODULE (gtao_depth_comp); DECLARE_SHADER_MODULE (gtao_depth_msaa_comp); +DECLARE_SHADER_MODULE (gtao_depth_r8_comp); +DECLARE_SHADER_MODULE (gtao_depth_msaa_r8_comp); DECLARE_SHADER_MODULE (gtao_depth_downsample_comp); DECLARE_SHADER_MODULE (gtao_denoise_comp); DECLARE_SHADER_MODULE (cs_tex_warp_comp); @@ -3958,6 +3962,8 @@ static void R_CreateGTAOPipelines () R_CreateComputePipeline (&vulkan_globals.gtao_msaa_pipeline, gtao_msaa_comp_module, 0, NULL, "gtao_msaa"); R_CreateComputePipeline (&vulkan_globals.gtao_depth_pipeline, gtao_depth_comp_module, 0, NULL, "gtao_depth"); R_CreateComputePipeline (&vulkan_globals.gtao_depth_msaa_pipeline, gtao_depth_msaa_comp_module, 0, NULL, "gtao_depth_msaa"); + R_CreateComputePipeline (&vulkan_globals.gtao_depth_r8_pipeline, gtao_depth_r8_comp_module, 0, NULL, "gtao_depth_r8"); + R_CreateComputePipeline (&vulkan_globals.gtao_depth_msaa_r8_pipeline, gtao_depth_msaa_r8_comp_module, 0, NULL, "gtao_depth_msaa_r8"); R_CreateComputePipeline (&vulkan_globals.gtao_depth_downsample_pipeline, gtao_depth_downsample_comp_module, 0, NULL, "gtao_depth_downsample"); R_CreateComputePipeline (&vulkan_globals.gtao_denoise_pipeline, gtao_denoise_comp_module, 0, NULL, "gtao_denoise"); } @@ -4061,6 +4067,8 @@ static void R_CreateShaderModules () CREATE_SHADER_MODULE (gtao_msaa_comp); CREATE_SHADER_MODULE (gtao_depth_comp); CREATE_SHADER_MODULE (gtao_depth_msaa_comp); + CREATE_SHADER_MODULE (gtao_depth_r8_comp); + CREATE_SHADER_MODULE (gtao_depth_msaa_r8_comp); CREATE_SHADER_MODULE (gtao_depth_downsample_comp); CREATE_SHADER_MODULE (gtao_denoise_comp); CREATE_SHADER_MODULE (cs_tex_warp_comp); @@ -4137,6 +4145,8 @@ static void R_DestroyShaderModules () DESTROY_SHADER_MODULE (gtao_msaa_comp); DESTROY_SHADER_MODULE (gtao_depth_comp); DESTROY_SHADER_MODULE (gtao_depth_msaa_comp); + DESTROY_SHADER_MODULE (gtao_depth_r8_comp); + DESTROY_SHADER_MODULE (gtao_depth_msaa_r8_comp); DESTROY_SHADER_MODULE (gtao_depth_downsample_comp); DESTROY_SHADER_MODULE (gtao_denoise_comp); DESTROY_SHADER_MODULE (cs_tex_warp_comp); @@ -4348,6 +4358,10 @@ void R_DestroyPipelines (void) vulkan_globals.gtao_depth_pipeline.handle = VK_NULL_HANDLE; vkDestroyPipeline (vulkan_globals.device, vulkan_globals.gtao_depth_msaa_pipeline.handle, NULL); vulkan_globals.gtao_depth_msaa_pipeline.handle = VK_NULL_HANDLE; + vkDestroyPipeline (vulkan_globals.device, vulkan_globals.gtao_depth_r8_pipeline.handle, NULL); + vulkan_globals.gtao_depth_r8_pipeline.handle = VK_NULL_HANDLE; + vkDestroyPipeline (vulkan_globals.device, vulkan_globals.gtao_depth_msaa_r8_pipeline.handle, NULL); + vulkan_globals.gtao_depth_msaa_r8_pipeline.handle = VK_NULL_HANDLE; vkDestroyPipeline (vulkan_globals.device, vulkan_globals.gtao_depth_downsample_pipeline.handle, NULL); vulkan_globals.gtao_depth_downsample_pipeline.handle = VK_NULL_HANDLE; vkDestroyPipeline (vulkan_globals.device, vulkan_globals.gtao_denoise_pipeline.handle, NULL); diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index 68b37dd2a..c8d7d9182 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -199,6 +199,7 @@ static VkImage gtao_liquid_mask_buffer; static vulkan_memory_t gtao_liquid_mask_buffer_memory; static VkImageView gtao_liquid_mask_buffer_view; static qboolean gtao_liquid_mask_buffer_initialized; +static VkFormat gtao_liquid_mask_format = VK_FORMAT_R8G8B8A8_UNORM; static qboolean gtao_supported; static vulkan_memory_t color_buffers_memory[NUM_COLOR_BUFFERS]; static VkImageView color_buffers_view[NUM_COLOR_BUFFERS]; @@ -1491,6 +1492,9 @@ static void GL_InitDevice (void) qboolean gtao_image_support = (format_properties.optimalTilingFeatures & gtao_image_features) == gtao_image_features; vkGetPhysicalDeviceFormatProperties (vulkan_physical_device, VK_FORMAT_R32_SFLOAT, &format_properties); qboolean gtao_depth_pyramid_support = (format_properties.optimalTilingFeatures & gtao_image_features) == gtao_image_features; + vkGetPhysicalDeviceFormatProperties (vulkan_physical_device, VK_FORMAT_R8_UNORM, &format_properties); + if ((format_properties.optimalTilingFeatures & gtao_image_features) == gtao_image_features) + gtao_liquid_mask_format = VK_FORMAT_R8_UNORM; gtao_supported = depth_sampling_support && gtao_image_support && gtao_depth_pyramid_support; if (!gtao_supported) Con_Printf ("GTAO unavailable: selected depth or AO image format is not sampleable/storage-capable\n"); @@ -2207,6 +2211,7 @@ static void GL_CreateGTAOBuffer (void) GL_SetObjectName ((uint64_t)gtao_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "GTAO Buffer View"); gtao_buffer_initialized = false; + image_create_info.format = gtao_liquid_mask_format; image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; err = vkCreateImage (vulkan_globals.device, &image_create_info, NULL, >ao_liquid_mask_buffer); if (err != VK_SUCCESS) @@ -2222,6 +2227,7 @@ static void GL_CreateGTAOBuffer (void) err = vkBindImageMemory (vulkan_globals.device, gtao_liquid_mask_buffer, gtao_liquid_mask_buffer_memory.handle, 0); if (err != VK_SUCCESS) Sys_Error ("vkBindImageMemory failed with code %i", (int)err); + image_view_create_info.format = gtao_liquid_mask_format; image_view_create_info.image = gtao_liquid_mask_buffer; err = vkCreateImageView (vulkan_globals.device, &image_view_create_info, NULL, >ao_liquid_mask_buffer_view); if (err != VK_SUCCESS) @@ -2229,6 +2235,8 @@ static void GL_CreateGTAOBuffer (void) GL_SetObjectName ((uint64_t)gtao_liquid_mask_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "GTAO Liquid Mask Buffer View"); gtao_liquid_mask_buffer_initialized = false; + image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; + image_view_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; err = vkCreateImage (vulkan_globals.device, &image_create_info, NULL, >ao_denoise_buffer); if (err != VK_SUCCESS) @@ -4212,7 +4220,13 @@ static void GL_GTAODepthPyramid (cb_context_t *cbx, end_rendering_parms_t *parms vulkan_pipeline_t *pipeline; if (mip == 0) - pipeline = vulkan_globals.sample_count == VK_SAMPLE_COUNT_1_BIT ? &vulkan_globals.gtao_depth_pipeline : &vulkan_globals.gtao_depth_msaa_pipeline; + { + const qboolean single_sample = vulkan_globals.sample_count == VK_SAMPLE_COUNT_1_BIT; + if (gtao_liquid_mask_format == VK_FORMAT_R8_UNORM) + pipeline = single_sample ? &vulkan_globals.gtao_depth_r8_pipeline : &vulkan_globals.gtao_depth_msaa_r8_pipeline; + else + pipeline = single_sample ? &vulkan_globals.gtao_depth_pipeline : &vulkan_globals.gtao_depth_msaa_pipeline; + } else pipeline = &vulkan_globals.gtao_depth_downsample_pipeline; R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); diff --git a/Quake/glquake.h b/Quake/glquake.h index 2a6087759..99761baa6 100644 --- a/Quake/glquake.h +++ b/Quake/glquake.h @@ -456,6 +456,8 @@ typedef struct vulkan_pipeline_t gtao_msaa_pipeline; vulkan_pipeline_t gtao_depth_pipeline; vulkan_pipeline_t gtao_depth_msaa_pipeline; + vulkan_pipeline_t gtao_depth_r8_pipeline; + vulkan_pipeline_t gtao_depth_msaa_r8_pipeline; vulkan_pipeline_t gtao_depth_downsample_pipeline; vulkan_pipeline_t gtao_denoise_pipeline; vulkan_pipeline_t cs_tex_warp_pipeline; diff --git a/Shaders/gtao_depth.comp b/Shaders/gtao_depth.comp index cd9bc633e..2bd04770d 100644 --- a/Shaders/gtao_depth.comp +++ b/Shaders/gtao_depth.comp @@ -14,8 +14,12 @@ layout (set = 0, binding = 2) uniform usampler2D input_stencil_tex; #endif layout (set = 0, binding = 1, r32f) uniform writeonly image2D output_depth_image; #ifndef GTAO_DEPTH_DOWNSAMPLE +#ifdef GTAO_LIQUID_MASK_R8 +layout (set = 0, binding = 3, r8) uniform writeonly image2D output_liquid_mask_image; +#else layout (set = 0, binding = 3, rgba8) uniform writeonly image2D output_liquid_mask_image; #endif +#endif layout (push_constant) uniform DepthConsts { diff --git a/Shaders/shaders.h b/Shaders/shaders.h index e60057451..6c2d72dc7 100644 --- a/Shaders/shaders.h +++ b/Shaders/shaders.h @@ -76,6 +76,8 @@ DECLARE_SHADER_SPV (gtao_comp); DECLARE_SHADER_SPV (gtao_msaa_comp); DECLARE_SHADER_SPV (gtao_depth_comp); DECLARE_SHADER_SPV (gtao_depth_msaa_comp); +DECLARE_SHADER_SPV (gtao_depth_r8_comp); +DECLARE_SHADER_SPV (gtao_depth_msaa_r8_comp); DECLARE_SHADER_SPV (gtao_depth_downsample_comp); DECLARE_SHADER_SPV (gtao_denoise_comp); DECLARE_SHADER_SPV (cs_tex_warp_comp); diff --git a/Windows/VisualStudio/embedded.vcxproj b/Windows/VisualStudio/embedded.vcxproj index 432efade2..0566cb047 100644 --- a/Windows/VisualStudio/embedded.vcxproj +++ b/Windows/VisualStudio/embedded.vcxproj @@ -264,6 +264,10 @@ $(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(Solution "$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth.comp.spv" gtao_depth.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth.comp.c" $(VULKAN_SDK)\bin\glslangValidator.exe -g -V -DGTAO_DEPTH_MSAA=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_msaa.comp.spv" "$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_msaa.comp.spv" gtao_depth_msaa.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_msaa.comp.c" +$(VULKAN_SDK)\bin\glslangValidator.exe -g -V -DGTAO_LIQUID_MASK_R8=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_r8.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_r8.comp.spv" gtao_depth_r8.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_r8.comp.c" +$(VULKAN_SDK)\bin\glslangValidator.exe -g -V -DGTAO_DEPTH_MSAA=1 -DGTAO_LIQUID_MASK_R8=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_msaa_r8.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_msaa_r8.comp.spv" gtao_depth_msaa_r8.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_msaa_r8.comp.c" $(VULKAN_SDK)\bin\glslangValidator.exe -g -V -DGTAO_DEPTH_DOWNSAMPLE=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_downsample.comp.spv" "$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_downsample.comp.spv" gtao_depth_downsample.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_downsample.comp.c" $(VULKAN_SDK)\bin\glslangValidator.exe -V "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth.comp.spv" @@ -272,11 +276,17 @@ $(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(Solution $(VULKAN_SDK)\bin\glslangValidator.exe -V -DGTAO_DEPTH_MSAA=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa.comp.spv" $(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa.comp.spv" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa.comp.spv" "$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa.comp.spv" gtao_depth_msaa.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa.comp.c" +$(VULKAN_SDK)\bin\glslangValidator.exe -V -DGTAO_LIQUID_MASK_R8=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_r8.comp.spv" +$(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_r8.comp.spv" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_r8.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_r8.comp.spv" gtao_depth_r8.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_r8.comp.c" +$(VULKAN_SDK)\bin\glslangValidator.exe -V -DGTAO_DEPTH_MSAA=1 -DGTAO_LIQUID_MASK_R8=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa_r8.comp.spv" +$(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa_r8.comp.spv" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa_r8.comp.spv" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa_r8.comp.spv" gtao_depth_msaa_r8.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa_r8.comp.c" $(VULKAN_SDK)\bin\glslangValidator.exe -V -DGTAO_DEPTH_DOWNSAMPLE=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_downsample.comp.spv" $(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_downsample.comp.spv" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_downsample.comp.spv" "$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_downsample.comp.spv" gtao_depth_downsample.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_downsample.comp.c" - $(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_msaa.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_downsample.comp.c - $(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_downsample.comp.c + $(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_msaa.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_r8.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_msaa_r8.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_depth_downsample.comp.c + $(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_r8.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_msaa_r8.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_depth_downsample.comp.c Document @@ -477,4 +487,4 @@ $(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(Solution - \ No newline at end of file + diff --git a/Windows/VisualStudio/vkquake.vcxproj b/Windows/VisualStudio/vkquake.vcxproj index 728d446db..ea4d9c117 100644 --- a/Windows/VisualStudio/vkquake.vcxproj +++ b/Windows/VisualStudio/vkquake.vcxproj @@ -497,6 +497,16 @@ copy "$(SolutionDir)\..\SDL3\lib64\*.dll" "$(TargetDir)" NotUsing NotUsing + + true + NotUsing + NotUsing + + + true + NotUsing + NotUsing + true NotUsing @@ -822,6 +832,16 @@ copy "$(SolutionDir)\..\SDL3\lib64\*.dll" "$(TargetDir)" NotUsing NotUsing + + true + NotUsing + NotUsing + + + true + NotUsing + NotUsing + true NotUsing diff --git a/Windows/VisualStudio/vkquake.vcxproj.filters b/Windows/VisualStudio/vkquake.vcxproj.filters index 85b973e18..ea549bd62 100644 --- a/Windows/VisualStudio/vkquake.vcxproj.filters +++ b/Windows/VisualStudio/vkquake.vcxproj.filters @@ -409,6 +409,12 @@ Shaders\Debug + + Shaders\Debug + + + Shaders\Debug + Shaders\Debug @@ -526,6 +532,12 @@ Shaders\Release + + Shaders\Release + + + Shaders\Release + Shaders\Release diff --git a/meson.build b/meson.build index a8c8a2126..525b77f63 100644 --- a/meson.build +++ b/meson.build @@ -154,6 +154,8 @@ shader_variants = [ ['Shaders/gtao.comp', 'gtao_msaa.comp', ['-DGTAO_MSAA=1']], ['Shaders/gtao_depth.comp', 'gtao_depth.comp', []], ['Shaders/gtao_depth.comp', 'gtao_depth_msaa.comp', ['-DGTAO_DEPTH_MSAA=1']], + ['Shaders/gtao_depth.comp', 'gtao_depth_r8.comp', ['-DGTAO_LIQUID_MASK_R8=1']], + ['Shaders/gtao_depth.comp', 'gtao_depth_msaa_r8.comp', ['-DGTAO_DEPTH_MSAA=1', '-DGTAO_LIQUID_MASK_R8=1']], ['Shaders/gtao_depth.comp', 'gtao_depth_downsample.comp', ['-DGTAO_DEPTH_DOWNSAMPLE=1']], ['Shaders/gtao_denoise.comp', 'gtao_denoise.comp', []], ['Shaders/update_lightmap.comp', 'update_lightmap_8bit.comp', []], From b959b9d15aae65747ef98255018b48d9bf843544 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 17:57:14 -0500 Subject: [PATCH 07/26] Trim GTAO descriptor and barrier overhead --- Quake/gl_rmisc.c | 10 +++------- Quake/gl_vidsdl.c | 21 +++++++++------------ Shaders/gtao_denoise.comp | 3 +-- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index 45729c299..36ff920f4 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -1580,23 +1580,19 @@ void R_CreateDescriptorSetLayouts () } { - ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, bindings, 3); + ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, bindings, 2); bindings[0].binding = 0; bindings[0].descriptorCount = 1; bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; bindings[1].binding = 1; bindings[1].descriptorCount = 1; - bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - bindings[2].binding = 2; - bindings[2].descriptorCount = 1; - bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - bindings[2].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; descriptor_set_layout_create_info.bindingCount = countof (bindings); descriptor_set_layout_create_info.pBindings = bindings; memset (&vulkan_globals.gtao_denoise_set_layout, 0, sizeof (vulkan_globals.gtao_denoise_set_layout)); - vulkan_globals.gtao_denoise_set_layout.num_combined_image_samplers = 2; + vulkan_globals.gtao_denoise_set_layout.num_combined_image_samplers = 1; vulkan_globals.gtao_denoise_set_layout.num_storage_images = 1; err = vkCreateDescriptorSetLayout (vulkan_globals.device, &descriptor_set_layout_create_info, NULL, &vulkan_globals.gtao_denoise_set_layout.handle); if (err != VK_SUCCESS) diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index c8d7d9182..c5b20246f 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -3019,23 +3019,20 @@ void GL_UpdateDescriptorSets (void) for (uint32_t pass = 0; pass < 2; ++pass) { vulkan_globals.gtao_denoise_desc_sets[pass] = R_AllocateDescriptorSet (&vulkan_globals.gtao_denoise_set_layout); - ZEROED_STRUCT_ARRAY (VkDescriptorImageInfo, image_infos, 3); + ZEROED_STRUCT_ARRAY (VkDescriptorImageInfo, image_infos, 2); image_infos[0].imageView = pass == 0 ? gtao_buffer_view : gtao_denoise_buffer_view; image_infos[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image_infos[0].sampler = vulkan_globals.point_sampler; - image_infos[1].imageView = gtao_depth_pyramid_view; - image_infos[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - image_infos[1].sampler = vulkan_globals.point_sampler; - image_infos[2].imageView = pass == 0 ? gtao_denoise_buffer_view : gtao_buffer_view; - image_infos[2].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, writes, 3); - for (uint32_t binding = 0; binding < 3; ++binding) + image_infos[1].imageView = pass == 0 ? gtao_denoise_buffer_view : gtao_buffer_view; + image_infos[1].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, writes, 2); + for (uint32_t binding = 0; binding < 2; ++binding) { writes[binding].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[binding].dstSet = vulkan_globals.gtao_denoise_desc_sets[pass]; writes[binding].dstBinding = binding; writes[binding].descriptorCount = 1; - writes[binding].descriptorType = binding == 2 ? VK_DESCRIPTOR_TYPE_STORAGE_IMAGE : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writes[binding].descriptorType = binding == 1 ? VK_DESCRIPTOR_TYPE_STORAGE_IMAGE : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[binding].pImageInfo = &image_infos[binding]; } vkUpdateDescriptorSets (vulkan_globals.device, countof (writes), writes, 0, NULL); @@ -4364,6 +4361,8 @@ GL_GTAO static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) { R_BeginDebugUtilsLabel (cbx, "GTAO"); + const qboolean store_temporal_history = r_gtao_temporal.value > 0.0f && r_gtao_bent_normals.value <= 0.0f && + (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 6 || (int)r_gtao_debug.value == 7); ZEROED_STRUCT_ARRAY (VkImageMemoryBarrier, image_barriers, 3); @@ -4405,7 +4404,7 @@ static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) vkCmdPipelineBarrier ( cbx->cb, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, countof (image_barriers), image_barriers); + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, store_temporal_history ? countof (image_barriers) : 2, image_barriers); GL_GTAODepthPyramid (cbx, parms); GL_SetCanvas (cbx, CANVAS_NONE); @@ -4484,8 +4483,6 @@ static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) const uint32_t gtao_stride = r_gtao_halfres.value > 0.0f ? 2u : 1u; vkCmdDispatch (cbx->cb, (parms->vid_width + 8 * gtao_stride - 1) / (8 * gtao_stride), (parms->vid_height + 8 * gtao_stride - 1) / (8 * gtao_stride), 1); - const qboolean store_temporal_history = r_gtao_temporal.value > 0.0f && r_gtao_bent_normals.value <= 0.0f && - (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 6 || (int)r_gtao_debug.value == 7); if (store_temporal_history) { ZEROED_STRUCT_ARRAY (VkImageMemoryBarrier, copy_barriers, 2); diff --git a/Shaders/gtao_denoise.comp b/Shaders/gtao_denoise.comp index 811f0bb86..ae39fd967 100644 --- a/Shaders/gtao_denoise.comp +++ b/Shaders/gtao_denoise.comp @@ -3,8 +3,7 @@ #extension GL_ARB_shading_language_420pack : enable layout (set = 0, binding = 0) uniform sampler2D input_ao_tex; -layout (set = 0, binding = 1) uniform sampler2D view_depth_tex; -layout (set = 0, binding = 2, rgba8) uniform writeonly image2D output_ao_image; +layout (set = 0, binding = 1, rgba8) uniform writeonly image2D output_ao_image; layout (push_constant) uniform DenoiseConsts { From 75b6879c9e0275a0468d20d9819bb199713cc2f9 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 19:08:00 -0500 Subject: [PATCH 08/26] Restore baseline renderer path when GTAO is disabled --- Quake/gl_rmain.c | 30 +++++++----- Quake/gl_rmisc.c | 80 +++++++++++++++++++------------ Quake/gl_vidsdl.c | 98 +++++++++++++++++++++++--------------- Quake/glquake.h | 6 +++ Quake/r_alias.c | 2 +- Quake/r_brush.c | 5 +- Quake/r_world.c | 5 +- Shaders/screen_effects.inc | 4 +- 8 files changed, 143 insertions(+), 87 deletions(-) diff --git a/Quake/gl_rmain.c b/Quake/gl_rmain.c index 723a0443e..31106600e 100644 --- a/Quake/gl_rmain.c +++ b/Quake/gl_rmain.c @@ -440,15 +440,18 @@ static void R_SetupViewBeforeMark (void *unused) R_SetFrustum (r_fovx, r_fovy); // johnfitz -- use r_fov* vars R_SetupMatrices (); - vulkan_globals.gtao_viewport_x = r_refdef.vrect.x; - vulkan_globals.gtao_viewport_y = r_refdef.vrect.y; - vulkan_globals.gtao_viewport_width = r_refdef.vrect.width; - vulkan_globals.gtao_viewport_height = r_refdef.vrect.height; - vulkan_globals.gtao_projection[0] = 1.0f / vulkan_globals.projection_matrix[0]; - vulkan_globals.gtao_projection[1] = 1.0f / (-vulkan_globals.projection_matrix[5]); - vulkan_globals.gtao_projection[2] = vulkan_globals.projection_matrix[10]; - vulkan_globals.gtao_projection[3] = vulkan_globals.projection_matrix[14]; - memcpy (vulkan_globals.gtao_view_projection, vulkan_globals.view_projection_matrix, sizeof (vulkan_globals.gtao_view_projection)); + if (R_GTAOEnabled ()) + { + vulkan_globals.gtao_viewport_x = r_refdef.vrect.x; + vulkan_globals.gtao_viewport_y = r_refdef.vrect.y; + vulkan_globals.gtao_viewport_width = r_refdef.vrect.width; + vulkan_globals.gtao_viewport_height = r_refdef.vrect.height; + vulkan_globals.gtao_projection[0] = 1.0f / vulkan_globals.projection_matrix[0]; + vulkan_globals.gtao_projection[1] = 1.0f / (-vulkan_globals.projection_matrix[5]); + vulkan_globals.gtao_projection[2] = vulkan_globals.projection_matrix[10]; + vulkan_globals.gtao_projection[3] = vulkan_globals.projection_matrix[14]; + memcpy (vulkan_globals.gtao_view_projection, vulkan_globals.view_projection_matrix, sizeof (vulkan_globals.gtao_view_projection)); + } // johnfitz -- cheat-protect some draw modes r_fullbright_cheatsafe = false; @@ -1185,9 +1188,12 @@ void R_RenderView (qboolean use_tasks, task_handle_t begin_rendering_task, task_ { task_handle_t before_mark = Task_AllocateAndAssignFunc (R_SetupViewBeforeMark, NULL, 0); Task_AddDependency (setup_frame_task, before_mark); - // Keep frame-local projection/viewport state from being overwritten while - // the previous frame's asynchronous end-render task is consuming it. - Task_AddDependency (begin_rendering_task, before_mark); + if (R_GTAOEnabled ()) + { + // Keep frame-local projection/viewport state from being overwritten while + // the previous frame's asynchronous end-render task is consuming it. + Task_AddDependency (begin_rendering_task, before_mark); + } task_handle_t store_efrags = INVALID_TASK_HANDLE; task_handle_t cull_surfaces = INVALID_TASK_HANDLE; diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index 36ff920f4..b86d75235 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -109,6 +109,7 @@ atomic_uint64_t total_host_vulkan_allocation_size; qboolean use_simd; oit_mode_t frame_oit_mode; +qboolean frame_gtao_enabled; static SDL_Mutex *vertex_allocate_mutex; static SDL_Mutex *index_allocate_mutex; @@ -3340,14 +3341,17 @@ static void R_CreateSkyPipelines () base.depth_stencil_state.depthTestEnable = VK_TRUE; base.depth_stencil_state.depthWriteEnable = VK_TRUE; - base.depth_stencil_state.stencilTestEnable = VK_TRUE; - base.depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS; - base.depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP; - base.depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP; - base.depth_stencil_state.front.passOp = VK_STENCIL_OP_REPLACE; - base.depth_stencil_state.front.compareMask = 0x1; - base.depth_stencil_state.front.writeMask = 0x1; - base.depth_stencil_state.front.reference = 0x1; + if (R_GTAOEnabled ()) + { + base.depth_stencil_state.stencilTestEnable = VK_TRUE; + base.depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS; + base.depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP; + base.depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP; + base.depth_stencil_state.front.passOp = VK_STENCIL_OP_REPLACE; + base.depth_stencil_state.front.compareMask = STENCIL_MASK_SKY; + base.depth_stencil_state.front.writeMask = STENCIL_MASK_SKY; + base.depth_stencil_state.front.reference = STENCIL_MASK_SKY; + } pipeline_create_infos_t infos; for (int variant = 0; variant < MAIN_RENDER_PASS_VARIANT_COUNT; ++variant) @@ -3365,7 +3369,7 @@ static void R_CreateSkyPipelines () infos.depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP; infos.depth_stencil_state.front.passOp = VK_STENCIL_OP_REPLACE; infos.depth_stencil_state.front.compareMask = 0xFF; - infos.depth_stencil_state.front.writeMask = 0x1; + infos.depth_stencil_state.front.writeMask = R_GTAOEnabled () ? STENCIL_MASK_SKY : 0xFF; infos.depth_stencil_state.front.reference = 0x1; infos.blend_attachment_states[0].colorWriteMask = 0; // We only want to write stencil R_CreateGraphicsPipeline ( @@ -3571,7 +3575,7 @@ static void R_CreateWorldPipelines () base.shader_stages[1].pSpecializationInfo = &specialization_info; pipeline_create_infos_t infos; - for (int liquid = 0; liquid < 2; ++liquid) + for (int liquid = 0; liquid < (R_GTAOEnabled () ? 2 : 1); ++liquid) { for (int alpha_blend = 0; alpha_blend < 2; ++alpha_blend) { @@ -3702,10 +3706,13 @@ static void R_CreateAliasPipelines () infos.depth_stencil_state.depthWriteEnable = alpha_blend ? VK_FALSE : VK_TRUE; R_CreateGraphicsPipeline ( &vulkan_globals.alias_pipelines[variant][pipeline_index], &infos, layout, va (variant ? "alias_main_oit %d" : "alias %d", pipeline_index)); - R_SetViewModelStencilState (&infos); - R_CreateGraphicsPipeline ( - &vulkan_globals.alias_viewmodel_pipelines[variant][pipeline_index], &infos, layout, - va (variant ? "alias_viewmodel_main_oit %d" : "alias_viewmodel %d", pipeline_index)); + if (R_GTAOEnabled ()) + { + R_SetViewModelStencilState (&infos); + R_CreateGraphicsPipeline ( + &vulkan_globals.alias_viewmodel_pipelines[variant][pipeline_index], &infos, layout, + va (variant ? "alias_viewmodel_main_oit %d" : "alias_viewmodel %d", pipeline_index)); + } } if (alpha_blend) @@ -3805,10 +3812,13 @@ static void R_CreateMD5Pipelines () infos.depth_stencil_state.depthWriteEnable = alpha_blend ? VK_FALSE : VK_TRUE; R_CreateGraphicsPipeline ( &vulkan_globals.md5_pipelines[variant][pipeline_index], &infos, layout, va (variant ? "md5_main_oit %d" : "md5 %d", pipeline_index)); - R_SetViewModelStencilState (&infos); - R_CreateGraphicsPipeline ( - &vulkan_globals.md5_viewmodel_pipelines[variant][pipeline_index], &infos, layout, - va (variant ? "md5_viewmodel_main_oit %d" : "md5_viewmodel %d", pipeline_index)); + if (R_GTAOEnabled ()) + { + R_SetViewModelStencilState (&infos); + R_CreateGraphicsPipeline ( + &vulkan_globals.md5_viewmodel_pipelines[variant][pipeline_index], &infos, layout, + va (variant ? "md5_viewmodel_main_oit %d" : "md5_viewmodel %d", pipeline_index)); + } } if (alpha_blend) @@ -3933,17 +3943,23 @@ R_CreateScreenEffectsPipelines static void R_CreateScreenEffectsPipelines () { const qboolean ten_bit = vulkan_globals.color_format == VK_FORMAT_A2B10G10R10_UNORM_PACK32; + const VkBool32 gtao_enabled = R_GTAOEnabled () ? VK_TRUE : VK_FALSE; + const VkSpecializationMapEntry specialization_entry = {0, 0, sizeof (gtao_enabled)}; + const VkSpecializationInfo specialization_info = {1, &specialization_entry, sizeof (gtao_enabled), >ao_enabled}; R_CreateComputePipeline ( - &vulkan_globals.screen_effects_pipeline, ten_bit ? screen_effects_10bit_comp_module : screen_effects_8bit_comp_module, 0, NULL, "screen_effects"); + &vulkan_globals.screen_effects_pipeline, ten_bit ? screen_effects_10bit_comp_module : screen_effects_8bit_comp_module, 0, &specialization_info, + "screen_effects"); R_CreateComputePipeline ( - &vulkan_globals.screen_effects_scale_pipeline, ten_bit ? screen_effects_10bit_scale_comp_module : screen_effects_8bit_scale_comp_module, 0, NULL, + &vulkan_globals.screen_effects_scale_pipeline, ten_bit ? screen_effects_10bit_scale_comp_module : screen_effects_8bit_scale_comp_module, 0, + &specialization_info, "screen_effects_scale"); if (vulkan_globals.screen_effects_sops) R_CreateComputePipeline ( &vulkan_globals.screen_effects_scale_sops_pipeline, ten_bit ? screen_effects_10bit_scale_sops_comp_module : screen_effects_8bit_scale_sops_comp_module, - VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT | VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT, NULL, + VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT | VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT, + &specialization_info, "screen_effects_scale_sops"); } @@ -4059,14 +4075,17 @@ static void R_CreateShaderModules () CREATE_SHADER_MODULE (screen_effects_10bit_comp); CREATE_SHADER_MODULE (screen_effects_10bit_scale_comp); CREATE_SHADER_MODULE_COND (screen_effects_10bit_scale_sops_comp, vulkan_globals.screen_effects_sops); - CREATE_SHADER_MODULE (gtao_comp); - CREATE_SHADER_MODULE (gtao_msaa_comp); - CREATE_SHADER_MODULE (gtao_depth_comp); - CREATE_SHADER_MODULE (gtao_depth_msaa_comp); - CREATE_SHADER_MODULE (gtao_depth_r8_comp); - CREATE_SHADER_MODULE (gtao_depth_msaa_r8_comp); - CREATE_SHADER_MODULE (gtao_depth_downsample_comp); - CREATE_SHADER_MODULE (gtao_denoise_comp); + if (R_GTAOEnabled ()) + { + CREATE_SHADER_MODULE (gtao_comp); + CREATE_SHADER_MODULE (gtao_msaa_comp); + CREATE_SHADER_MODULE (gtao_depth_comp); + CREATE_SHADER_MODULE (gtao_depth_msaa_comp); + CREATE_SHADER_MODULE (gtao_depth_r8_comp); + CREATE_SHADER_MODULE (gtao_depth_msaa_r8_comp); + CREATE_SHADER_MODULE (gtao_depth_downsample_comp); + CREATE_SHADER_MODULE (gtao_denoise_comp); + } CREATE_SHADER_MODULE (cs_tex_warp_comp); CREATE_SHADER_MODULE (indirect_comp); CREATE_SHADER_MODULE (indirect_clear_comp); @@ -4183,7 +4202,8 @@ void R_CreatePipelines () R_CreateMD5Pipelines (); R_CreatePostprocessPipelines (); R_CreateScreenEffectsPipelines (); - R_CreateGTAOPipelines (); + if (R_GTAOEnabled ()) + R_CreateGTAOPipelines (); R_CreateUpdateLightmapPipelines (); R_CreateIndirectComputePipelines (); R_CreateRayDebugPipelines (); diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index c5b20246f..0c30b76e8 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -96,7 +96,6 @@ static void GL_CreateGTAOBuffer (void); static void GL_DestroyOITBuffers (void); static void GL_DestroyMainRenderPasses (void); static void GL_DestroyRenderResources (void); -static qboolean GL_GTAOEnabled (void); viddef_t vid; // global video state modestate_t modestate = MS_UNINIT; @@ -1661,9 +1660,9 @@ static void GL_CreateRenderPasses () attachment_descriptions[1].samples = vulkan_globals.sample_count; attachment_descriptions[1].format = vulkan_globals.depth_format; attachment_descriptions[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachment_descriptions[1].storeOp = (gtao_supported || use_oit) ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment_descriptions[1].storeOp = (R_GTAOEnabled () || use_oit) ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; attachment_descriptions[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachment_descriptions[1].stencilStoreOp = gtao_supported ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment_descriptions[1].stencilStoreOp = R_GTAOEnabled () ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE; if (resolve) { @@ -2080,7 +2079,7 @@ static void GL_CreateDepthBuffer (void) image_create_info.arrayLayers = 1; image_create_info.samples = vulkan_globals.sample_count; image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; - image_create_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | (gtao_supported ? VK_IMAGE_USAGE_SAMPLED_BIT : 0); + image_create_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | (R_GTAOEnabled () ? VK_IMAGE_USAGE_SAMPLED_BIT : 0); assert (depth_buffer == VK_NULL_HANDLE); err = vkCreateImage (vulkan_globals.device, &image_create_info, NULL, &depth_buffer); @@ -2131,12 +2130,15 @@ static void GL_CreateDepthBuffer (void) GL_SetObjectName ((uint64_t)depth_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "Depth Buffer View"); - image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; - assert (stencil_buffer_view == VK_NULL_HANDLE); - err = vkCreateImageView (vulkan_globals.device, &image_view_create_info, NULL, &stencil_buffer_view); - if (err != VK_SUCCESS) - Sys_Error ("vkCreateImageView failed with code %i", (int)err); - GL_SetObjectName ((uint64_t)stencil_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "Stencil Buffer View"); + if (R_GTAOEnabled ()) + { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + assert (stencil_buffer_view == VK_NULL_HANDLE); + err = vkCreateImageView (vulkan_globals.device, &image_view_create_info, NULL, &stencil_buffer_view); + if (err != VK_SUCCESS) + Sys_Error ("vkCreateImageView failed with code %i", (int)err); + GL_SetObjectName ((uint64_t)stencil_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "Stencil Buffer View"); + } } /* @@ -2916,7 +2918,7 @@ void GL_UpdateDescriptorSets (void) blue_noise_image_info.sampler = vulkan_globals.linear_sampler; VkImageView gtao_input_view = color_buffers_view[1]; - if (gtao_supported) + if (R_GTAOEnabled ()) { vulkan_globals.gtao_desc_set = R_AllocateDescriptorSet (&vulkan_globals.gtao_set_layout); @@ -3045,15 +3047,15 @@ void GL_UpdateDescriptorSets (void) gtao_input_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; gtao_input_image_info.sampler = vulkan_globals.point_sampler; ZEROED_STRUCT (VkDescriptorImageInfo, gtao_resolve_depth_image_info); - gtao_resolve_depth_image_info.imageView = gtao_supported ? gtao_depth_pyramid_view : color_buffers_view[1]; + gtao_resolve_depth_image_info.imageView = R_GTAOEnabled () ? gtao_depth_pyramid_view : color_buffers_view[1]; gtao_resolve_depth_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; gtao_resolve_depth_image_info.sampler = vulkan_globals.point_sampler; ZEROED_STRUCT (VkDescriptorImageInfo, gtao_denoise_image_info); - gtao_denoise_image_info.imageView = gtao_supported ? gtao_denoise_buffer_view : color_buffers_view[1]; + gtao_denoise_image_info.imageView = R_GTAOEnabled () ? gtao_denoise_buffer_view : color_buffers_view[1]; gtao_denoise_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; gtao_denoise_image_info.sampler = vulkan_globals.point_sampler; ZEROED_STRUCT (VkDescriptorImageInfo, gtao_liquid_mask_image_info); - gtao_liquid_mask_image_info.imageView = gtao_supported ? gtao_liquid_mask_buffer_view : color_buffers_view[1]; + gtao_liquid_mask_image_info.imageView = R_GTAOEnabled () ? gtao_liquid_mask_buffer_view : color_buffers_view[1]; gtao_liquid_mask_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; gtao_liquid_mask_image_info.sampler = vulkan_globals.point_sampler; @@ -3572,7 +3574,7 @@ static void GL_CreateRenderResources (void) GL_CreateColorBuffer (); GL_CreateDepthBuffer (); - if (gtao_supported) + if (R_GTAOEnabled ()) GL_CreateGTAOBuffer (); GL_CreateRenderPasses (); GL_CreateFrameBuffers (); @@ -3837,7 +3839,8 @@ void GL_BeginRenderingTask (void *unused) if (scbx_index <= SCBX_OIT_RESOLVE) { - const int main_render_pass_stencil = (Sky_NeedStencil () || GL_GTAOEnabled ()) ? MAIN_RENDER_PASS_STENCIL_CLEAR : MAIN_RENDER_PASS_NO_STENCIL; + const qboolean gtao_enabled = R_GTAOEnabled (); + const int main_render_pass_stencil = (Sky_NeedStencil () || gtao_enabled) ? MAIN_RENDER_PASS_STENCIL_CLEAR : MAIN_RENDER_PASS_NO_STENCIL; cbx->render_pass = vulkan_globals.main_render_pass [R_UseMBOIT () ? MAIN_RENDER_PASS_MBOIT : R_UseWBOIT () ? MAIN_RENDER_PASS_OIT @@ -3976,13 +3979,24 @@ qboolean GL_BeginRendering (qboolean use_tasks, task_handle_t *begin_rendering_t const int requested_oit_value = (int)r_oit.value; const oit_mode_t requested_oit_mode = GL_FrameOITModeForCvarValue (requested_oit_value); const qboolean oit_mode_changed = (requested_oit_mode != frame_oit_mode); - frame_oit_mode = requested_oit_mode; + const qboolean requested_gtao_enabled = gtao_supported && r_gtao.value > 0.0f; + const qboolean gtao_mode_changed = requested_gtao_enabled != frame_gtao_enabled; - if (vid.restart_next_frame || (render_resources_created && oit_mode_changed)) + if (vid.restart_next_frame || (render_resources_created && (oit_mode_changed || gtao_mode_changed))) { + // Finish the previous frame before changing the configuration it observes. + // VID_Restart synchronizes too, but the frame flags must remain unchanged + // until that synchronization is complete. + GL_SynchronizeEndRenderingTask (); + frame_oit_mode = requested_oit_mode; + frame_gtao_enabled = requested_gtao_enabled; VID_Restart (false); vid.restart_next_frame = false; - frame_oit_mode = GL_FrameOITModeForCvarValue (requested_oit_value); + } + else + { + frame_oit_mode = requested_oit_mode; + frame_gtao_enabled = requested_gtao_enabled; } if (!render_resources_created) @@ -4163,11 +4177,6 @@ typedef struct end_rendering_parms_s #define SCREEN_EFFECT_FLAG_MENU 0x10 #define SCREEN_EFFECT_FLAG_GTAO 0x20 -static qboolean GL_GTAOEnabled (void) -{ - return gtao_supported && (r_gtao.value > 0.0f || r_gtao_debug.value > 0.0f); -} - typedef struct gtao_depth_constants_s { uint32_t width; @@ -4648,10 +4657,10 @@ static void GL_ScreenEffects (cb_context_t *cbx, qboolean enabled, end_rendering screen_effect_flags |= SCREEN_EFFECT_FLAG_PALETTIZE; if (parms->menu) screen_effect_flags |= SCREEN_EFFECT_FLAG_MENU; - if (GL_GTAOEnabled ()) + if (R_GTAOEnabled ()) screen_effect_flags |= SCREEN_EFFECT_FLAG_GTAO; - const screen_effect_constants_t push_constants = { + screen_effect_constants_t push_constants = { parms->vid_width - 1, parms->vid_height - 1, 1.0f / (float)parms->vid_width, @@ -4663,17 +4672,27 @@ static void GL_ScreenEffects (cb_context_t *cbx, qboolean enabled, end_rendering (float)parms->v_blend[1] / 255.0f, (float)parms->v_blend[2] / 255.0f, (float)parms->v_blend[3] / 255.0f, - r_gtao_strength.value, - (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 8), - (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 7) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u, - CLAMP (0.0f, r_gtao_multibounce.value, 1.0f), - r_gtao_halfres.value > 0.0f ? 1u : 0u, - CLAMP (0.0f, r_gtao_liquid_water.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_WATER), 1.0f), - CLAMP (0.0f, r_gtao_liquid_slime.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_SLIME), 1.0f), - CLAMP (0.0f, r_gtao_liquid_lava.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_LAVA), 1.0f), - CLAMP (0.0f, r_gtao_liquid_tele.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_TELE), 1.0f), }; - R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (push_constants), &push_constants); + uint32_t push_constant_size = offsetof (screen_effect_constants_t, gtao_strength); + if (R_GTAOEnabled ()) + { + push_constants.gtao_strength = r_gtao_strength.value; + push_constants.gtao_debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 8); + push_constants.gtao_denoise = + (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 7) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u; + push_constants.gtao_multibounce = CLAMP (0.0f, r_gtao_multibounce.value, 1.0f); + push_constants.gtao_halfres = r_gtao_halfres.value > 0.0f ? 1u : 0u; + push_constants.gtao_liquid_water = + CLAMP (0.0f, r_gtao_liquid_water.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_WATER), 1.0f); + push_constants.gtao_liquid_slime = + CLAMP (0.0f, r_gtao_liquid_slime.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_SLIME), 1.0f); + push_constants.gtao_liquid_lava = + CLAMP (0.0f, r_gtao_liquid_lava.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_LAVA), 1.0f); + push_constants.gtao_liquid_tele = + CLAMP (0.0f, r_gtao_liquid_tele.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_TELE), 1.0f); + push_constant_size = sizeof (push_constants); + } + R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constant_size, &push_constants); } #if defined(_DEBUG) else @@ -4978,7 +4997,7 @@ static void GL_EndRenderingTask (end_rendering_parms_t *parms) depth_clear_value.depthStencil.stencil = 0; const qboolean screen_effects = - parms->render_warp || (parms->render_scale >= 2) || parms->vid_palettize || (parms->polyblend && parms->v_blend[3]) || parms->menu || parms->ray_debug || GL_GTAOEnabled (); + parms->render_warp || (parms->render_scale >= 2) || parms->vid_palettize || (parms->polyblend && parms->v_blend[3]) || parms->menu || parms->ray_debug || R_GTAOEnabled (); { const qboolean resolve = (vulkan_globals.sample_count != VK_SAMPLE_COUNT_1_BIT); const qboolean use_mboit = parms->use_mboit; @@ -5017,11 +5036,12 @@ static void GL_EndRenderingTask (end_rendering_parms_t *parms) } ZEROED_STRUCT (VkRenderPassBeginInfo, render_pass_begin_info); render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + const qboolean gtao_enabled = R_GTAOEnabled (); render_pass_begin_info.renderPass = vulkan_globals.main_render_pass [use_mboit ? MAIN_RENDER_PASS_MBOIT : use_wboit ? MAIN_RENDER_PASS_OIT - : MAIN_RENDER_PASS_STANDARD][(Sky_NeedStencil () || GL_GTAOEnabled ()) ? MAIN_RENDER_PASS_STENCIL_CLEAR : MAIN_RENDER_PASS_NO_STENCIL]; + : MAIN_RENDER_PASS_STANDARD][(Sky_NeedStencil () || gtao_enabled) ? MAIN_RENDER_PASS_STENCIL_CLEAR : MAIN_RENDER_PASS_NO_STENCIL]; render_pass_begin_info.framebuffer = main_framebuffers[screen_effects ? 1 : 0]; render_pass_begin_info.renderArea = render_area; render_pass_begin_info.clearValueCount = use_mboit ? (resolve ? 6 : 5) : resolve ? (use_wboit ? 5 : 3) : (use_wboit ? 4 : 2); @@ -5055,7 +5075,7 @@ static void GL_EndRenderingTask (end_rendering_parms_t *parms) vkCmdEndRenderPass (render_passes_cb); } - if (GL_GTAOEnabled ()) + if (R_GTAOEnabled ()) GL_GTAO (&vulkan_globals.primary_cb_contexts[PCBX_RENDER_PASSES], parms); GL_ScreenEffects (&vulkan_globals.primary_cb_contexts[PCBX_RENDER_PASSES], screen_effects, parms); diff --git a/Quake/glquake.h b/Quake/glquake.h index 99761baa6..c319fc253 100644 --- a/Quake/glquake.h +++ b/Quake/glquake.h @@ -280,6 +280,7 @@ typedef enum } oit_mode_t; extern oit_mode_t frame_oit_mode; +extern qboolean frame_gtao_enabled; static inline qboolean R_UseOIT (void) { @@ -296,6 +297,11 @@ static inline qboolean R_UseMBOIT (void) return frame_oit_mode == OIT_MODE_MBOIT; } +static inline qboolean R_GTAOEnabled (void) +{ + return frame_gtao_enabled; +} + static inline main_render_pass_variant_t R_MainPassPipelineVariant (int render_pass_index) { if (render_pass_index == RENDER_PASS_INDEX_MAIN_OIT) diff --git a/Quake/r_alias.c b/Quake/r_alias.c index e798614b3..6107e8101 100644 --- a/Quake/r_alias.c +++ b/Quake/r_alias.c @@ -110,7 +110,7 @@ static void GL_DrawAliasFrame ( if (oit_pass && (showtris != 0 || !has_alpha)) return; const main_render_pass_variant_t main_variant = R_MainPassPipelineVariant (cbx->render_pass_index); - const qboolean viewmodel = e == &cl.viewent && showtris == 0 && !oit_pass; + const qboolean viewmodel = R_GTAOEnabled () && e == &cl.viewent && showtris == 0 && !oit_pass; if (paliashdr->poseverttype == PV_MD5) pipeline = R_PipelineForRenderPass ( diff --git a/Quake/r_brush.c b/Quake/r_brush.c index a009dd576..963197399 100644 --- a/Quake/r_brush.c +++ b/Quake/r_brush.c @@ -976,16 +976,17 @@ void R_DrawIndirectBrushes (cb_context_t *cbx, qboolean draw_water, qboolean tra { const qboolean alpha_test = texture->type == TEXTYPE_CUTOUT; const qboolean alpha_blend = alpha < 1.0f; + const qboolean gtao_liquid = draw_water && R_GTAOEnabled (); int pipeline_index = (fullbright_enabled ? 1 : 0) + (alpha_test ? 2 : 0) + (alpha_blend ? 4 : 0) + (vid_filter.value != 0 && vid_palettize.value != 0 ? 8 : 0); - if (draw_water) + if (gtao_liquid) pipeline_index |= WORLD_PIPELINE_LIQUID_BIT; vulkan_pipeline_t pipeline = R_PipelineForRenderPass ( cbx->render_pass_index, vulkan_globals.world_pipelines[R_MainPassPipelineVariant (cbx->render_pass_index)][pipeline_index], vulkan_globals.world_wboit_pipelines[pipeline_index], vulkan_globals.world_mboit_moment_pipelines[pipeline_index], vulkan_globals.world_mboit_composite_pipelines[pipeline_index]); R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - if (draw_water) + if (gtao_liquid) { const uint32_t stencil_reference = texture->type == TEXTYPE_WATER ? STENCIL_MASK_WATER : texture->type == TEXTYPE_SLIME ? STENCIL_MASK_SLIME diff --git a/Quake/r_world.c b/Quake/r_world.c index 964f982bd..c40bc0256 100644 --- a/Quake/r_world.c +++ b/Quake/r_world.c @@ -1158,16 +1158,17 @@ static void R_FlushBatch ( { if (cbx->num_vbo_indices > 0) { + const qboolean gtao_liquid = TEXTYPE_ISLIQUID (liquid_type) && R_GTAOEnabled (); int pipeline_index = (fullbright_enabled ? 1 : 0) + (alpha_test ? 2 : 0) + (alpha_blend ? 4 : 0) + (vid_filter.value != 0 && vid_palettize.value != 0 ? 8 : 0); - if (TEXTYPE_ISLIQUID (liquid_type)) + if (gtao_liquid) pipeline_index |= WORLD_PIPELINE_LIQUID_BIT; vulkan_pipeline_t pipeline = R_PipelineForRenderPass ( cbx->render_pass_index, vulkan_globals.world_pipelines[R_MainPassPipelineVariant (cbx->render_pass_index)][pipeline_index], vulkan_globals.world_wboit_pipelines[pipeline_index], vulkan_globals.world_mboit_moment_pipelines[pipeline_index], vulkan_globals.world_mboit_composite_pipelines[pipeline_index]); R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); - if (TEXTYPE_ISLIQUID (liquid_type)) + if (gtao_liquid) { const uint32_t stencil_reference = liquid_type == TEXTYPE_WATER ? STENCIL_MASK_WATER : liquid_type == TEXTYPE_SLIME ? STENCIL_MASK_SLIME diff --git a/Shaders/screen_effects.inc b/Shaders/screen_effects.inc index 47db2fe5f..27bbb9281 100644 --- a/Shaders/screen_effects.inc +++ b/Shaders/screen_effects.inc @@ -198,6 +198,8 @@ vec3 gtao_multibounce_visibility (float visibility, vec3 albedo) #define SCREEN_EFFECT_FLAG_MENU 0x10 #define SCREEN_EFFECT_FLAG_GTAO 0x20 +layout (constant_id = 0) const bool GTAO_ENABLED = true; + #if defined(SCALING) // Vulkan guarantees 16384 bytes of shared memory, so host doesn't need to check shared uint group_red[16]; @@ -282,7 +284,7 @@ void main () else color = texelFetch (input_tex, ivec2 (min (push_constants.clamp_size.x, pos_x), min (push_constants.clamp_size.y, pos_y)), 0); - [[branch]] if ((push_constants.flags & SCREEN_EFFECT_FLAG_GTAO) != 0) + [[branch]] if (GTAO_ENABLED && (push_constants.flags & SCREEN_EFFECT_FLAG_GTAO) != 0) { const ivec2 ao_pixel = ivec2 (min (push_constants.clamp_size.x, pos_x), min (push_constants.clamp_size.y, pos_y)); const bool gtao_warped = (push_constants.flags & SCREEN_EFFECT_FLAG_WATER_WARP) != 0; From 25701ac55268067e23f3ff4b897604048abd5c22 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 19:58:55 -0500 Subject: [PATCH 09/26] Reuse resolved depth for GTAO sampling --- Quake/gl_rmisc.c | 17 +------- Quake/gl_vidsdl.c | 24 +++--------- Quake/glquake.h | 1 - Shaders/gtao.comp | 41 ++++---------------- Shaders/gtao_depth.comp | 5 ++- Shaders/shaders.h | 1 - Windows/VisualStudio/embedded.vcxproj | 13 ++----- Windows/VisualStudio/vkquake.vcxproj | 10 ----- Windows/VisualStudio/vkquake.vcxproj.filters | 6 --- meson.build | 1 - 10 files changed, 23 insertions(+), 96 deletions(-) diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index b86d75235..82ddbd52f 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -1512,7 +1512,7 @@ void R_CreateDescriptorSetLayouts () } { - ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, gtao_layout_bindings, 6); + ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, gtao_layout_bindings, 5); gtao_layout_bindings[0].binding = 0; gtao_layout_bindings[0].descriptorCount = 1; gtao_layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; @@ -1533,16 +1533,11 @@ void R_CreateDescriptorSetLayouts () gtao_layout_bindings[4].descriptorCount = 1; gtao_layout_bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; gtao_layout_bindings[4].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - gtao_layout_bindings[5].binding = 5; - gtao_layout_bindings[5].descriptorCount = 1; - gtao_layout_bindings[5].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - gtao_layout_bindings[5].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - descriptor_set_layout_create_info.bindingCount = countof (gtao_layout_bindings); descriptor_set_layout_create_info.pBindings = gtao_layout_bindings; memset (&vulkan_globals.gtao_set_layout, 0, sizeof (vulkan_globals.gtao_set_layout)); - vulkan_globals.gtao_set_layout.num_combined_image_samplers = 5; + vulkan_globals.gtao_set_layout.num_combined_image_samplers = 4; vulkan_globals.gtao_set_layout.num_storage_images = 1; err = vkCreateDescriptorSetLayout (vulkan_globals.device, &descriptor_set_layout_create_info, NULL, &vulkan_globals.gtao_set_layout.handle); @@ -2095,8 +2090,6 @@ void R_CreatePipelineLayouts () Sys_Error ("vkCreatePipelineLayout failed with code %i", (int)err); GL_SetObjectName ((uint64_t)vulkan_globals.gtao_pipeline.layout.handle, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "gtao_pipeline_layout"); vulkan_globals.gtao_pipeline.layout.push_constant_range = push_constant_range; - vulkan_globals.gtao_msaa_pipeline.layout.handle = vulkan_globals.gtao_pipeline.layout.handle; - vulkan_globals.gtao_msaa_pipeline.layout.push_constant_range = push_constant_range; } { @@ -2603,7 +2596,6 @@ DECLARE_SHADER_MODULE (screen_effects_10bit_comp); DECLARE_SHADER_MODULE (screen_effects_10bit_scale_comp); DECLARE_SHADER_MODULE (screen_effects_10bit_scale_sops_comp); DECLARE_SHADER_MODULE (gtao_comp); -DECLARE_SHADER_MODULE (gtao_msaa_comp); DECLARE_SHADER_MODULE (gtao_depth_comp); DECLARE_SHADER_MODULE (gtao_depth_msaa_comp); DECLARE_SHADER_MODULE (gtao_depth_r8_comp); @@ -3971,7 +3963,6 @@ R_CreateGTAOPipelines static void R_CreateGTAOPipelines () { R_CreateComputePipeline (&vulkan_globals.gtao_pipeline, gtao_comp_module, 0, NULL, "gtao"); - R_CreateComputePipeline (&vulkan_globals.gtao_msaa_pipeline, gtao_msaa_comp_module, 0, NULL, "gtao_msaa"); R_CreateComputePipeline (&vulkan_globals.gtao_depth_pipeline, gtao_depth_comp_module, 0, NULL, "gtao_depth"); R_CreateComputePipeline (&vulkan_globals.gtao_depth_msaa_pipeline, gtao_depth_msaa_comp_module, 0, NULL, "gtao_depth_msaa"); R_CreateComputePipeline (&vulkan_globals.gtao_depth_r8_pipeline, gtao_depth_r8_comp_module, 0, NULL, "gtao_depth_r8"); @@ -4078,7 +4069,6 @@ static void R_CreateShaderModules () if (R_GTAOEnabled ()) { CREATE_SHADER_MODULE (gtao_comp); - CREATE_SHADER_MODULE (gtao_msaa_comp); CREATE_SHADER_MODULE (gtao_depth_comp); CREATE_SHADER_MODULE (gtao_depth_msaa_comp); CREATE_SHADER_MODULE (gtao_depth_r8_comp); @@ -4157,7 +4147,6 @@ static void R_DestroyShaderModules () DESTROY_SHADER_MODULE (screen_effects_10bit_scale_comp); DESTROY_SHADER_MODULE (screen_effects_10bit_scale_sops_comp); DESTROY_SHADER_MODULE (gtao_comp); - DESTROY_SHADER_MODULE (gtao_msaa_comp); DESTROY_SHADER_MODULE (gtao_depth_comp); DESTROY_SHADER_MODULE (gtao_depth_msaa_comp); DESTROY_SHADER_MODULE (gtao_depth_r8_comp); @@ -4382,8 +4371,6 @@ void R_DestroyPipelines (void) vulkan_globals.gtao_depth_downsample_pipeline.handle = VK_NULL_HANDLE; vkDestroyPipeline (vulkan_globals.device, vulkan_globals.gtao_denoise_pipeline.handle, NULL); vulkan_globals.gtao_denoise_pipeline.handle = VK_NULL_HANDLE; - vkDestroyPipeline (vulkan_globals.device, vulkan_globals.gtao_msaa_pipeline.handle, NULL); - vulkan_globals.gtao_msaa_pipeline.handle = VK_NULL_HANDLE; vkDestroyPipeline (vulkan_globals.device, vulkan_globals.cs_tex_warp_pipeline.handle, NULL); vulkan_globals.cs_tex_warp_pipeline.handle = VK_NULL_HANDLE; if (vulkan_globals.showtris_pipeline[MAIN_RENDER_PASS_STANDARD].handle != VK_NULL_HANDLE) diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index 0c30b76e8..f623b75fc 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -2923,8 +2923,8 @@ void GL_UpdateDescriptorSets (void) vulkan_globals.gtao_desc_set = R_AllocateDescriptorSet (&vulkan_globals.gtao_set_layout); ZEROED_STRUCT (VkDescriptorImageInfo, gtao_depth_image_info); - gtao_depth_image_info.imageView = depth_buffer_view; - gtao_depth_image_info.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + gtao_depth_image_info.imageView = gtao_depth_pyramid_view; + gtao_depth_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; gtao_depth_image_info.sampler = vulkan_globals.point_sampler; ZEROED_STRUCT (VkDescriptorImageInfo, gtao_output_image_info); @@ -2932,8 +2932,8 @@ void GL_UpdateDescriptorSets (void) gtao_output_image_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; ZEROED_STRUCT (VkDescriptorImageInfo, gtao_stencil_image_info); - gtao_stencil_image_info.imageView = stencil_buffer_view; - gtao_stencil_image_info.imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + gtao_stencil_image_info.imageView = gtao_liquid_mask_buffer_view; + gtao_stencil_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; gtao_stencil_image_info.sampler = vulkan_globals.point_sampler; ZEROED_STRUCT (VkDescriptorImageInfo, gtao_history_image_info); @@ -2941,12 +2941,7 @@ void GL_UpdateDescriptorSets (void) gtao_history_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; gtao_history_image_info.sampler = vulkan_globals.point_sampler; - ZEROED_STRUCT (VkDescriptorImageInfo, gtao_depth_pyramid_image_info); - gtao_depth_pyramid_image_info.imageView = gtao_depth_pyramid_view; - gtao_depth_pyramid_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - gtao_depth_pyramid_image_info.sampler = vulkan_globals.point_sampler; - - ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, gtao_writes, 6); + ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, gtao_writes, 5); gtao_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; gtao_writes[0].dstBinding = 0; gtao_writes[0].descriptorCount = 1; @@ -2982,13 +2977,6 @@ void GL_UpdateDescriptorSets (void) gtao_writes[4].dstSet = vulkan_globals.gtao_desc_set; gtao_writes[4].pImageInfo = >ao_history_image_info; - gtao_writes[5].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - gtao_writes[5].dstBinding = 5; - gtao_writes[5].descriptorCount = 1; - gtao_writes[5].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - gtao_writes[5].dstSet = vulkan_globals.gtao_desc_set; - gtao_writes[5].pImageInfo = >ao_depth_pyramid_image_info; - vkUpdateDescriptorSets (vulkan_globals.device, countof (gtao_writes), gtao_writes, 0, NULL); for (uint32_t mip = 0; mip < GTAO_DEPTH_MIP_LEVELS; ++mip) @@ -4417,7 +4405,7 @@ static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) GL_GTAODepthPyramid (cbx, parms); GL_SetCanvas (cbx, CANVAS_NONE); - vulkan_pipeline_t *pipeline = vulkan_globals.sample_count == VK_SAMPLE_COUNT_1_BIT ? &vulkan_globals.gtao_pipeline : &vulkan_globals.gtao_msaa_pipeline; + vulkan_pipeline_t *pipeline = &vulkan_globals.gtao_pipeline; R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); vkCmdBindDescriptorSets (cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->layout.handle, 0, 1, &vulkan_globals.gtao_desc_set, 0, NULL); diff --git a/Quake/glquake.h b/Quake/glquake.h index c319fc253..fdaf2fe74 100644 --- a/Quake/glquake.h +++ b/Quake/glquake.h @@ -459,7 +459,6 @@ typedef struct vulkan_pipeline_t screen_effects_scale_pipeline; vulkan_pipeline_t screen_effects_scale_sops_pipeline; vulkan_pipeline_t gtao_pipeline; - vulkan_pipeline_t gtao_msaa_pipeline; vulkan_pipeline_t gtao_depth_pipeline; vulkan_pipeline_t gtao_depth_msaa_pipeline; vulkan_pipeline_t gtao_depth_r8_pipeline; diff --git a/Shaders/gtao.comp b/Shaders/gtao.comp index 26793e276..c099fc152 100644 --- a/Shaders/gtao.comp +++ b/Shaders/gtao.comp @@ -2,17 +2,11 @@ #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable -#ifdef GTAO_MSAA -layout (set = 0, binding = 0) uniform sampler2DMS depth_tex; -layout (set = 0, binding = 3) uniform usampler2DMS stencil_tex; -#else -layout (set = 0, binding = 0) uniform sampler2D depth_tex; -layout (set = 0, binding = 3) uniform usampler2D stencil_tex; -#endif +layout (set = 0, binding = 0) uniform sampler2D view_depth_pyramid_tex; layout (set = 0, binding = 1) uniform sampler2D blue_noise_tex; layout (set = 0, binding = 2, rgba8) uniform writeonly image2D ao_image; +layout (set = 0, binding = 3) uniform sampler2D classification_mask_tex; layout (set = 0, binding = 4) uniform sampler2D ao_history_tex; -layout (set = 0, binding = 5) uniform sampler2D view_depth_pyramid_tex; layout (push_constant) uniform GTAOConsts { @@ -50,35 +44,17 @@ const uint GTAO_FLAG_HILBERT_NOISE = 0x8u; float sample_depth (ivec2 pixel) { -#ifdef GTAO_MSAA - float depth = 0.0f; - for (int sample_index = 0; sample_index < textureSamples (depth_tex); ++sample_index) - depth = max (depth, texelFetch (depth_tex, pixel, sample_index).r); - return depth; -#else - return texelFetch (depth_tex, pixel, 0).r; -#endif + return texelFetch (view_depth_pyramid_tex, pixel, 0).r; } ivec2 depth_texture_size () { -#ifdef GTAO_MSAA - return textureSize (depth_tex); -#else - return textureSize (depth_tex, 0); -#endif + return textureSize (view_depth_pyramid_tex, 0); } uint sample_stencil (ivec2 pixel) { -#ifdef GTAO_MSAA - uint stencil = 0u; - for (int sample_index = 0; sample_index < textureSamples (stencil_tex); ++sample_index) - stencil |= texelFetch (stencil_tex, pixel, sample_index).r; - return stencil; -#else - return texelFetch (stencil_tex, pixel, 0).r; -#endif + return uint (round (texelFetch (classification_mask_tex, pixel, 0).r * 255.0f)); } bool inside_viewport (ivec2 pixel) @@ -91,7 +67,7 @@ vec3 reconstruct_position (ivec2 pixel, float depth) { vec2 viewport_pixel = vec2 (pixel - push_constants.viewport_origin) + 0.5f; vec2 ndc = viewport_pixel / vec2 (push_constants.viewport_size) * 2.0f - 1.0f; - float view_z = -push_constants.projection.w / (depth + push_constants.projection.z); + float view_z = -depth; return vec3 ( ndc.x * (-view_z) * push_constants.projection.x, @@ -124,9 +100,8 @@ float sample_horizon_depth (ivec2 pixel, float offset_length, out bool valid) float depth_0 = texelFetch (view_depth_pyramid_tex, coord_0, mip_0).r; float depth_1 = texelFetch (view_depth_pyramid_tex, coord_1, mip_1).r; float view_depth = mix (depth_0, depth_1, fract (mip_level)); - float result = view_depth > 0.00001f ? push_constants.projection.w / view_depth - push_constants.projection.z : depth; - valid = result > 0.00001f; - return result; + valid = view_depth > 0.00001f; + return valid ? view_depth : depth; } vec4 calculate_edges (float center_z, float left_z, float right_z, float top_z, float bottom_z) diff --git a/Shaders/gtao_depth.comp b/Shaders/gtao_depth.comp index 2bd04770d..76174b525 100644 --- a/Shaders/gtao_depth.comp +++ b/Shaders/gtao_depth.comp @@ -33,7 +33,6 @@ push_constants; layout (local_size_x = 8, local_size_y = 8) in; const uint STENCIL_EXCLUDED = 0x3u; -const uint STENCIL_MASK_LIQUID = 0x3cu; float depth_mip_filter (vec4 depths) { @@ -86,6 +85,8 @@ void main () #endif float view_depth = device_depth > 0.00001f ? push_constants.projection.y / (device_depth + push_constants.projection.x) : 0.0f; imageStore (output_depth_image, pixel, vec4 (max (view_depth, 0.0f))); - imageStore (output_liquid_mask_image, pixel, vec4 (float (stencil & STENCIL_MASK_LIQUID) / 255.0f, 0.0f, 0.0f, 1.0f)); + // Preserve the complete resolved stencil byte. GTAO uses the exclusion bits, + // while screen effects mask out the liquid bits from the same R8 value. + imageStore (output_liquid_mask_image, pixel, vec4 (float (stencil) / 255.0f, 0.0f, 0.0f, 1.0f)); #endif } diff --git a/Shaders/shaders.h b/Shaders/shaders.h index 6c2d72dc7..51e8e1579 100644 --- a/Shaders/shaders.h +++ b/Shaders/shaders.h @@ -73,7 +73,6 @@ DECLARE_SHADER_SPV (screen_effects_10bit_comp); DECLARE_SHADER_SPV (screen_effects_10bit_scale_comp); DECLARE_SHADER_SPV (screen_effects_10bit_scale_sops_comp); DECLARE_SHADER_SPV (gtao_comp); -DECLARE_SHADER_SPV (gtao_msaa_comp); DECLARE_SHADER_SPV (gtao_depth_comp); DECLARE_SHADER_SPV (gtao_depth_msaa_comp); DECLARE_SHADER_SPV (gtao_depth_r8_comp); diff --git a/Windows/VisualStudio/embedded.vcxproj b/Windows/VisualStudio/embedded.vcxproj index 0566cb047..a09b45f56 100644 --- a/Windows/VisualStudio/embedded.vcxproj +++ b/Windows/VisualStudio/embedded.vcxproj @@ -246,17 +246,12 @@ $(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(Solution Document $(VULKAN_SDK)\bin\glslangValidator.exe -g -V "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao.comp.spv" -"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao.comp.spv" gtao.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao.comp.c" -$(VULKAN_SDK)\bin\glslangValidator.exe -g -V -DGTAO_MSAA=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_msaa.comp.spv" -"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_msaa.comp.spv" gtao_msaa.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_msaa.comp.c" +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao.comp.spv" gtao.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao.comp.c" $(VULKAN_SDK)\bin\glslangValidator.exe -V "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.spv" $(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.spv" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.spv" -"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.spv" gtao.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.c" -$(VULKAN_SDK)\bin\glslangValidator.exe -V -DGTAO_MSAA=1 "%(FullPath)" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_msaa.comp.spv" -$(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_msaa.comp.spv" -o "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_msaa.comp.spv" -"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_msaa.comp.spv" gtao_msaa.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_msaa.comp.c" - $(SolutionDir)..\..\Shaders\Compiled\Debug\gtao.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Debug\gtao_msaa.comp.c - $(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.c;$(SolutionDir)..\..\Shaders\Compiled\Release\gtao_msaa.comp.c +"$(SolutionDir)\Build-bintoc\$(PlatformShortName)\$(Configuration)\bintoc.exe" "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.spv" gtao.comp_spv "$(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.c" + $(SolutionDir)..\..\Shaders\Compiled\Debug\gtao.comp.c + $(SolutionDir)..\..\Shaders\Compiled\Release\gtao.comp.c Document diff --git a/Windows/VisualStudio/vkquake.vcxproj b/Windows/VisualStudio/vkquake.vcxproj index ea4d9c117..c8f156a8c 100644 --- a/Windows/VisualStudio/vkquake.vcxproj +++ b/Windows/VisualStudio/vkquake.vcxproj @@ -482,11 +482,6 @@ copy "$(SolutionDir)\..\SDL3\lib64\*.dll" "$(TargetDir)" NotUsing NotUsing - - true - NotUsing - NotUsing - true NotUsing @@ -817,11 +812,6 @@ copy "$(SolutionDir)\..\SDL3\lib64\*.dll" "$(TargetDir)" NotUsing NotUsing - - true - NotUsing - NotUsing - true NotUsing diff --git a/Windows/VisualStudio/vkquake.vcxproj.filters b/Windows/VisualStudio/vkquake.vcxproj.filters index ea549bd62..a443dae93 100644 --- a/Windows/VisualStudio/vkquake.vcxproj.filters +++ b/Windows/VisualStudio/vkquake.vcxproj.filters @@ -400,9 +400,6 @@ Shaders\Debug - - Shaders\Debug - Shaders\Debug @@ -523,9 +520,6 @@ Shaders\Release - - Shaders\Release - Shaders\Release diff --git a/meson.build b/meson.build index 525b77f63..cf26b8e94 100644 --- a/meson.build +++ b/meson.build @@ -151,7 +151,6 @@ shader_variants = [ ['Shaders/screen_effects.comp', 'screen_effects_10bit_scale.comp', ['-DUSE_10BIT=1', '-DSCALING=1']], ['Shaders/screen_effects.comp', 'screen_effects_10bit_scale_sops.comp', ['--target-env', 'vulkan1.1', '-DUSE_10BIT=1', '-DSCALING=1', '-DUSE_SUBGROUP_OPS=1']], ['Shaders/gtao.comp', 'gtao.comp', []], - ['Shaders/gtao.comp', 'gtao_msaa.comp', ['-DGTAO_MSAA=1']], ['Shaders/gtao_depth.comp', 'gtao_depth.comp', []], ['Shaders/gtao_depth.comp', 'gtao_depth_msaa.comp', ['-DGTAO_DEPTH_MSAA=1']], ['Shaders/gtao_depth.comp', 'gtao_depth_r8.comp', ['-DGTAO_LIQUID_MASK_R8=1']], From 3bacfcae5f59f73b569ac51012d9d7e77dde7dbc Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 20:12:31 -0500 Subject: [PATCH 10/26] Add GTAO presets to graphics menu --- Quake/gl_rmisc.c | 9 +++++++-- Quake/menu.c | 33 +++++++++++++++++++++++++++++++++ Quake/render.h | 1 + 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index 82ddbd52f..08764420f 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -4429,10 +4429,10 @@ static void R_ScaleChanged_f (cvar_t *var) /* =================== -R_GTAODefaults_f +R_RestoreGTAODefaults =================== */ -static void R_GTAODefaults_f (void) +void R_RestoreGTAODefaults (void) { cvar_t *gtao_cvars[] = { &r_gtao, @@ -4462,6 +4462,11 @@ static void R_GTAODefaults_f (void) for (uint32_t i = 0; i < countof (gtao_cvars); ++i) Cvar_SetQuick (gtao_cvars[i], gtao_cvars[i]->default_string); +} + +static void R_GTAODefaults_f (void) +{ + R_RestoreGTAODefaults (); Con_Printf ("GTAO settings restored to renderer defaults\n"); } diff --git a/Quake/menu.c b/Quake/menu.c index cf3a98ee0..976a299ad 100644 --- a/Quake/menu.c +++ b/Quake/menu.c @@ -1731,6 +1731,7 @@ enum GRAPHICS_OPT_MODELS, GRAPHICS_OPT_MODEL_INTERPOLATION, GRAPHICS_OPT_PARTICLES, + GRAPHICS_OPT_GTAO, GRAPHICS_OPT_SHADOWS, GRAPHICS_OPTIONS_ITEMS, }; @@ -1844,6 +1845,29 @@ static void M_GraphicsOptions_ChooseNextParticles (int dir) Cvar_SetValueQuick (&r_particles, (float)value); } +static int M_GraphicsOptions_GTAOPreset (void) +{ + if (r_gtao.value <= 0.0f) + return 0; + if (r_gtao_halfres.value > 0.0f) + return r_gtao_quality.value >= 3.0f ? 2 : 1; + return 3; +} + +static void M_GraphicsOptions_ChooseNextGTAOPreset (int dir) +{ + const int preset = (M_GraphicsOptions_GTAOPreset () + 4 + dir) % 4; + if (preset == 0) + { + Cvar_SetValueQuick (&r_gtao, 0.0f); + return; + } + + R_RestoreGTAODefaults (); + Cvar_SetValueQuick (&r_gtao_halfres, preset < 3 ? 1.0f : 0.0f); + Cvar_SetValueQuick (&r_gtao_quality, preset == 1 ? 2.0f : 3.0f); +} + static void M_GraphicsOptions_AdjustSliders (int dir, qboolean mouse) { float f, clamped_mouse = CLAMP (SLIDER_START, (float)m_mouse_x, SLIDER_END); @@ -1923,6 +1947,9 @@ static void M_GraphicsOptions_AdjustSliders (int dir, qboolean mouse) case GRAPHICS_OPT_PARTICLES: M_GraphicsOptions_ChooseNextParticles (dir); break; + case GRAPHICS_OPT_GTAO: + M_GraphicsOptions_ChooseNextGTAOPreset (dir); + break; case GRAPHICS_OPT_SHADOWS: if (vulkan_globals.ray_query) Cvar_SetValueQuick (&r_rtshadows, (float)(((int)r_rtshadows.value + 4 + dir) % 4)); @@ -2061,6 +2088,12 @@ static void M_GraphicsOptions_Draw (cb_context_t *cbx) cbx, MENU_VALUE_X, top + CHARACTER_SIZE * GRAPHICS_OPT_PARTICLES, ((int)r_particles.value == 0) ? "off" : (((int)r_particles.value == 2) ? "Classic" : "glQuake")); + M_Print (cbx, MENU_LABEL_X, top + CHARACTER_SIZE * GRAPHICS_OPT_GTAO, "Ambient Occlusion"); + { + const char *gtao_presets[] = {"off", "low", "medium", "high"}; + M_Print (cbx, MENU_VALUE_X, top + CHARACTER_SIZE * GRAPHICS_OPT_GTAO, gtao_presets[M_GraphicsOptions_GTAOPreset ()]); + } + if (vulkan_globals.ray_query) { M_Print (cbx, MENU_LABEL_X, top + CHARACTER_SIZE * GRAPHICS_OPT_SHADOWS, "Dynamic Shadows"); diff --git a/Quake/render.h b/Quake/render.h index 8e4f7b792..82c90b82a 100644 --- a/Quake/render.h +++ b/Quake/render.h @@ -196,6 +196,7 @@ extern refdef_t r_refdef; extern vec3_t r_origin, vpn, vright, vup; void R_Init (void); +void R_RestoreGTAODefaults (void); void R_InitTextures (void); void R_InitEfrags (void); void R_RenderView ( From e3eb5560353c17ef7140e896532d7664ad66fe0e Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 20:33:58 -0500 Subject: [PATCH 11/26] Remove temporal GTAO accumulation --- Quake/gl_rmain.c | 3 - Quake/gl_rmisc.c | 16 +-- Quake/gl_vidsdl.c | 245 ++++--------------------------------- Quake/glquake.h | 3 - Shaders/gtao.comp | 41 +------ Shaders/screen_effects.inc | 4 +- 6 files changed, 31 insertions(+), 281 deletions(-) diff --git a/Quake/gl_rmain.c b/Quake/gl_rmain.c index 31106600e..29c8eb644 100644 --- a/Quake/gl_rmain.c +++ b/Quake/gl_rmain.c @@ -131,8 +131,6 @@ cvar_t r_gtao_denoise = {"r_gtao_denoise", "2", CVAR_ARCHIVE}; cvar_t r_gtao_bias = {"r_gtao_bias", "0", CVAR_ARCHIVE}; cvar_t r_gtao_bent_normals = {"r_gtao_bent_normals", "0", CVAR_NONE}; cvar_t r_gtao_multibounce = {"r_gtao_multibounce", "1", CVAR_ARCHIVE}; -cvar_t r_gtao_temporal = {"r_gtao_temporal", "0", CVAR_ARCHIVE}; -cvar_t r_gtao_temporal_blend = {"r_gtao_temporal_blend", "0.85", CVAR_ARCHIVE}; cvar_t r_gtao_halfres = {"r_gtao_halfres", "1", CVAR_ARCHIVE}; cvar_t r_gtao_liquid_water = {"r_gtao_liquid_water", "1", CVAR_ARCHIVE}; cvar_t r_gtao_liquid_slime = {"r_gtao_liquid_slime", "1", CVAR_ARCHIVE}; @@ -450,7 +448,6 @@ static void R_SetupViewBeforeMark (void *unused) vulkan_globals.gtao_projection[1] = 1.0f / (-vulkan_globals.projection_matrix[5]); vulkan_globals.gtao_projection[2] = vulkan_globals.projection_matrix[10]; vulkan_globals.gtao_projection[3] = vulkan_globals.projection_matrix[14]; - memcpy (vulkan_globals.gtao_view_projection, vulkan_globals.view_projection_matrix, sizeof (vulkan_globals.gtao_view_projection)); } // johnfitz -- cheat-protect some draw modes diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index 08764420f..ce40c05fb 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -76,8 +76,6 @@ extern cvar_t r_gtao_denoise; extern cvar_t r_gtao_bias; extern cvar_t r_gtao_bent_normals; extern cvar_t r_gtao_multibounce; -extern cvar_t r_gtao_temporal; -extern cvar_t r_gtao_temporal_blend; extern cvar_t r_gtao_halfres; extern cvar_t r_gtao_liquid_water; extern cvar_t r_gtao_liquid_slime; @@ -1512,7 +1510,7 @@ void R_CreateDescriptorSetLayouts () } { - ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, gtao_layout_bindings, 5); + ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, gtao_layout_bindings, 4); gtao_layout_bindings[0].binding = 0; gtao_layout_bindings[0].descriptorCount = 1; gtao_layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; @@ -1529,15 +1527,11 @@ void R_CreateDescriptorSetLayouts () gtao_layout_bindings[3].descriptorCount = 1; gtao_layout_bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; gtao_layout_bindings[3].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - gtao_layout_bindings[4].binding = 4; - gtao_layout_bindings[4].descriptorCount = 1; - gtao_layout_bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - gtao_layout_bindings[4].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; descriptor_set_layout_create_info.bindingCount = countof (gtao_layout_bindings); descriptor_set_layout_create_info.pBindings = gtao_layout_bindings; memset (&vulkan_globals.gtao_set_layout, 0, sizeof (vulkan_globals.gtao_set_layout)); - vulkan_globals.gtao_set_layout.num_combined_image_samplers = 4; + vulkan_globals.gtao_set_layout.num_combined_image_samplers = 3; vulkan_globals.gtao_set_layout.num_storage_images = 1; err = vkCreateDescriptorSetLayout (vulkan_globals.device, &descriptor_set_layout_create_info, NULL, &vulkan_globals.gtao_set_layout.handle); @@ -2075,7 +2069,7 @@ void R_CreatePipelineLayouts () ZEROED_STRUCT (VkPushConstantRange, push_constant_range); push_constant_range.offset = 0; - push_constant_range.size = 12 * sizeof (uint32_t) + 20 * sizeof (float); + push_constant_range.size = 8 * sizeof (uint32_t) + 10 * sizeof (float); push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; ZEROED_STRUCT (VkPipelineLayoutCreateInfo, pipeline_layout_create_info); @@ -4451,8 +4445,6 @@ void R_RestoreGTAODefaults (void) &r_gtao_bias, &r_gtao_bent_normals, &r_gtao_multibounce, - &r_gtao_temporal, - &r_gtao_temporal_blend, &r_gtao_halfres, &r_gtao_liquid_water, &r_gtao_liquid_slime, @@ -4558,8 +4550,6 @@ void R_Init (void) Cvar_RegisterVariable (&r_gtao_bias); Cvar_RegisterVariable (&r_gtao_bent_normals); Cvar_RegisterVariable (&r_gtao_multibounce); - Cvar_RegisterVariable (&r_gtao_temporal); - Cvar_RegisterVariable (&r_gtao_temporal_blend); Cvar_RegisterVariable (&r_gtao_halfres); Cvar_RegisterVariable (&r_gtao_liquid_water); Cvar_RegisterVariable (&r_gtao_liquid_slime); diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index f623b75fc..b82ab8f10 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -162,33 +162,6 @@ static VkImage gtao_denoise_buffer; static vulkan_memory_t gtao_denoise_buffer_memory; static VkImageView gtao_denoise_buffer_view; static qboolean gtao_denoise_buffer_initialized; -static VkImage gtao_history_buffer; -static vulkan_memory_t gtao_history_buffer_memory; -static VkImageView gtao_history_buffer_view; -static qboolean gtao_history_initialized; -static float gtao_previous_view_projection[16]; -static vec3_t gtao_previous_origin; -static vec3_t gtao_previous_forward; -static int32_t gtao_previous_viewport_x; -static int32_t gtao_previous_viewport_y; -static uint32_t gtao_previous_viewport_width; -static uint32_t gtao_previous_viewport_height; -static float gtao_previous_projection[2]; -static qboolean gtao_previous_halfres; -static uint32_t gtao_previous_debug_mode; -typedef struct gtao_history_settings_s -{ - float radius; - float thickness; - float depth_mip_offset; - float bias; - float radius_multiplier; - float falloff_range; - uint32_t normal_mode; - uint32_t quality; - uint32_t noise_mode; -} gtao_history_settings_t; -static gtao_history_settings_t gtao_previous_history_settings; static VkImage gtao_depth_pyramid; static vulkan_memory_t gtao_depth_pyramid_memory; static VkImageView gtao_depth_pyramid_view; @@ -2166,7 +2139,7 @@ static void GL_CreateGTAOBuffer (void) image_create_info.arrayLayers = 1; image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; - image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; err = vkCreateImage (vulkan_globals.device, &image_create_info, NULL, >ao_buffer); if (err != VK_SUCCESS) @@ -2261,30 +2234,6 @@ static void GL_CreateGTAOBuffer (void) GL_SetObjectName ((uint64_t)gtao_denoise_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "GTAO Denoise Buffer View"); gtao_denoise_buffer_initialized = false; - image_create_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - err = vkCreateImage (vulkan_globals.device, &image_create_info, NULL, >ao_history_buffer); - if (err != VK_SUCCESS) - Sys_Error ("vkCreateImage failed with code %i", (int)err); - GL_SetObjectName ((uint64_t)gtao_history_buffer, VK_OBJECT_TYPE_IMAGE, "GTAO History Buffer"); - - vkGetImageMemoryRequirements (vulkan_globals.device, gtao_history_buffer, &memory_requirements); - dedicated_allocation_info.image = gtao_history_buffer; - memory_allocate_info.allocationSize = memory_requirements.size; - memory_allocate_info.memoryTypeIndex = GL_MemoryTypeFromProperties (memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0); - assert (gtao_history_buffer_memory.handle == VK_NULL_HANDLE); - R_AllocateVulkanMemory (>ao_history_buffer_memory, &memory_allocate_info, VULKAN_MEMORY_TYPE_DEVICE, &num_vulkan_misc_allocations); - GL_SetObjectName ((uint64_t)gtao_history_buffer_memory.handle, VK_OBJECT_TYPE_DEVICE_MEMORY, "GTAO History Buffer"); - err = vkBindImageMemory (vulkan_globals.device, gtao_history_buffer, gtao_history_buffer_memory.handle, 0); - if (err != VK_SUCCESS) - Sys_Error ("vkBindImageMemory failed with code %i", (int)err); - - image_view_create_info.image = gtao_history_buffer; - err = vkCreateImageView (vulkan_globals.device, &image_view_create_info, NULL, >ao_history_buffer_view); - if (err != VK_SUCCESS) - Sys_Error ("vkCreateImageView failed with code %i", (int)err); - GL_SetObjectName ((uint64_t)gtao_history_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "GTAO History Buffer View"); - gtao_history_initialized = false; - image_create_info.format = VK_FORMAT_R32_SFLOAT; image_create_info.mipLevels = GTAO_DEPTH_MIP_LEVELS; image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; @@ -2936,12 +2885,7 @@ void GL_UpdateDescriptorSets (void) gtao_stencil_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; gtao_stencil_image_info.sampler = vulkan_globals.point_sampler; - ZEROED_STRUCT (VkDescriptorImageInfo, gtao_history_image_info); - gtao_history_image_info.imageView = gtao_history_buffer_view; - gtao_history_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - gtao_history_image_info.sampler = vulkan_globals.point_sampler; - - ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, gtao_writes, 5); + ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, gtao_writes, 4); gtao_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; gtao_writes[0].dstBinding = 0; gtao_writes[0].descriptorCount = 1; @@ -2970,13 +2914,6 @@ void GL_UpdateDescriptorSets (void) gtao_writes[3].dstSet = vulkan_globals.gtao_desc_set; gtao_writes[3].pImageInfo = >ao_stencil_image_info; - gtao_writes[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - gtao_writes[4].dstBinding = 4; - gtao_writes[4].descriptorCount = 1; - gtao_writes[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - gtao_writes[4].dstSet = vulkan_globals.gtao_desc_set; - gtao_writes[4].pImageInfo = >ao_history_image_info; - vkUpdateDescriptorSets (vulkan_globals.device, countof (gtao_writes), gtao_writes, 0, NULL); for (uint32_t mip = 0; mip < GTAO_DEPTH_MIP_LEVELS; ++mip) @@ -3702,13 +3639,6 @@ static void GL_DestroyRenderResources (void) gtao_denoise_buffer = VK_NULL_HANDLE; gtao_denoise_buffer_initialized = false; - vkDestroyImageView (vulkan_globals.device, gtao_history_buffer_view, NULL); - vkDestroyImage (vulkan_globals.device, gtao_history_buffer, NULL); - R_FreeVulkanMemory (>ao_history_buffer_memory, &num_vulkan_misc_allocations); - gtao_history_buffer_view = VK_NULL_HANDLE; - gtao_history_buffer = VK_NULL_HANDLE; - gtao_history_initialized = false; - for (uint32_t mip = 0; mip < GTAO_DEPTH_MIP_LEVELS; ++mip) { vkDestroyImageView (vulkan_globals.device, gtao_depth_mip_views[mip], NULL); @@ -4108,11 +4038,6 @@ typedef struct gtao_constants_s uint32_t flags; float radius_multiplier; float falloff_range; - float temporal_blend; - uint32_t padding; - float current_to_previous_clip_x[4]; - float current_to_previous_clip_y[4]; - float current_to_previous_clip_w[4]; } gtao_constants_t; typedef struct ray_debug_constants_s @@ -4261,8 +4186,8 @@ typedef struct gtao_denoise_constants_s static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) { - const uint32_t debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 7); - const uint32_t pass_count = (debug_mode == 0u || debug_mode == 7u) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u; + const uint32_t debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 6); + const uint32_t pass_count = (debug_mode == 0u || debug_mode == 6u) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u; if (pass_count == 0) return; @@ -4358,10 +4283,7 @@ GL_GTAO static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) { R_BeginDebugUtilsLabel (cbx, "GTAO"); - const qboolean store_temporal_history = r_gtao_temporal.value > 0.0f && r_gtao_bent_normals.value <= 0.0f && - (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 6 || (int)r_gtao_debug.value == 7); - - ZEROED_STRUCT_ARRAY (VkImageMemoryBarrier, image_barriers, 3); + ZEROED_STRUCT_ARRAY (VkImageMemoryBarrier, image_barriers, 2); image_barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_barriers[0].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; @@ -4387,21 +4309,9 @@ static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) image_barriers[1].subresourceRange.levelCount = 1; image_barriers[1].subresourceRange.layerCount = 1; - image_barriers[2].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - image_barriers[2].srcAccessMask = gtao_history_initialized ? VK_ACCESS_SHADER_READ_BIT : 0; - image_barriers[2].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - image_barriers[2].oldLayout = gtao_history_initialized ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; - image_barriers[2].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - image_barriers[2].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_barriers[2].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_barriers[2].image = gtao_history_buffer; - image_barriers[2].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - image_barriers[2].subresourceRange.levelCount = 1; - image_barriers[2].subresourceRange.layerCount = 1; - vkCmdPipelineBarrier ( cbx->cb, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, store_temporal_history ? countof (image_barriers) : 2, image_barriers); + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, countof (image_barriers), image_barriers); GL_GTAODepthPyramid (cbx, parms); GL_SetCanvas (cbx, CANVAS_NONE); @@ -4409,45 +4319,6 @@ static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); vkCmdBindDescriptorSets (cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->layout.handle, 0, 1, &vulkan_globals.gtao_desc_set, 0, NULL); - vec3_t origin_delta; - VectorSubtract (parms->origin, gtao_previous_origin, origin_delta); - const gtao_history_settings_t history_settings = { - .radius = q_max (1.0f, r_gtao_radius.value), - .thickness = q_max (0.0f, r_gtao_thickness.value), - .depth_mip_offset = r_gtao_depth_prefilter.value > 0.0f ? CLAMP (0.0f, r_gtao_depth_mip_offset.value, 8.0f) : 64.0f, - .bias = CLAMP (0.0f, r_gtao_bias.value, 0.5f), - .radius_multiplier = CLAMP (0.3f, r_gtao_radius_multiplier.value, 3.0f), - .falloff_range = CLAMP (0.01f, r_gtao_falloff.value, 1.0f), - .normal_mode = (uint32_t)CLAMP (0, (int)r_gtao_normal_mode.value, 2), - .quality = (uint32_t)CLAMP (0, (int)r_gtao_quality.value, 3), - .noise_mode = r_gtao_noise_mode.value > 0.0f ? 1u : 0u, - }; - const qboolean temporal_history_valid = gtao_history_initialized && - gtao_previous_viewport_x == vulkan_globals.gtao_viewport_x && gtao_previous_viewport_y == vulkan_globals.gtao_viewport_y && - gtao_previous_viewport_width == vulkan_globals.gtao_viewport_width && gtao_previous_viewport_height == vulkan_globals.gtao_viewport_height && - fabsf (gtao_previous_projection[0] - vulkan_globals.gtao_projection[0]) < 0.0001f && - fabsf (gtao_previous_projection[1] - vulkan_globals.gtao_projection[1]) < 0.0001f && - gtao_previous_halfres == (r_gtao_halfres.value > 0.0f) && - gtao_previous_debug_mode == (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 7) && - memcmp (>ao_previous_history_settings, &history_settings, sizeof (history_settings)) == 0 && - DotProduct (origin_delta, origin_delta) < 4096.0f && DotProduct (parms->forward, gtao_previous_forward) > 0.5f; - - float reprojection[3][4]; - const int previous_clip_rows[3] = {0, 1, 3}; - for (int output_row = 0; output_row < 3; ++output_row) - { - const int row = previous_clip_rows[output_row]; - vec3_t previous_row = { - gtao_previous_view_projection[0 * 4 + row], - gtao_previous_view_projection[1 * 4 + row], - gtao_previous_view_projection[2 * 4 + row], - }; - reprojection[output_row][0] = DotProduct (previous_row, parms->right); - reprojection[output_row][1] = -DotProduct (previous_row, parms->down); - reprojection[output_row][2] = -DotProduct (previous_row, parms->forward); - reprojection[output_row][3] = DotProduct (previous_row, parms->origin) + gtao_previous_view_projection[3 * 4 + row]; - } - gtao_constants_t push_constants = { .viewport_x = vulkan_globals.gtao_viewport_x, .viewport_y = vulkan_globals.gtao_viewport_y, @@ -4460,102 +4331,36 @@ static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) .radius = q_max (1.0f, r_gtao_radius.value), .thickness = q_max (0.0f, r_gtao_thickness.value), .normal_mode = (uint32_t)CLAMP (0, (int)r_gtao_normal_mode.value, 2), - .debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 7), + .debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 6), .quality = (uint32_t)CLAMP (0, (int)r_gtao_quality.value, 3), .depth_mip_offset = r_gtao_depth_prefilter.value > 0.0f ? CLAMP (0.0f, r_gtao_depth_mip_offset.value, 8.0f) : 64.0f, .bias = CLAMP (0.0f, r_gtao_bias.value, 0.5f), - .flags = (r_gtao_bent_normals.value > 0.0f ? 1u : 0u) | - ((r_gtao_temporal.value > 0.0f && r_gtao_bent_normals.value <= 0.0f && temporal_history_valid && - (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 6 || (int)r_gtao_debug.value == 7)) ? 2u : 0u) | - (r_gtao_halfres.value > 0.0f ? 4u : 0u) | (r_gtao_noise_mode.value > 0.0f ? 8u : 0u), + .flags = (r_gtao_bent_normals.value > 0.0f ? 1u : 0u) | (r_gtao_halfres.value > 0.0f ? 4u : 0u) | + (r_gtao_noise_mode.value > 0.0f ? 8u : 0u), .radius_multiplier = CLAMP (0.3f, r_gtao_radius_multiplier.value, 3.0f), .falloff_range = CLAMP (0.01f, r_gtao_falloff.value, 1.0f), - .temporal_blend = CLAMP (0.0f, r_gtao_temporal_blend.value, 0.97f), - .padding = 0u, }; - memcpy (push_constants.current_to_previous_clip_x, reprojection[0], sizeof (reprojection[0])); - memcpy (push_constants.current_to_previous_clip_y, reprojection[1], sizeof (reprojection[1])); - memcpy (push_constants.current_to_previous_clip_w, reprojection[2], sizeof (reprojection[2])); R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (push_constants), &push_constants); const uint32_t gtao_stride = r_gtao_halfres.value > 0.0f ? 2u : 1u; vkCmdDispatch (cbx->cb, (parms->vid_width + 8 * gtao_stride - 1) / (8 * gtao_stride), (parms->vid_height + 8 * gtao_stride - 1) / (8 * gtao_stride), 1); - if (store_temporal_history) - { - ZEROED_STRUCT_ARRAY (VkImageMemoryBarrier, copy_barriers, 2); - copy_barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - copy_barriers[0].srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; - copy_barriers[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - copy_barriers[0].oldLayout = VK_IMAGE_LAYOUT_GENERAL; - copy_barriers[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - copy_barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - copy_barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - copy_barriers[0].image = gtao_buffer; - copy_barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - copy_barriers[0].subresourceRange.levelCount = 1; - copy_barriers[0].subresourceRange.layerCount = 1; - copy_barriers[1] = copy_barriers[0]; - copy_barriers[1].srcAccessMask = gtao_history_initialized ? VK_ACCESS_SHADER_READ_BIT : 0; - copy_barriers[1].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - copy_barriers[1].oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - copy_barriers[1].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - copy_barriers[1].image = gtao_history_buffer; - vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 2, copy_barriers); - - const uint32_t history_stride = r_gtao_halfres.value > 0.0f ? 2u : 1u; - ZEROED_STRUCT (VkImageCopy, history_copy); - history_copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - history_copy.srcSubresource.layerCount = 1; - history_copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - history_copy.dstSubresource.layerCount = 1; - history_copy.extent.width = (parms->vid_width + history_stride - 1) / history_stride; - history_copy.extent.height = (parms->vid_height + history_stride - 1) / history_stride; - history_copy.extent.depth = 1; - vkCmdCopyImage (cbx->cb, gtao_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, gtao_history_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &history_copy); - - copy_barriers[0].srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - copy_barriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - copy_barriers[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - copy_barriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - copy_barriers[1].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - copy_barriers[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - copy_barriers[1].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - copy_barriers[1].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 2, copy_barriers); - } - else - { - ZEROED_STRUCT (VkImageMemoryBarrier, ao_barrier); - ao_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - ao_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; - ao_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - ao_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; - ao_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - ao_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - ao_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - ao_barrier.image = gtao_buffer; - ao_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - ao_barrier.subresourceRange.levelCount = 1; - ao_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier ( - cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &ao_barrier); - } + ZEROED_STRUCT (VkImageMemoryBarrier, ao_barrier); + ao_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + ao_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + ao_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + ao_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + ao_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + ao_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + ao_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + ao_barrier.image = gtao_buffer; + ao_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + ao_barrier.subresourceRange.levelCount = 1; + ao_barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier ( + cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &ao_barrier); GL_GTAODenoise (cbx, parms); gtao_buffer_initialized = true; - gtao_history_initialized = store_temporal_history; - memcpy (gtao_previous_view_projection, vulkan_globals.gtao_view_projection, sizeof (gtao_previous_view_projection)); - VectorCopy (parms->origin, gtao_previous_origin); - VectorCopy (parms->forward, gtao_previous_forward); - gtao_previous_viewport_x = vulkan_globals.gtao_viewport_x; - gtao_previous_viewport_y = vulkan_globals.gtao_viewport_y; - gtao_previous_viewport_width = vulkan_globals.gtao_viewport_width; - gtao_previous_viewport_height = vulkan_globals.gtao_viewport_height; - gtao_previous_projection[0] = vulkan_globals.gtao_projection[0]; - gtao_previous_projection[1] = vulkan_globals.gtao_projection[1]; - gtao_previous_halfres = r_gtao_halfres.value > 0.0f; - gtao_previous_debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 7); - gtao_previous_history_settings = history_settings; R_EndDebugUtilsLabel (cbx); } @@ -4665,9 +4470,9 @@ static void GL_ScreenEffects (cb_context_t *cbx, qboolean enabled, end_rendering if (R_GTAOEnabled ()) { push_constants.gtao_strength = r_gtao_strength.value; - push_constants.gtao_debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 8); + push_constants.gtao_debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 7); push_constants.gtao_denoise = - (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 7) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u; + (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 6) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u; push_constants.gtao_multibounce = CLAMP (0.0f, r_gtao_multibounce.value, 1.0f); push_constants.gtao_halfres = r_gtao_halfres.value > 0.0f ? 1u : 0u; push_constants.gtao_liquid_water = diff --git a/Quake/glquake.h b/Quake/glquake.h index fdaf2fe74..afe05e242 100644 --- a/Quake/glquake.h +++ b/Quake/glquake.h @@ -530,7 +530,6 @@ typedef struct uint32_t gtao_viewport_width; uint32_t gtao_viewport_height; float gtao_projection[4]; - float gtao_view_projection[16]; // Dispatch table PFN_vkCmdBindPipeline vk_cmd_bind_pipeline; @@ -620,8 +619,6 @@ extern cvar_t r_gtao_denoise; extern cvar_t r_gtao_bias; extern cvar_t r_gtao_bent_normals; extern cvar_t r_gtao_multibounce; -extern cvar_t r_gtao_temporal; -extern cvar_t r_gtao_temporal_blend; extern cvar_t r_gtao_halfres; extern cvar_t r_gtao_liquid_water; extern cvar_t r_gtao_liquid_slime; diff --git a/Shaders/gtao.comp b/Shaders/gtao.comp index c099fc152..8c58979e7 100644 --- a/Shaders/gtao.comp +++ b/Shaders/gtao.comp @@ -6,7 +6,6 @@ layout (set = 0, binding = 0) uniform sampler2D view_depth_pyramid_tex; layout (set = 0, binding = 1) uniform sampler2D blue_noise_tex; layout (set = 0, binding = 2, rgba8) uniform writeonly image2D ao_image; layout (set = 0, binding = 3) uniform sampler2D classification_mask_tex; -layout (set = 0, binding = 4) uniform sampler2D ao_history_tex; layout (push_constant) uniform GTAOConsts { @@ -23,11 +22,6 @@ layout (push_constant) uniform GTAOConsts uint flags; float radius_multiplier; float falloff_range; - float temporal_blend; - uint padding; - vec4 current_to_previous_clip_x; - vec4 current_to_previous_clip_y; - vec4 current_to_previous_clip_w; } push_constants; @@ -38,7 +32,6 @@ const float HALF_PI = 1.57079632679489661923f; const uint STENCIL_MASK_SKY = 0x1u; const uint STENCIL_MASK_VIEWMODEL = 0x2u; const uint GTAO_FLAG_BENT_NORMALS = 0x1u; -const uint GTAO_FLAG_TEMPORAL = 0x2u; const uint GTAO_FLAG_HALFRES = 0x4u; const uint GTAO_FLAG_HILBERT_NOISE = 0x8u; @@ -459,38 +452,6 @@ void main () imageStore (ao_image, ao_pixel, vec4 (safe_normalize (bent_sum) * 0.5f + 0.5f, 1.0f)); return; } - float packed_depth = clamp (log2 (1.0f + max (-center.z, 0.0f)) / 16.0f, 0.0f, 1.0f); - float temporal_weight = 0.0f; - if ((push_constants.flags & GTAO_FLAG_TEMPORAL) != 0u) - { - vec4 view_position = vec4 (center, 1.0f); - vec3 previous_clip = vec3 ( - dot (push_constants.current_to_previous_clip_x, view_position), - dot (push_constants.current_to_previous_clip_y, view_position), - dot (push_constants.current_to_previous_clip_w, view_position)); - if (previous_clip.z > 0.0001f) - { - vec2 previous_ndc = previous_clip.xy / previous_clip.z; - ivec2 previous_pixel = push_constants.viewport_origin + ivec2 ((previous_ndc * 0.5f + 0.5f) * vec2 (push_constants.viewport_size)); - if (inside_viewport (previous_pixel)) - { - ivec2 history_pixel = half_res ? previous_pixel / 2 : previous_pixel; - vec4 history = texelFetch (ao_history_tex, history_pixel, 0); - float previous_packed_depth = clamp (log2 (1.0f + previous_clip.z) / 16.0f, 0.0f, 1.0f); - if (abs (history.a - previous_packed_depth) < 0.004f) - { - float clamped_history = clamp (history.r, visibility - 0.15f, visibility + 0.15f); - temporal_weight = clamp (push_constants.temporal_blend, 0.0f, 0.97f); - visibility = mix (visibility, clamped_history, temporal_weight); - } - } - } - } - if (push_constants.debug_mode == 6u) - { - imageStore (ao_image, ao_pixel, vec4 (temporal_weight, 1.0f - temporal_weight, 0.0f, packed_depth)); - return; - } - vec2 packed_bent = compute_bent_normal ? oct_encode (safe_normalize (bent_sum)) : vec2 (0.0f, packed_depth); + vec2 packed_bent = compute_bent_normal ? oct_encode (safe_normalize (bent_sum)) : vec2 (0.0f); imageStore (ao_image, ao_pixel, vec4 (visibility, packed_edges, packed_bent)); } diff --git a/Shaders/screen_effects.inc b/Shaders/screen_effects.inc index 27bbb9281..f92ba45e8 100644 --- a/Shaders/screen_effects.inc +++ b/Shaders/screen_effects.inc @@ -292,9 +292,9 @@ void main () const ivec2 liquid_pixel = gtao_warped ? clamp (ivec2 (effect_texcoord / push_constants.screen_size_rcp), ivec2 (0), ivec2 (push_constants.clamp_size)) : ao_pixel; const uint liquid_mask = gtao_liquid_mask (liquid_pixel); - if (push_constants.gtao_debug_mode == 8u) + if (push_constants.gtao_debug_mode == 7u) color.rgb = gtao_liquid_debug_color (liquid_mask); - else if (push_constants.gtao_debug_mode == 1u || push_constants.gtao_debug_mode == 7u) + else if (push_constants.gtao_debug_mode == 1u || push_constants.gtao_debug_mode == 6u) color.rgb = vec3 (clamp (ao_sample.r, 0.0f, 1.0f)); else if (push_constants.gtao_debug_mode > 1u) color.rgb = ao_sample.rgb; From 7944fad1bdbc100eba274a04980e7318f9ec40b9 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 20:50:12 -0500 Subject: [PATCH 12/26] Simplify GTAO tuning paths --- Quake/gl_rmain.c | 10 +-- Quake/gl_rmisc.c | 26 +------ Quake/gl_vidsdl.c | 25 +++---- Quake/glquake.h | 8 +- Shaders/gtao.comp | 149 +++++-------------------------------- Shaders/screen_effects.inc | 4 +- 6 files changed, 38 insertions(+), 184 deletions(-) diff --git a/Quake/gl_rmain.c b/Quake/gl_rmain.c index 29c8eb644..6ecaf20a0 100644 --- a/Quake/gl_rmain.c +++ b/Quake/gl_rmain.c @@ -116,20 +116,14 @@ qboolean r_drawworld_cheatsafe, r_fullbright_cheatsafe, r_lightmap_cheatsafe; // cvar_t r_scale = {"r_scale", "1", CVAR_ARCHIVE}; cvar_t r_gtao = {"r_gtao", "1", CVAR_ARCHIVE}; -cvar_t r_gtao_radius = {"r_gtao_radius", "24", CVAR_ARCHIVE}; -cvar_t r_gtao_radius_multiplier = {"r_gtao_radius_multiplier", "1.457", CVAR_ARCHIVE}; +cvar_t r_gtao_radius = {"r_gtao_radius", "32", CVAR_ARCHIVE}; cvar_t r_gtao_falloff = {"r_gtao_falloff", "0.615", CVAR_ARCHIVE}; -cvar_t r_gtao_thickness = {"r_gtao_thickness", "0", CVAR_ARCHIVE}; +cvar_t r_gtao_thin_occluder_compensation = {"r_gtao_thin_occluder_compensation", "0", CVAR_ARCHIVE}; cvar_t r_gtao_strength = {"r_gtao_strength", "0.6", CVAR_ARCHIVE}; cvar_t r_gtao_debug = {"r_gtao_debug", "0", CVAR_NONE}; -cvar_t r_gtao_normal_mode = {"r_gtao_normal_mode", "2", CVAR_ARCHIVE}; cvar_t r_gtao_quality = {"r_gtao_quality", "2", CVAR_ARCHIVE}; -cvar_t r_gtao_noise_mode = {"r_gtao_noise_mode", "0", CVAR_ARCHIVE}; -cvar_t r_gtao_depth_prefilter = {"r_gtao_depth_prefilter", "1", CVAR_ARCHIVE}; -cvar_t r_gtao_depth_mip_offset = {"r_gtao_depth_mip_offset", "3.30", CVAR_ARCHIVE}; cvar_t r_gtao_denoise = {"r_gtao_denoise", "2", CVAR_ARCHIVE}; cvar_t r_gtao_bias = {"r_gtao_bias", "0", CVAR_ARCHIVE}; -cvar_t r_gtao_bent_normals = {"r_gtao_bent_normals", "0", CVAR_NONE}; cvar_t r_gtao_multibounce = {"r_gtao_multibounce", "1", CVAR_ARCHIVE}; cvar_t r_gtao_halfres = {"r_gtao_halfres", "1", CVAR_ARCHIVE}; cvar_t r_gtao_liquid_water = {"r_gtao_liquid_water", "1", CVAR_ARCHIVE}; diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index ce40c05fb..b09cb11bf 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -62,19 +62,13 @@ extern cvar_t r_parallelmark; extern cvar_t r_usesops; extern cvar_t r_gtao; extern cvar_t r_gtao_radius; -extern cvar_t r_gtao_radius_multiplier; extern cvar_t r_gtao_falloff; -extern cvar_t r_gtao_thickness; +extern cvar_t r_gtao_thin_occluder_compensation; extern cvar_t r_gtao_strength; extern cvar_t r_gtao_debug; -extern cvar_t r_gtao_normal_mode; extern cvar_t r_gtao_quality; -extern cvar_t r_gtao_noise_mode; -extern cvar_t r_gtao_depth_prefilter; -extern cvar_t r_gtao_depth_mip_offset; extern cvar_t r_gtao_denoise; extern cvar_t r_gtao_bias; -extern cvar_t r_gtao_bent_normals; extern cvar_t r_gtao_multibounce; extern cvar_t r_gtao_halfres; extern cvar_t r_gtao_liquid_water; @@ -2069,7 +2063,7 @@ void R_CreatePipelineLayouts () ZEROED_STRUCT (VkPushConstantRange, push_constant_range); push_constant_range.offset = 0; - push_constant_range.size = 8 * sizeof (uint32_t) + 10 * sizeof (float); + push_constant_range.size = 7 * sizeof (uint32_t) + 8 * sizeof (float); push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; ZEROED_STRUCT (VkPipelineLayoutCreateInfo, pipeline_layout_create_info); @@ -4431,19 +4425,13 @@ void R_RestoreGTAODefaults (void) cvar_t *gtao_cvars[] = { &r_gtao, &r_gtao_radius, - &r_gtao_radius_multiplier, &r_gtao_falloff, - &r_gtao_thickness, + &r_gtao_thin_occluder_compensation, &r_gtao_strength, &r_gtao_debug, - &r_gtao_normal_mode, &r_gtao_quality, - &r_gtao_noise_mode, - &r_gtao_depth_prefilter, - &r_gtao_depth_mip_offset, &r_gtao_denoise, &r_gtao_bias, - &r_gtao_bent_normals, &r_gtao_multibounce, &r_gtao_halfres, &r_gtao_liquid_water, @@ -4536,19 +4524,13 @@ void R_Init (void) Cvar_RegisterVariable (&r_scale); Cvar_RegisterVariable (&r_gtao); Cvar_RegisterVariable (&r_gtao_radius); - Cvar_RegisterVariable (&r_gtao_radius_multiplier); Cvar_RegisterVariable (&r_gtao_falloff); - Cvar_RegisterVariable (&r_gtao_thickness); + Cvar_RegisterVariable (&r_gtao_thin_occluder_compensation); Cvar_RegisterVariable (&r_gtao_strength); Cvar_RegisterVariable (&r_gtao_debug); - Cvar_RegisterVariable (&r_gtao_normal_mode); Cvar_RegisterVariable (&r_gtao_quality); - Cvar_RegisterVariable (&r_gtao_noise_mode); - Cvar_RegisterVariable (&r_gtao_depth_prefilter); - Cvar_RegisterVariable (&r_gtao_depth_mip_offset); Cvar_RegisterVariable (&r_gtao_denoise); Cvar_RegisterVariable (&r_gtao_bias); - Cvar_RegisterVariable (&r_gtao_bent_normals); Cvar_RegisterVariable (&r_gtao_multibounce); Cvar_RegisterVariable (&r_gtao_halfres); Cvar_RegisterVariable (&r_gtao_liquid_water); diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index b82ab8f10..b58974952 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -4029,14 +4029,11 @@ typedef struct gtao_constants_s float projection_z; float projection_w; float radius; - float thickness; - uint32_t normal_mode; + float thin_occluder_compensation; uint32_t debug_mode; uint32_t quality; - float depth_mip_offset; float bias; uint32_t flags; - float radius_multiplier; float falloff_range; } gtao_constants_t; @@ -4152,7 +4149,7 @@ static void GL_GTAODepthPyramid (cb_context_t *cbx, end_rendering_parms_t *parms vkCmdBindDescriptorSets (cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->layout.handle, 0, 1, &vulkan_globals.gtao_depth_desc_sets[mip], 0, NULL); const gtao_depth_constants_t constants = { width, height, vulkan_globals.gtao_projection[2], vulkan_globals.gtao_projection[3], - q_max (1.0f, r_gtao_radius.value) * CLAMP (0.3f, r_gtao_radius_multiplier.value, 3.0f), + q_max (1.0f, r_gtao_radius.value), CLAMP (0.01f, r_gtao_falloff.value, 1.0f)}; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); vkCmdDispatch (cbx->cb, (width + 7) / 8, (height + 7) / 8, 1); @@ -4186,8 +4183,8 @@ typedef struct gtao_denoise_constants_s static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) { - const uint32_t debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 6); - const uint32_t pass_count = (debug_mode == 0u || debug_mode == 6u) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u; + const uint32_t debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 5); + const uint32_t pass_count = (debug_mode == 0u || debug_mode == 5u) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u; if (pass_count == 0) return; @@ -4329,15 +4326,11 @@ static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) .projection_z = vulkan_globals.gtao_projection[2], .projection_w = vulkan_globals.gtao_projection[3], .radius = q_max (1.0f, r_gtao_radius.value), - .thickness = q_max (0.0f, r_gtao_thickness.value), - .normal_mode = (uint32_t)CLAMP (0, (int)r_gtao_normal_mode.value, 2), - .debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 6), + .thin_occluder_compensation = q_max (0.0f, r_gtao_thin_occluder_compensation.value), + .debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 4), .quality = (uint32_t)CLAMP (0, (int)r_gtao_quality.value, 3), - .depth_mip_offset = r_gtao_depth_prefilter.value > 0.0f ? CLAMP (0.0f, r_gtao_depth_mip_offset.value, 8.0f) : 64.0f, .bias = CLAMP (0.0f, r_gtao_bias.value, 0.5f), - .flags = (r_gtao_bent_normals.value > 0.0f ? 1u : 0u) | (r_gtao_halfres.value > 0.0f ? 4u : 0u) | - (r_gtao_noise_mode.value > 0.0f ? 8u : 0u), - .radius_multiplier = CLAMP (0.3f, r_gtao_radius_multiplier.value, 3.0f), + .flags = r_gtao_halfres.value > 0.0f ? 1u : 0u, .falloff_range = CLAMP (0.01f, r_gtao_falloff.value, 1.0f), }; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (push_constants), &push_constants); @@ -4470,9 +4463,9 @@ static void GL_ScreenEffects (cb_context_t *cbx, qboolean enabled, end_rendering if (R_GTAOEnabled ()) { push_constants.gtao_strength = r_gtao_strength.value; - push_constants.gtao_debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 7); + push_constants.gtao_debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 6); push_constants.gtao_denoise = - (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 6) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u; + (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 5) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u; push_constants.gtao_multibounce = CLAMP (0.0f, r_gtao_multibounce.value, 1.0f); push_constants.gtao_halfres = r_gtao_halfres.value > 0.0f ? 1u : 0u; push_constants.gtao_liquid_water = diff --git a/Quake/glquake.h b/Quake/glquake.h index afe05e242..6ff4db38c 100644 --- a/Quake/glquake.h +++ b/Quake/glquake.h @@ -605,19 +605,13 @@ extern cvar_t r_novis; extern cvar_t r_scale; extern cvar_t r_gtao; extern cvar_t r_gtao_radius; -extern cvar_t r_gtao_radius_multiplier; extern cvar_t r_gtao_falloff; -extern cvar_t r_gtao_thickness; +extern cvar_t r_gtao_thin_occluder_compensation; extern cvar_t r_gtao_strength; extern cvar_t r_gtao_debug; -extern cvar_t r_gtao_normal_mode; extern cvar_t r_gtao_quality; -extern cvar_t r_gtao_noise_mode; -extern cvar_t r_gtao_depth_prefilter; -extern cvar_t r_gtao_depth_mip_offset; extern cvar_t r_gtao_denoise; extern cvar_t r_gtao_bias; -extern cvar_t r_gtao_bent_normals; extern cvar_t r_gtao_multibounce; extern cvar_t r_gtao_halfres; extern cvar_t r_gtao_liquid_water; diff --git a/Shaders/gtao.comp b/Shaders/gtao.comp index 8c58979e7..fa339a116 100644 --- a/Shaders/gtao.comp +++ b/Shaders/gtao.comp @@ -13,14 +13,11 @@ layout (push_constant) uniform GTAOConsts uvec2 viewport_size; vec4 projection; float radius; - float thickness; - uint normal_mode; + float thin_occluder_compensation; uint debug_mode; uint quality; - float depth_mip_offset; float bias; uint flags; - float radius_multiplier; float falloff_range; } push_constants; @@ -31,9 +28,8 @@ const float PI = 3.14159265358979323846f; const float HALF_PI = 1.57079632679489661923f; const uint STENCIL_MASK_SKY = 0x1u; const uint STENCIL_MASK_VIEWMODEL = 0x2u; -const uint GTAO_FLAG_BENT_NORMALS = 0x1u; -const uint GTAO_FLAG_HALFRES = 0x4u; -const uint GTAO_FLAG_HILBERT_NOISE = 0x8u; +const uint GTAO_FLAG_HALFRES = 0x1u; +const float DEPTH_MIP_OFFSET = 3.30f; float sample_depth (ivec2 pixel) { @@ -79,10 +75,10 @@ float sample_horizon_depth (ivec2 pixel, float offset_length, out bool valid) valid = inside_viewport (pixel) && depth > 0.00001f; if (valid) valid = (sample_stencil (pixel) & (STENCIL_MASK_SKY | STENCIL_MASK_VIEWMODEL)) == 0u; - if (push_constants.depth_mip_offset > 32.0f || !valid) + if (!valid) return depth; - float mip_level = clamp (log2 (max (offset_length, 1.0f)) - push_constants.depth_mip_offset, 0.0f, float (textureQueryLevels (view_depth_pyramid_tex) - 1)); + float mip_level = clamp (log2 (max (offset_length, 1.0f)) - DEPTH_MIP_OFFSET, 0.0f, float (textureQueryLevels (view_depth_pyramid_tex) - 1)); int mip_0 = int (floor (mip_level)); int mip_1 = min (mip_0 + 1, textureQueryLevels (view_depth_pyramid_tex) - 1); vec2 uv = (vec2 (pixel) + 0.5f) / vec2 (textureSize (view_depth_pyramid_tex, 0)); @@ -118,52 +114,6 @@ vec3 safe_normalize (vec3 value) return value * inversesqrt (max (dot (value, value), 0.00000001f)); } -uint hilbert_index (uint pos_x, uint pos_y) -{ - uint index = 0u; - for (uint level = 32u; level > 0u; level /= 2u) - { - uint region_x = (pos_x & level) != 0u ? 1u : 0u; - uint region_y = (pos_y & level) != 0u ? 1u : 0u; - index += level * level * ((3u * region_x) ^ region_y); - if (region_y == 0u) - { - if (region_x != 0u) - { - pos_x = 63u - pos_x; - pos_y = 63u - pos_y; - } - uint temp = pos_x; - pos_x = pos_y; - pos_y = temp; - } - } - return index; -} - -mat3 rotation_from_to (vec3 from, vec3 to) -{ - float c = dot (from, to); - if (abs (c) > 0.9997f) - return mat3 (1.0f); - vec3 v = cross (from, to); - float h = 1.0f / (1.0f + c); - mat3 rows = mat3 ( - c + h * v.x * v.x, h * v.x * v.y - v.z, h * v.x * v.z + v.y, - h * v.x * v.y + v.z, c + h * v.y * v.y, h * v.y * v.z - v.x, - h * v.x * v.z - v.y, h * v.y * v.z + v.x, c + h * v.z * v.z); - return transpose (rows); -} - -vec2 oct_encode (vec3 normal) -{ - normal /= abs (normal.x) + abs (normal.y) + abs (normal.z); - vec2 encoded = normal.xy; - if (normal.z < 0.0f) - encoded = (1.0f - abs (encoded.yx)) * sign (encoded.xy); - return encoded * 0.5f + 0.5f; -} - vec4 classification_color (uint stencil, bool has_depth, bool in_viewport) { if (!in_viewport) @@ -257,45 +207,15 @@ void main () edges_lrtb *= vec4 (edge_left_valid, edge_right_valid, edge_top_valid, edge_bottom_valid); float packed_edges = pack_edges (edges_lrtb); - vec3 dx; - vec3 dy; - if (push_constants.normal_mode == 0u) - { - dx = normal_right - normal_left; - dy = normal_top - normal_bottom; - } - else if (push_constants.normal_mode == 1u) - { - if (normal_left_valid && normal_right_valid) - dx = abs (normal_right.z - center.z) <= abs (normal_left.z - center.z) ? normal_right - center : center - normal_left; - else if (normal_right_valid) - dx = normal_right - center; - else - dx = center - normal_left; - - if (normal_top_valid && normal_bottom_valid) - dy = abs (normal_top.z - center.z) <= abs (normal_bottom.z - center.z) ? normal_top - center : center - normal_bottom; - else if (normal_top_valid) - dy = normal_top - center; - else - dy = center - normal_bottom; - } - else - { - vec4 accepted = clamp (vec4 ( - normal_edges_lrtb.x * normal_edges_lrtb.z, normal_edges_lrtb.z * normal_edges_lrtb.y, - normal_edges_lrtb.y * normal_edges_lrtb.w, normal_edges_lrtb.w * normal_edges_lrtb.x) + 0.01f, 0.0f, 1.0f); - vec3 dir_left = safe_normalize (normal_left - center); - vec3 dir_right = safe_normalize (normal_right - center); - vec3 dir_top = safe_normalize (normal_top - center); - vec3 dir_bottom = safe_normalize (normal_bottom - center); - vec3 xe_normal = accepted.x * cross (dir_left, dir_top) + accepted.y * cross (dir_top, dir_right) + - accepted.z * cross (dir_right, dir_bottom) + accepted.w * cross (dir_bottom, dir_left); - dx = xe_normal; - dy = vec3 (0.0f, 0.0f, 1.0f); - } - - vec3 normal_cross = push_constants.normal_mode == 2u ? dx : cross (dx, dy); + vec4 accepted = clamp (vec4 ( + normal_edges_lrtb.x * normal_edges_lrtb.z, normal_edges_lrtb.z * normal_edges_lrtb.y, + normal_edges_lrtb.y * normal_edges_lrtb.w, normal_edges_lrtb.w * normal_edges_lrtb.x) + 0.01f, 0.0f, 1.0f); + vec3 dir_left = safe_normalize (normal_left - center); + vec3 dir_right = safe_normalize (normal_right - center); + vec3 dir_top = safe_normalize (normal_top - center); + vec3 dir_bottom = safe_normalize (normal_bottom - center); + vec3 normal_cross = accepted.x * cross (dir_left, dir_top) + accepted.y * cross (dir_top, dir_right) + + accepted.z * cross (dir_right, dir_bottom) + accepted.w * cross (dir_bottom, dir_left); vec3 view_vector = normalize (-center); vec3 normal = dot (normal_cross, normal_cross) > 0.00000001f ? normalize (normal_cross) : view_vector; if (dot (normal, view_vector) < 0.0f) @@ -316,28 +236,17 @@ void main () // Noise belongs to the compact AO working grid. ivec2 noise_pixel = ao_pixel; noise_pixel &= ivec2 (63); - vec2 noise; - if ((push_constants.flags & GTAO_FLAG_HILBERT_NOISE) != 0u) - { - uint noise_index = hilbert_index (uint (noise_pixel.x), uint (noise_pixel.y)); - noise = fract (vec2 (0.5f) + float (noise_index) * vec2 (0.7548776662466928f, 0.5698402909980533f)); - } - else - { - noise = texelFetch (blue_noise_tex, noise_pixel, 0).rg; - } - // vkQuake has no TAA to absorb an animated sampling pattern. Reprojection may - // accumulate a fixed spatial pattern, but must not make the raw AO vary at rest. + vec2 noise = texelFetch (blue_noise_tex, noise_pixel, 0).rg; float noise_slice = noise.x; float noise_sample = noise.y; - float effect_radius = max (push_constants.radius, 1.0f) * push_constants.radius_multiplier; + float effect_radius = max (push_constants.radius, 1.0f); float falloff_range = effect_radius * push_constants.falloff_range; float falloff_from = effect_radius - falloff_range; float falloff_mul = -1.0f / falloff_range; float falloff_add = falloff_from / falloff_range + 1.0f; float pixel_size_x = max ((-center.z) * push_constants.projection.x * 2.0f / float (push_constants.viewport_size.x), 0.00001f); float pixel_size_y = max ((-center.z) * push_constants.projection.y * 2.0f / float (push_constants.viewport_size.y), 0.00001f); - if (push_constants.debug_mode == 5u) + if (push_constants.debug_mode == 4u) { float footprint = effect_radius / min (pixel_size_x, pixel_size_y); float mip = float (clamp (int (floor (log2 (max (footprint, 1.0f)))) - 1, 0, textureQueryLevels (view_depth_pyramid_tex) - 1)); @@ -347,9 +256,6 @@ void main () } float screen_space_radius = effect_radius / pixel_size_x; float visibility = clamp ((10.0f - screen_space_radius) / 100.0f, 0.0f, 1.0f) * 0.5f; - bool compute_bent_normal = (push_constants.flags & GTAO_FLAG_BENT_NORMALS) != 0u || push_constants.debug_mode == 4u; - vec3 bent_sum = vec3 (0.0f); - // Use a denser 4x4 spatial tier for vkQuake's non-TAA default; retain // XeGTAO's low, medium and ultra tiers at 1x2, 2x2 and 9x3 respectively. int slice_count = push_constants.quality == 0u ? 1 : (push_constants.quality == 1u ? 2 : (push_constants.quality == 2u ? 4 : 9)); @@ -398,7 +304,7 @@ void main () float sample_distance_0 = length (sample_delta_0); if (sample_distance_0 > 0.00001f) { - float falloff_base_0 = length (vec3 (sample_delta_0.xy, sample_delta_0.z * (1.0f + max (push_constants.thickness, 0.0f)))); + float falloff_base_0 = length (vec3 (sample_delta_0.xy, sample_delta_0.z * (1.0f + max (push_constants.thin_occluder_compensation, 0.0f)))); float weight_0 = clamp (falloff_base_0 * falloff_mul + falloff_add, 0.0f, 1.0f); float sample_horizon_cos_0 = dot (sample_delta_0 / sample_distance_0, view_vector) - clamp (push_constants.bias, 0.0f, 0.5f); sample_horizon_cos_0 = mix (low_horizon_cos_0, sample_horizon_cos_0, weight_0); @@ -418,7 +324,7 @@ void main () float sample_distance_1 = length (sample_delta_1); if (sample_distance_1 > 0.00001f) { - float falloff_base_1 = length (vec3 (sample_delta_1.xy, sample_delta_1.z * (1.0f + max (push_constants.thickness, 0.0f)))); + float falloff_base_1 = length (vec3 (sample_delta_1.xy, sample_delta_1.z * (1.0f + max (push_constants.thin_occluder_compensation, 0.0f)))); float weight_1 = clamp (falloff_base_1 * falloff_mul + falloff_add, 0.0f, 1.0f); float sample_horizon_cos_1 = dot (sample_delta_1 / sample_distance_1, view_vector) - clamp (push_constants.bias, 0.0f, 0.5f); sample_horizon_cos_1 = mix (low_horizon_cos_1, sample_horizon_cos_1, weight_1); @@ -434,24 +340,9 @@ void main () float integrated_arc_0 = (cos_normal + 2.0f * h0 * sin (n) - cos (2.0f * h0 - n)) * 0.25f; float integrated_arc_1 = (cos_normal + 2.0f * h1 * sin (n) - cos (2.0f * h1 - n)) * 0.25f; visibility += projected_normal_length * (integrated_arc_0 + integrated_arc_1); - if (compute_bent_normal) - { - float t0 = (6.0f * sin (h0 - n) - sin (3.0f * h0 - n) + 6.0f * sin (h1 - n) - sin (3.0f * h1 - n) + - 16.0f * sin (n) - 3.0f * (sin (h0 + n) + sin (h1 + n))) / 12.0f; - float t1 = (-cos (3.0f * h0 - n) - cos (3.0f * h1 - n) + 8.0f * cos (n) - - 3.0f * (cos (h0 + n) + cos (h1 + n))) / 12.0f; - vec3 local_bent = vec3 (direction_vector.xy * t0, t1); - bent_sum += rotation_from_to (vec3 (0.0f, 0.0f, 1.0f), view_vector) * local_bent * projected_normal_length; - } } visibility = pow (max (visibility / float (slice_count), 0.0f), 2.2f); visibility = max (visibility, 0.03f); - if (push_constants.debug_mode == 4u) - { - imageStore (ao_image, ao_pixel, vec4 (safe_normalize (bent_sum) * 0.5f + 0.5f, 1.0f)); - return; - } - vec2 packed_bent = compute_bent_normal ? oct_encode (safe_normalize (bent_sum)) : vec2 (0.0f); - imageStore (ao_image, ao_pixel, vec4 (visibility, packed_edges, packed_bent)); + imageStore (ao_image, ao_pixel, vec4 (visibility, packed_edges, 0.0f, 0.0f)); } diff --git a/Shaders/screen_effects.inc b/Shaders/screen_effects.inc index f92ba45e8..893f9fb87 100644 --- a/Shaders/screen_effects.inc +++ b/Shaders/screen_effects.inc @@ -292,9 +292,9 @@ void main () const ivec2 liquid_pixel = gtao_warped ? clamp (ivec2 (effect_texcoord / push_constants.screen_size_rcp), ivec2 (0), ivec2 (push_constants.clamp_size)) : ao_pixel; const uint liquid_mask = gtao_liquid_mask (liquid_pixel); - if (push_constants.gtao_debug_mode == 7u) + if (push_constants.gtao_debug_mode == 6u) color.rgb = gtao_liquid_debug_color (liquid_mask); - else if (push_constants.gtao_debug_mode == 1u || push_constants.gtao_debug_mode == 6u) + else if (push_constants.gtao_debug_mode == 1u || push_constants.gtao_debug_mode == 5u) color.rgb = vec3 (clamp (ao_sample.r, 0.0f, 1.0f)); else if (push_constants.gtao_debug_mode > 1u) color.rgb = ao_sample.rgb; From f876ddb303bcfc119679d3ecd323b0e89ce2826b Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 22:10:49 -0500 Subject: [PATCH 13/26] Reuse GTAO horizon depth samples --- Shaders/gtao.comp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Shaders/gtao.comp b/Shaders/gtao.comp index fa339a116..012b236bb 100644 --- a/Shaders/gtao.comp +++ b/Shaders/gtao.comp @@ -79,6 +79,8 @@ float sample_horizon_depth (ivec2 pixel, float offset_length, out bool valid) return depth; float mip_level = clamp (log2 (max (offset_length, 1.0f)) - DEPTH_MIP_OFFSET, 0.0f, float (textureQueryLevels (view_depth_pyramid_tex) - 1)); + if (mip_level <= 0.0f) + return depth; int mip_0 = int (floor (mip_level)); int mip_1 = min (mip_0 + 1, textureQueryLevels (view_depth_pyramid_tex) - 1); vec2 uv = (vec2 (pixel) + 0.5f) / vec2 (textureSize (view_depth_pyramid_tex, 0)); @@ -86,7 +88,12 @@ float sample_horizon_depth (ivec2 pixel, float offset_length, out bool valid) ivec2 size_1 = textureSize (view_depth_pyramid_tex, mip_1); ivec2 coord_0 = clamp (ivec2 (uv * vec2 (size_0)), ivec2 (0), size_0 - 1); ivec2 coord_1 = clamp (ivec2 (uv * vec2 (size_1)), ivec2 (0), size_1 - 1); - float depth_0 = texelFetch (view_depth_pyramid_tex, coord_0, mip_0).r; + float depth_0 = mip_0 == 0 ? depth : texelFetch (view_depth_pyramid_tex, coord_0, mip_0).r; + if (mip_0 == mip_1) + { + valid = depth_0 > 0.00001f; + return valid ? depth_0 : depth; + } float depth_1 = texelFetch (view_depth_pyramid_tex, coord_1, mip_1).r; float view_depth = mix (depth_0, depth_1, fract (mip_level)); valid = view_depth > 0.00001f; From 4cad680c2db21e45170c50b4d500c03553d74f3d Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 22:22:54 -0500 Subject: [PATCH 14/26] Compact half-resolution GTAO resources --- Quake/gl_vidsdl.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index b58974952..93e993efd 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -2127,13 +2127,15 @@ static void GL_CreateGTAOBuffer (void) return; VkResult err; + const uint32_t gtao_width = r_gtao_halfres.value > 0.0f ? (vid.width + 1) / 2 : vid.width; + const uint32_t gtao_height = r_gtao_halfres.value > 0.0f ? (vid.height + 1) / 2 : vid.height; ZEROED_STRUCT (VkImageCreateInfo, image_create_info); image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_create_info.imageType = VK_IMAGE_TYPE_2D; image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; - image_create_info.extent.width = vid.width; - image_create_info.extent.height = vid.height; + image_create_info.extent.width = gtao_width; + image_create_info.extent.height = gtao_height; image_create_info.extent.depth = 1; image_create_info.mipLevels = 1; image_create_info.arrayLayers = 1; @@ -2186,6 +2188,8 @@ static void GL_CreateGTAOBuffer (void) GL_SetObjectName ((uint64_t)gtao_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "GTAO Buffer View"); gtao_buffer_initialized = false; + image_create_info.extent.width = vid.width; + image_create_info.extent.height = vid.height; image_create_info.format = gtao_liquid_mask_format; image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; err = vkCreateImage (vulkan_globals.device, &image_create_info, NULL, >ao_liquid_mask_buffer); @@ -2210,6 +2214,8 @@ static void GL_CreateGTAOBuffer (void) GL_SetObjectName ((uint64_t)gtao_liquid_mask_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "GTAO Liquid Mask Buffer View"); gtao_liquid_mask_buffer_initialized = false; + image_create_info.extent.width = gtao_width; + image_create_info.extent.height = gtao_height; image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; image_view_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; @@ -2234,6 +2240,8 @@ static void GL_CreateGTAOBuffer (void) GL_SetObjectName ((uint64_t)gtao_denoise_buffer_view, VK_OBJECT_TYPE_IMAGE_VIEW, "GTAO Denoise Buffer View"); gtao_denoise_buffer_initialized = false; + image_create_info.extent.width = vid.width; + image_create_info.extent.height = vid.height; image_create_info.format = VK_FORMAT_R32_SFLOAT; image_create_info.mipLevels = GTAO_DEPTH_MIP_LEVELS; image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; @@ -3891,6 +3899,7 @@ static oit_mode_t GL_FrameOITModeForCvarValue (int r_oit_value) qboolean GL_BeginRendering (qboolean use_tasks, task_handle_t *begin_rendering_task, int *width, int *height) { + static qboolean frame_gtao_halfres; if (!use_tasks) GL_SynchronizeEndRenderingTask (); @@ -3899,8 +3908,10 @@ qboolean GL_BeginRendering (qboolean use_tasks, task_handle_t *begin_rendering_t const qboolean oit_mode_changed = (requested_oit_mode != frame_oit_mode); const qboolean requested_gtao_enabled = gtao_supported && r_gtao.value > 0.0f; const qboolean gtao_mode_changed = requested_gtao_enabled != frame_gtao_enabled; + const qboolean requested_gtao_halfres = requested_gtao_enabled && r_gtao_halfres.value > 0.0f; + const qboolean gtao_size_changed = requested_gtao_halfres != frame_gtao_halfres; - if (vid.restart_next_frame || (render_resources_created && (oit_mode_changed || gtao_mode_changed))) + if (vid.restart_next_frame || (render_resources_created && (oit_mode_changed || gtao_mode_changed || gtao_size_changed))) { // Finish the previous frame before changing the configuration it observes. // VID_Restart synchronizes too, but the frame flags must remain unchanged @@ -3908,6 +3919,7 @@ qboolean GL_BeginRendering (qboolean use_tasks, task_handle_t *begin_rendering_t GL_SynchronizeEndRenderingTask (); frame_oit_mode = requested_oit_mode; frame_gtao_enabled = requested_gtao_enabled; + frame_gtao_halfres = requested_gtao_halfres; VID_Restart (false); vid.restart_next_frame = false; } @@ -3915,6 +3927,7 @@ qboolean GL_BeginRendering (qboolean use_tasks, task_handle_t *begin_rendering_t { frame_oit_mode = requested_oit_mode; frame_gtao_enabled = requested_gtao_enabled; + frame_gtao_halfres = requested_gtao_halfres; } if (!render_resources_created) From 20f0baa55e5fa9264664136c20f7bb77c5849625 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 22:26:27 -0500 Subject: [PATCH 15/26] Skip GTAO denoising in liquid-mask debug mode --- Quake/gl_vidsdl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index 93e993efd..d6930fc38 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -4196,7 +4196,7 @@ typedef struct gtao_denoise_constants_s static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) { - const uint32_t debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 5); + const uint32_t debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 6); const uint32_t pass_count = (debug_mode == 0u || debug_mode == 5u) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u; if (pass_count == 0) return; From ca8e9069d6763ffc00ed8cebead3ac47df47de0a Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 22:32:29 -0500 Subject: [PATCH 16/26] Tie GTAO MSAA classification to resolved depth --- Shaders/gtao_depth.comp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Shaders/gtao_depth.comp b/Shaders/gtao_depth.comp index 76174b525..d1a59860a 100644 --- a/Shaders/gtao_depth.comp +++ b/Shaders/gtao_depth.comp @@ -71,13 +71,25 @@ void main () float device_depth = 0.0f; uint stencil = 0u; #ifdef GTAO_DEPTH_MSAA + float closest_sample_depth = -1.0f; + uint closest_sample_stencil = 0u; + uint selected_stencil = 0u; for (int sample_index = 0; sample_index < textureSamples (input_depth_tex); ++sample_index) { uint sample_stencil = texelFetch (input_stencil_tex, pixel, sample_index).r; - stencil |= sample_stencil; - if ((sample_stencil & STENCIL_EXCLUDED) == 0u) - device_depth = max (device_depth, texelFetch (input_depth_tex, pixel, sample_index).r); + float sample_depth = texelFetch (input_depth_tex, pixel, sample_index).r; + if (sample_depth > closest_sample_depth) + { + closest_sample_depth = sample_depth; + closest_sample_stencil = sample_stencil; + } + if ((sample_stencil & STENCIL_EXCLUDED) == 0u && sample_depth > device_depth) + { + device_depth = sample_depth; + selected_stencil = sample_stencil; + } } + stencil = device_depth > 0.0f ? selected_stencil : closest_sample_stencil; #else stencil = texelFetch (input_stencil_tex, pixel, 0).r; if ((stencil & STENCIL_EXCLUDED) == 0u) From f286c2a0733cda206543a778cd14fb24ff2dcc9b Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 22:36:49 -0500 Subject: [PATCH 17/26] Add depth-aware half-resolution GTAO reconstruction --- Quake/gl_rmain.c | 1 + Quake/gl_rmisc.c | 15 ++++++--- Quake/gl_vidsdl.c | 27 ++++++++++++---- Quake/glquake.h | 1 + Shaders/gtao.comp | 37 +++++++++++++++++---- Shaders/gtao_denoise.comp | 66 ++++++++++++++++++++++++++++++++++---- Shaders/screen_effects.inc | 54 ++++++++++++++++++++++++++++++- 7 files changed, 176 insertions(+), 25 deletions(-) diff --git a/Quake/gl_rmain.c b/Quake/gl_rmain.c index 6ecaf20a0..81b650b3a 100644 --- a/Quake/gl_rmain.c +++ b/Quake/gl_rmain.c @@ -126,6 +126,7 @@ cvar_t r_gtao_denoise = {"r_gtao_denoise", "2", CVAR_ARCHIVE}; cvar_t r_gtao_bias = {"r_gtao_bias", "0", CVAR_ARCHIVE}; cvar_t r_gtao_multibounce = {"r_gtao_multibounce", "1", CVAR_ARCHIVE}; cvar_t r_gtao_halfres = {"r_gtao_halfres", "1", CVAR_ARCHIVE}; +cvar_t r_gtao_halfres_depth_aware = {"r_gtao_halfres_depth_aware", "0", CVAR_ARCHIVE}; cvar_t r_gtao_liquid_water = {"r_gtao_liquid_water", "1", CVAR_ARCHIVE}; cvar_t r_gtao_liquid_slime = {"r_gtao_liquid_slime", "1", CVAR_ARCHIVE}; cvar_t r_gtao_liquid_lava = {"r_gtao_liquid_lava", "1", CVAR_ARCHIVE}; diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index b09cb11bf..5cdc0ea2e 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -71,6 +71,7 @@ extern cvar_t r_gtao_denoise; extern cvar_t r_gtao_bias; extern cvar_t r_gtao_multibounce; extern cvar_t r_gtao_halfres; +extern cvar_t r_gtao_halfres_depth_aware; extern cvar_t r_gtao_liquid_water; extern cvar_t r_gtao_liquid_slime; extern cvar_t r_gtao_liquid_lava; @@ -1564,7 +1565,7 @@ void R_CreateDescriptorSetLayouts () } { - ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, bindings, 2); + ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, bindings, 3); bindings[0].binding = 0; bindings[0].descriptorCount = 1; bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; @@ -1573,10 +1574,14 @@ void R_CreateDescriptorSetLayouts () bindings[1].descriptorCount = 1; bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + bindings[2].binding = 2; + bindings[2].descriptorCount = 1; + bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + bindings[2].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; descriptor_set_layout_create_info.bindingCount = countof (bindings); descriptor_set_layout_create_info.pBindings = bindings; memset (&vulkan_globals.gtao_denoise_set_layout, 0, sizeof (vulkan_globals.gtao_denoise_set_layout)); - vulkan_globals.gtao_denoise_set_layout.num_combined_image_samplers = 1; + vulkan_globals.gtao_denoise_set_layout.num_combined_image_samplers = 2; vulkan_globals.gtao_denoise_set_layout.num_storage_images = 1; err = vkCreateDescriptorSetLayout (vulkan_globals.device, &descriptor_set_layout_create_info, NULL, &vulkan_globals.gtao_denoise_set_layout.handle); if (err != VK_SUCCESS) @@ -2034,7 +2039,7 @@ void R_CreatePipelineLayouts () ZEROED_STRUCT (VkPushConstantRange, push_constant_range); push_constant_range.offset = 0; - push_constant_range.size = 6 * sizeof (uint32_t) + 14 * sizeof (float); + push_constant_range.size = 7 * sizeof (uint32_t) + 14 * sizeof (float); push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; ZEROED_STRUCT (VkPipelineLayoutCreateInfo, pipeline_layout_create_info); @@ -2107,7 +2112,7 @@ void R_CreatePipelineLayouts () VkDescriptorSetLayout set_layouts[1] = {vulkan_globals.gtao_denoise_set_layout.handle}; ZEROED_STRUCT (VkPushConstantRange, push_constant_range); push_constant_range.offset = 0; - push_constant_range.size = 4 * sizeof (uint32_t); + push_constant_range.size = 5 * sizeof (uint32_t); push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; ZEROED_STRUCT (VkPipelineLayoutCreateInfo, pipeline_layout_create_info); pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; @@ -4434,6 +4439,7 @@ void R_RestoreGTAODefaults (void) &r_gtao_bias, &r_gtao_multibounce, &r_gtao_halfres, + &r_gtao_halfres_depth_aware, &r_gtao_liquid_water, &r_gtao_liquid_slime, &r_gtao_liquid_lava, @@ -4533,6 +4539,7 @@ void R_Init (void) Cvar_RegisterVariable (&r_gtao_bias); Cvar_RegisterVariable (&r_gtao_multibounce); Cvar_RegisterVariable (&r_gtao_halfres); + Cvar_RegisterVariable (&r_gtao_halfres_depth_aware); Cvar_RegisterVariable (&r_gtao_liquid_water); Cvar_RegisterVariable (&r_gtao_liquid_slime); Cvar_RegisterVariable (&r_gtao_liquid_lava); diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index d6930fc38..35b44d2de 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -2954,14 +2954,17 @@ void GL_UpdateDescriptorSets (void) for (uint32_t pass = 0; pass < 2; ++pass) { vulkan_globals.gtao_denoise_desc_sets[pass] = R_AllocateDescriptorSet (&vulkan_globals.gtao_denoise_set_layout); - ZEROED_STRUCT_ARRAY (VkDescriptorImageInfo, image_infos, 2); + ZEROED_STRUCT_ARRAY (VkDescriptorImageInfo, image_infos, 3); image_infos[0].imageView = pass == 0 ? gtao_buffer_view : gtao_denoise_buffer_view; image_infos[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image_infos[0].sampler = vulkan_globals.point_sampler; image_infos[1].imageView = pass == 0 ? gtao_denoise_buffer_view : gtao_buffer_view; image_infos[1].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, writes, 2); - for (uint32_t binding = 0; binding < 2; ++binding) + image_infos[2].imageView = gtao_depth_pyramid_view; + image_infos[2].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_infos[2].sampler = vulkan_globals.point_sampler; + ZEROED_STRUCT_ARRAY (VkWriteDescriptorSet, writes, 3); + for (uint32_t binding = 0; binding < 3; ++binding) { writes[binding].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[binding].dstSet = vulkan_globals.gtao_denoise_desc_sets[pass]; @@ -4025,6 +4028,7 @@ typedef struct screen_effect_constants_s uint32_t gtao_denoise; float gtao_multibounce; uint32_t gtao_halfres; + uint32_t gtao_halfres_depth_aware; float gtao_liquid_water; float gtao_liquid_slime; float gtao_liquid_lava; @@ -4192,6 +4196,7 @@ typedef struct gtao_denoise_constants_s uint32_t clamp_height; uint32_t sample_stride; float center_weight; + uint32_t depth_aware; } gtao_denoise_constants_t; static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) @@ -4222,7 +4227,13 @@ static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) const uint32_t working_stride = r_gtao_halfres.value > 0.0f ? 2u : 1u; const uint32_t working_width = (parms->vid_width + working_stride - 1) / working_stride; const uint32_t working_height = (parms->vid_height + working_stride - 1) / working_stride; - gtao_denoise_constants_t constants = {working_width - 1, working_height - 1, 1u, pass_count == 1 ? 1.2f : 0.24f}; + gtao_denoise_constants_t constants = { + working_width - 1, + working_height - 1, + 1u, + pass_count == 1 ? 1.2f : 0.24f, + r_gtao_halfres.value > 0.0f && r_gtao_halfres_depth_aware.value > 0.0f ? 1u : 0u, + }; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); vkCmdDispatch (cbx->cb, (working_width + 15) / 16, (working_height + 7) / 8, 1); @@ -4292,6 +4303,7 @@ GL_GTAO */ static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) { + const int gtao_debug = CLAMP (0, (int)r_gtao_debug.value, 6); R_BeginDebugUtilsLabel (cbx, "GTAO"); ZEROED_STRUCT_ARRAY (VkImageMemoryBarrier, image_barriers, 2); @@ -4340,10 +4352,11 @@ static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) .projection_w = vulkan_globals.gtao_projection[3], .radius = q_max (1.0f, r_gtao_radius.value), .thin_occluder_compensation = q_max (0.0f, r_gtao_thin_occluder_compensation.value), - .debug_mode = (uint32_t)CLAMP (0, (int)r_gtao_debug.value, 4), + .debug_mode = gtao_debug >= 1 && gtao_debug <= 4 ? (uint32_t)gtao_debug : 0u, .quality = (uint32_t)CLAMP (0, (int)r_gtao_quality.value, 3), .bias = CLAMP (0.0f, r_gtao_bias.value, 0.5f), - .flags = r_gtao_halfres.value > 0.0f ? 1u : 0u, + .flags = (r_gtao_halfres.value > 0.0f ? 1u : 0u) | + (r_gtao_halfres.value > 0.0f && r_gtao_halfres_depth_aware.value > 0.0f ? 2u : 0u), .falloff_range = CLAMP (0.01f, r_gtao_falloff.value, 1.0f), }; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (push_constants), &push_constants); @@ -4481,6 +4494,8 @@ static void GL_ScreenEffects (cb_context_t *cbx, qboolean enabled, end_rendering (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 5) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u; push_constants.gtao_multibounce = CLAMP (0.0f, r_gtao_multibounce.value, 1.0f); push_constants.gtao_halfres = r_gtao_halfres.value > 0.0f ? 1u : 0u; + push_constants.gtao_halfres_depth_aware = + r_gtao_halfres.value > 0.0f && r_gtao_halfres_depth_aware.value > 0.0f ? 1u : 0u; push_constants.gtao_liquid_water = CLAMP (0.0f, r_gtao_liquid_water.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_WATER), 1.0f); push_constants.gtao_liquid_slime = diff --git a/Quake/glquake.h b/Quake/glquake.h index 6ff4db38c..1db92febf 100644 --- a/Quake/glquake.h +++ b/Quake/glquake.h @@ -614,6 +614,7 @@ extern cvar_t r_gtao_denoise; extern cvar_t r_gtao_bias; extern cvar_t r_gtao_multibounce; extern cvar_t r_gtao_halfres; +extern cvar_t r_gtao_halfres_depth_aware; extern cvar_t r_gtao_liquid_water; extern cvar_t r_gtao_liquid_slime; extern cvar_t r_gtao_liquid_lava; diff --git a/Shaders/gtao.comp b/Shaders/gtao.comp index 012b236bb..62f2a55cc 100644 --- a/Shaders/gtao.comp +++ b/Shaders/gtao.comp @@ -29,6 +29,7 @@ const float HALF_PI = 1.57079632679489661923f; const uint STENCIL_MASK_SKY = 0x1u; const uint STENCIL_MASK_VIEWMODEL = 0x2u; const uint GTAO_FLAG_HALFRES = 0x1u; +const uint GTAO_FLAG_HALFRES_DEPTH_AWARE = 0x2u; const float DEPTH_MIP_OFFSET = 3.30f; float sample_depth (ivec2 pixel) @@ -143,6 +144,25 @@ void main () if (any (greaterThanEqual (ao_pixel, working_size)) || any (greaterThanEqual (ao_pixel, imageSize (ao_image)))) return; ivec2 pixel = ao_pixel * working_stride; + uint representative_index = 0u; + if (half_res && (push_constants.flags & GTAO_FLAG_HALFRES_DEPTH_AWARE) != 0u) + { + float closest_depth = 3.402823466e+38f; + for (uint index = 0u; index < 4u; ++index) + { + ivec2 candidate = pixel + ivec2 (int (index & 1u), int (index >> 1u)); + if (!inside_viewport (candidate)) + continue; + float candidate_depth = sample_depth (candidate); + if (valid_surface_sample (candidate, candidate_depth) && candidate_depth < closest_depth) + { + closest_depth = candidate_depth; + representative_index = index; + } + } + pixel += ivec2 (int (representative_index & 1u), int (representative_index >> 1u)); + } + float representative_value = float (representative_index) / 3.0f; bool in_viewport = inside_viewport (pixel); float center_depth = in_viewport ? sample_depth (pixel) : 0.0f; @@ -150,9 +170,10 @@ void main () bool has_depth = center_depth > 0.00001f; if (!in_viewport || !has_depth || (center_stencil & (STENCIL_MASK_SKY | STENCIL_MASK_VIEWMODEL)) != 0u) { - imageStore ( - ao_image, ao_pixel, - push_constants.debug_mode == 3u ? classification_color (center_stencil, has_depth, in_viewport) : vec4 (1.0f)); + vec4 invalid_value = push_constants.debug_mode == 3u + ? classification_color (center_stencil, has_depth, in_viewport) : vec4 (1.0f); + invalid_value.a = representative_value; + imageStore (ao_image, ao_pixel, invalid_value); return; } @@ -231,12 +252,14 @@ void main () if (push_constants.debug_mode == 2u) { - imageStore (ao_image, ao_pixel, vec4 (normal * 0.5f + 0.5f, 1.0f)); + imageStore (ao_image, ao_pixel, vec4 (normal * 0.5f + 0.5f, representative_value)); return; } if (push_constants.debug_mode == 3u) { - imageStore (ao_image, ao_pixel, classification_color (center_stencil, true, true)); + vec4 color = classification_color (center_stencil, true, true); + color.a = representative_value; + imageStore (ao_image, ao_pixel, color); return; } @@ -258,7 +281,7 @@ void main () float footprint = effect_radius / min (pixel_size_x, pixel_size_y); float mip = float (clamp (int (floor (log2 (max (footprint, 1.0f)))) - 1, 0, textureQueryLevels (view_depth_pyramid_tex) - 1)); vec3 mip_color = vec3 (mip / float (textureQueryLevels (view_depth_pyramid_tex) - 1), 1.0f - mip / float (textureQueryLevels (view_depth_pyramid_tex) - 1), 0.2f); - imageStore (ao_image, ao_pixel, vec4 (mip_color, 1.0f)); + imageStore (ao_image, ao_pixel, vec4 (mip_color, representative_value)); return; } float screen_space_radius = effect_radius / pixel_size_x; @@ -351,5 +374,5 @@ void main () visibility = pow (max (visibility / float (slice_count), 0.0f), 2.2f); visibility = max (visibility, 0.03f); - imageStore (ao_image, ao_pixel, vec4 (visibility, packed_edges, 0.0f, 0.0f)); + imageStore (ao_image, ao_pixel, vec4 (visibility, packed_edges, 0.0f, representative_value)); } diff --git a/Shaders/gtao_denoise.comp b/Shaders/gtao_denoise.comp index ae39fd967..4da4425aa 100644 --- a/Shaders/gtao_denoise.comp +++ b/Shaders/gtao_denoise.comp @@ -4,12 +4,14 @@ layout (set = 0, binding = 0) uniform sampler2D input_ao_tex; layout (set = 0, binding = 1, rgba8) uniform writeonly image2D output_ao_image; +layout (set = 0, binding = 2) uniform sampler2D view_depth_tex; layout (push_constant) uniform DenoiseConsts { uvec2 clamp_size; uint sample_stride; float center_weight; + uint depth_aware; } push_constants; @@ -22,11 +24,31 @@ vec4 unpack_edges (float packed_value) float ((packed >> 2) & 3u), float (packed & 3u)) / 3.0f; } +ivec2 representative_offset (vec4 ao_sample) +{ + uint index = uint (round (clamp (ao_sample.a, 0.0f, 1.0f) * 3.0f)); + return ivec2 (int (index & 1u), int (index >> 1u)); +} + +float representative_depth (ivec2 ao_pixel, vec4 ao_sample) +{ + ivec2 depth_pixel = clamp (ao_pixel * 2 + representative_offset (ao_sample), ivec2 (0), textureSize (view_depth_tex, 0) - 1); + return texelFetch (view_depth_tex, depth_pixel, 0).r; +} + +float depth_similarity (float center_depth, float sample_depth) +{ + if (center_depth <= 0.0f || sample_depth <= 0.0f) + return center_depth == sample_depth ? 1.0f : 0.0f; + return clamp (1.25f - abs (sample_depth - center_depth) / max (center_depth * 0.011f, 0.00001f), 0.0f, 1.0f); +} + vec4 denoise_value ( + ivec2 center_pixel, vec4 center, vec4 ao_l, vec4 ao_r, vec4 ao_t, vec4 ao_b, vec4 ao_tl, vec4 ao_tr, vec4 ao_bl, vec4 ao_br) { - if (all (greaterThan (center, vec4 (0.999f)))) + if (all (greaterThan (center.rgb, vec3 (0.999f)))) return center; vec4 edges_l = unpack_edges (ao_l.g); @@ -35,13 +57,40 @@ vec4 denoise_value ( vec4 edges_b = unpack_edges (ao_b.g); vec4 edges_c = unpack_edges (center.g); edges_c *= vec4 (edges_l.y, edges_r.x, edges_t.w, edges_b.z); + vec4 depth_weights = vec4 (1.0f); + vec4 diagonal_depth_weights = vec4 (1.0f); + if (push_constants.depth_aware != 0u) + { + int stride = int (push_constants.sample_stride); + ivec2 limit = ivec2 (push_constants.clamp_size); + ivec2 p_l = clamp (center_pixel + ivec2 (-stride, 0), ivec2 (0), limit); + ivec2 p_r = clamp (center_pixel + ivec2 ( stride, 0), ivec2 (0), limit); + ivec2 p_t = clamp (center_pixel + ivec2 (0, -stride), ivec2 (0), limit); + ivec2 p_b = clamp (center_pixel + ivec2 (0, stride), ivec2 (0), limit); + ivec2 p_tl = clamp (center_pixel + ivec2 (-stride, -stride), ivec2 (0), limit); + ivec2 p_tr = clamp (center_pixel + ivec2 ( stride, -stride), ivec2 (0), limit); + ivec2 p_bl = clamp (center_pixel + ivec2 (-stride, stride), ivec2 (0), limit); + ivec2 p_br = clamp (center_pixel + ivec2 ( stride, stride), ivec2 (0), limit); + float center_depth = representative_depth (center_pixel, center); + depth_weights = vec4 ( + depth_similarity (center_depth, representative_depth (p_l, ao_l)), + depth_similarity (center_depth, representative_depth (p_r, ao_r)), + depth_similarity (center_depth, representative_depth (p_t, ao_t)), + depth_similarity (center_depth, representative_depth (p_b, ao_b))); + diagonal_depth_weights = vec4 ( + depth_similarity (center_depth, representative_depth (p_tl, ao_tl)), + depth_similarity (center_depth, representative_depth (p_tr, ao_tr)), + depth_similarity (center_depth, representative_depth (p_bl, ao_bl)), + depth_similarity (center_depth, representative_depth (p_br, ao_br))); + } float edginess = clamp (1.5f - dot (edges_c, vec4 (1.0f)), 0.0f, 1.5f) / 1.5f * 0.5f; edges_c = clamp (edges_c + edginess, 0.0f, 1.0f); + edges_c *= depth_weights; const float diagonal_weight = 0.85f * 0.5f; - float weight_tl = diagonal_weight * (edges_c.x * edges_l.z + edges_c.z * edges_t.x); - float weight_tr = diagonal_weight * (edges_c.z * edges_t.y + edges_c.y * edges_r.z); - float weight_bl = diagonal_weight * (edges_c.w * edges_b.x + edges_c.x * edges_l.w); - float weight_br = diagonal_weight * (edges_c.y * edges_r.w + edges_c.w * edges_b.y); + float weight_tl = diagonal_weight * (edges_c.x * edges_l.z + edges_c.z * edges_t.x) * diagonal_depth_weights.x; + float weight_tr = diagonal_weight * (edges_c.z * edges_t.y + edges_c.y * edges_r.z) * diagonal_depth_weights.y; + float weight_bl = diagonal_weight * (edges_c.w * edges_b.x + edges_c.x * edges_l.w) * diagonal_depth_weights.z; + float weight_br = diagonal_weight * (edges_c.y * edges_r.w + edges_c.w * edges_b.y) * diagonal_depth_weights.w; float ao_sum = center.r * push_constants.center_weight + ao_l.r * edges_c.x + ao_r.r * edges_c.y + ao_t.r * edges_c.z + ao_b.r * edges_c.w + ao_tl.r * weight_tl + ao_tr.r * weight_tr + ao_bl.r * weight_bl + ao_br.r * weight_br; @@ -63,9 +112,10 @@ vec4 denoise_fetch (ivec2 pixel) ivec2 p_bl = clamp (pixel + ivec2 (-stride, stride), ivec2 (0), limit); ivec2 p_br = clamp (pixel + ivec2 ( stride, stride), ivec2 (0), limit); vec4 center = texelFetch (input_ao_tex, pixel, 0); - if (all (greaterThan (center, vec4 (0.999f)))) + if (all (greaterThan (center.rgb, vec3 (0.999f)))) return center; return denoise_value ( + pixel, center, texelFetch (input_ao_tex, p_l, 0), texelFetch (input_ao_tex, p_r, 0), texelFetch (input_ao_tex, p_t, 0), texelFetch (input_ao_tex, p_b, 0), texelFetch (input_ao_tex, p_tl, 0), texelFetch (input_ao_tex, p_tr, 0), @@ -86,7 +136,7 @@ void main () return; ivec2 pixel_next = pixel_base + ivec2 (stride, 0); - bool pair_inside = stride == 1 && pixel_base.x > 0 && pixel_next.x < limit.x && pixel_base.y > 0 && pixel_base.y < limit.y; + bool pair_inside = push_constants.depth_aware == 0u && stride == 1 && pixel_base.x > 0 && pixel_next.x < limit.x && pixel_base.y > 0 && pixel_base.y < limit.y; if (!pair_inside) { imageStore (output_ao_image, pixel_base, denoise_fetch (pixel_base)); @@ -106,6 +156,7 @@ void main () vec4 center_0 = texelFetch (input_ao_tex, pixel_base, 0); vec4 result_0 = denoise_value ( + pixel_base, center_0, gathered_sample (vis_q0.x, edge_q0.x), gathered_sample (vis_q1.x, edge_q1.x), gathered_sample (vis_q0.z, edge_q0.z), gathered_sample (vis_q2.z, edge_q2.w), @@ -115,6 +166,7 @@ void main () vec4 center_1 = texelFetch (input_ao_tex, pixel_next, 0); vec4 result_1 = denoise_value ( + pixel_next, center_1, gathered_sample (vis_q0.y, edge_q0.y), gathered_sample (vis_q1.y, edge_q1.y), gathered_sample (vis_q1.w, edge_q1.w), gathered_sample (vis_q3.w, edge_q2.z), diff --git a/Shaders/screen_effects.inc b/Shaders/screen_effects.inc index 893f9fb87..c40994c80 100644 --- a/Shaders/screen_effects.inc +++ b/Shaders/screen_effects.inc @@ -36,6 +36,7 @@ layout (push_constant) uniform PushConsts uint gtao_denoise; float gtao_multibounce; uint gtao_halfres; + uint gtao_halfres_depth_aware; float gtao_liquid_water; float gtao_liquid_slime; float gtao_liquid_lava; @@ -112,6 +113,14 @@ vec4 gtao_fetch (ivec2 pixel) return (push_constants.gtao_denoise & 1u) != 0u ? texelFetch (gtao_denoise_tex, pixel, 0) : texelFetch (gtao_tex, pixel, 0); } +ivec2 gtao_representative_offset (vec4 ao_sample) +{ + if (push_constants.gtao_halfres_depth_aware == 0u) + return ivec2 (0); + uint index = uint (round (clamp (ao_sample.a, 0.0f, 1.0f) * 3.0f)); + return ivec2 (int (index & 1u), int (index >> 1u)); +} + float gtao_depth_similarity (float center_depth, float sample_depth) { if (center_depth <= 0.0f || sample_depth <= 0.0f) @@ -126,12 +135,55 @@ float gtao_relative_depth_difference (float center_depth, float sample_depth) return abs (sample_depth - center_depth) / max (center_depth, 0.00001f); } +vec4 gtao_resolve_depth_aware (ivec2 target_pixel) +{ + ivec2 source_max = ivec2 (push_constants.clamp_size); + ivec2 working_max = source_max / 2; + ivec2 base = clamp (target_pixel / 2, ivec2 (0), working_max); + float target_depth = texelFetch (gtao_view_depth_tex, target_pixel, 0).r; + vec4 best_result = vec4 (1.0f); + float best_weight = 0.0f; + float visibility_sum = 0.0f; + float weight_sum = 0.0f; + for (int y = -1; y <= 1; ++y) + { + for (int x = -1; x <= 1; ++x) + { + ivec2 candidate_pixel = base + ivec2 (x, y); + if (any (lessThan (candidate_pixel, ivec2 (0))) || any (greaterThan (candidate_pixel, working_max))) + continue; + vec4 candidate = gtao_fetch (candidate_pixel); + ivec2 representative_pixel = min (candidate_pixel * 2 + gtao_representative_offset (candidate), source_max); + float sample_depth = texelFetch (gtao_view_depth_tex, representative_pixel, 0).r; + float depth_weight = gtao_depth_similarity (target_depth, sample_depth); + if (depth_weight <= 0.0f) + continue; + vec2 spatial_delta = vec2 (representative_pixel - target_pixel); + float spatial_weight = exp2 (-0.5f * dot (spatial_delta, spatial_delta)); + float weight = spatial_weight * depth_weight; + visibility_sum += candidate.r * weight; + weight_sum += weight; + if (weight > best_weight) + { + best_weight = weight; + best_result = candidate; + } + } + } + if (weight_sum <= 0.0001f) + return vec4 (1.0f); + best_result.r = visibility_sum / weight_sum; + return best_result; +} + vec4 gtao_resolve (ivec2 target_pixel, vec2 target_uv, bool warped) { if (warped) target_pixel = clamp (ivec2 (target_uv / push_constants.screen_size_rcp), ivec2 (0), ivec2 (push_constants.clamp_size)); if (push_constants.gtao_halfres == 0u) return gtao_fetch (target_pixel); + if (push_constants.gtao_halfres_depth_aware != 0u) + return gtao_resolve_depth_aware (target_pixel); // Half-resolution AO is stored contiguously in the upper-left working // region. AO texel q represents source pixel 2*q. @@ -157,10 +209,10 @@ vec4 gtao_resolve (ivec2 target_pixel, vec2 target_uv, bool warped) { if (spatial_weights[i] <= 0.0f) continue; + vec4 candidate = i == 0 ? closest_result : gtao_fetch (candidates[i]); ivec2 source_pixel = min (candidates[i] * 2, source_max); float sample_depth = texelFetch (gtao_view_depth_tex, source_pixel, 0).r; float depth_difference = gtao_relative_depth_difference (target_depth, sample_depth); - vec4 candidate = i == 0 ? closest_result : gtao_fetch (candidates[i]); if (depth_difference < closest_depth_difference) { closest_depth_difference = depth_difference; From e4656fd842d3bab48f81a751ab05cdf3bc393975 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 22:37:26 -0500 Subject: [PATCH 18/26] Add experimental GTAO quality tier --- Quake/gl_vidsdl.c | 2 +- Shaders/gtao.comp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index 35b44d2de..885f49409 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -4353,7 +4353,7 @@ static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) .radius = q_max (1.0f, r_gtao_radius.value), .thin_occluder_compensation = q_max (0.0f, r_gtao_thin_occluder_compensation.value), .debug_mode = gtao_debug >= 1 && gtao_debug <= 4 ? (uint32_t)gtao_debug : 0u, - .quality = (uint32_t)CLAMP (0, (int)r_gtao_quality.value, 3), + .quality = (uint32_t)CLAMP (0, (int)r_gtao_quality.value, 4), .bias = CLAMP (0.0f, r_gtao_bias.value, 0.5f), .flags = (r_gtao_halfres.value > 0.0f ? 1u : 0u) | (r_gtao_halfres.value > 0.0f && r_gtao_halfres_depth_aware.value > 0.0f ? 2u : 0u), diff --git a/Shaders/gtao.comp b/Shaders/gtao.comp index 62f2a55cc..93182a3ff 100644 --- a/Shaders/gtao.comp +++ b/Shaders/gtao.comp @@ -288,8 +288,10 @@ void main () float visibility = clamp ((10.0f - screen_space_radius) / 100.0f, 0.0f, 1.0f) * 0.5f; // Use a denser 4x4 spatial tier for vkQuake's non-TAA default; retain // XeGTAO's low, medium and ultra tiers at 1x2, 2x2 and 9x3 respectively. - int slice_count = push_constants.quality == 0u ? 1 : (push_constants.quality == 1u ? 2 : (push_constants.quality == 2u ? 4 : 9)); - int steps_per_slice = push_constants.quality < 2u ? 2 : (push_constants.quality == 2u ? 4 : 3); + // Quality 4 is an experimental no-TAA tier with 12 slices and 4 steps. + int slice_count = push_constants.quality == 0u ? 1 : + (push_constants.quality == 1u ? 2 : (push_constants.quality == 2u ? 4 : (push_constants.quality == 3u ? 9 : 12))); + int steps_per_slice = push_constants.quality < 2u ? 2 : (push_constants.quality == 2u ? 4 : (push_constants.quality == 3u ? 3 : 4)); for (int slice = 0; slice < slice_count; ++slice) { float phi = (float (slice) + noise_slice) * (PI / float (slice_count)); From 50e6d88c7c7834caccc05dd74734a7df6031b0fe Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 23:06:19 -0500 Subject: [PATCH 19/26] Make depth-aware GTAO reconstruction standard --- Quake/gl_rmain.c | 1 - Quake/gl_rmisc.c | 5 +--- Quake/gl_vidsdl.c | 10 ++----- Quake/glquake.h | 1 - Shaders/gtao.comp | 3 +- Shaders/gtao_denoise.comp | 6 ++-- Shaders/screen_effects.inc | 60 +------------------------------------- 7 files changed, 9 insertions(+), 77 deletions(-) diff --git a/Quake/gl_rmain.c b/Quake/gl_rmain.c index 81b650b3a..6ecaf20a0 100644 --- a/Quake/gl_rmain.c +++ b/Quake/gl_rmain.c @@ -126,7 +126,6 @@ cvar_t r_gtao_denoise = {"r_gtao_denoise", "2", CVAR_ARCHIVE}; cvar_t r_gtao_bias = {"r_gtao_bias", "0", CVAR_ARCHIVE}; cvar_t r_gtao_multibounce = {"r_gtao_multibounce", "1", CVAR_ARCHIVE}; cvar_t r_gtao_halfres = {"r_gtao_halfres", "1", CVAR_ARCHIVE}; -cvar_t r_gtao_halfres_depth_aware = {"r_gtao_halfres_depth_aware", "0", CVAR_ARCHIVE}; cvar_t r_gtao_liquid_water = {"r_gtao_liquid_water", "1", CVAR_ARCHIVE}; cvar_t r_gtao_liquid_slime = {"r_gtao_liquid_slime", "1", CVAR_ARCHIVE}; cvar_t r_gtao_liquid_lava = {"r_gtao_liquid_lava", "1", CVAR_ARCHIVE}; diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index 5cdc0ea2e..abe3e92f0 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -71,7 +71,6 @@ extern cvar_t r_gtao_denoise; extern cvar_t r_gtao_bias; extern cvar_t r_gtao_multibounce; extern cvar_t r_gtao_halfres; -extern cvar_t r_gtao_halfres_depth_aware; extern cvar_t r_gtao_liquid_water; extern cvar_t r_gtao_liquid_slime; extern cvar_t r_gtao_liquid_lava; @@ -2039,7 +2038,7 @@ void R_CreatePipelineLayouts () ZEROED_STRUCT (VkPushConstantRange, push_constant_range); push_constant_range.offset = 0; - push_constant_range.size = 7 * sizeof (uint32_t) + 14 * sizeof (float); + push_constant_range.size = 6 * sizeof (uint32_t) + 14 * sizeof (float); push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; ZEROED_STRUCT (VkPipelineLayoutCreateInfo, pipeline_layout_create_info); @@ -4439,7 +4438,6 @@ void R_RestoreGTAODefaults (void) &r_gtao_bias, &r_gtao_multibounce, &r_gtao_halfres, - &r_gtao_halfres_depth_aware, &r_gtao_liquid_water, &r_gtao_liquid_slime, &r_gtao_liquid_lava, @@ -4539,7 +4537,6 @@ void R_Init (void) Cvar_RegisterVariable (&r_gtao_bias); Cvar_RegisterVariable (&r_gtao_multibounce); Cvar_RegisterVariable (&r_gtao_halfres); - Cvar_RegisterVariable (&r_gtao_halfres_depth_aware); Cvar_RegisterVariable (&r_gtao_liquid_water); Cvar_RegisterVariable (&r_gtao_liquid_slime); Cvar_RegisterVariable (&r_gtao_liquid_lava); diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index 885f49409..3ff6059f1 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -4028,7 +4028,6 @@ typedef struct screen_effect_constants_s uint32_t gtao_denoise; float gtao_multibounce; uint32_t gtao_halfres; - uint32_t gtao_halfres_depth_aware; float gtao_liquid_water; float gtao_liquid_slime; float gtao_liquid_lava; @@ -4196,7 +4195,7 @@ typedef struct gtao_denoise_constants_s uint32_t clamp_height; uint32_t sample_stride; float center_weight; - uint32_t depth_aware; + uint32_t half_res; } gtao_denoise_constants_t; static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) @@ -4232,7 +4231,7 @@ static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) working_height - 1, 1u, pass_count == 1 ? 1.2f : 0.24f, - r_gtao_halfres.value > 0.0f && r_gtao_halfres_depth_aware.value > 0.0f ? 1u : 0u, + r_gtao_halfres.value > 0.0f ? 1u : 0u, }; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); vkCmdDispatch (cbx->cb, (working_width + 15) / 16, (working_height + 7) / 8, 1); @@ -4355,8 +4354,7 @@ static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) .debug_mode = gtao_debug >= 1 && gtao_debug <= 4 ? (uint32_t)gtao_debug : 0u, .quality = (uint32_t)CLAMP (0, (int)r_gtao_quality.value, 4), .bias = CLAMP (0.0f, r_gtao_bias.value, 0.5f), - .flags = (r_gtao_halfres.value > 0.0f ? 1u : 0u) | - (r_gtao_halfres.value > 0.0f && r_gtao_halfres_depth_aware.value > 0.0f ? 2u : 0u), + .flags = r_gtao_halfres.value > 0.0f ? 1u : 0u, .falloff_range = CLAMP (0.01f, r_gtao_falloff.value, 1.0f), }; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (push_constants), &push_constants); @@ -4494,8 +4492,6 @@ static void GL_ScreenEffects (cb_context_t *cbx, qboolean enabled, end_rendering (r_gtao_debug.value == 0.0f || (int)r_gtao_debug.value == 5) ? (uint32_t)CLAMP (0, (int)r_gtao_denoise.value, 3) : 0u; push_constants.gtao_multibounce = CLAMP (0.0f, r_gtao_multibounce.value, 1.0f); push_constants.gtao_halfres = r_gtao_halfres.value > 0.0f ? 1u : 0u; - push_constants.gtao_halfres_depth_aware = - r_gtao_halfres.value > 0.0f && r_gtao_halfres_depth_aware.value > 0.0f ? 1u : 0u; push_constants.gtao_liquid_water = CLAMP (0.0f, r_gtao_liquid_water.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_WATER), 1.0f); push_constants.gtao_liquid_slime = diff --git a/Quake/glquake.h b/Quake/glquake.h index 1db92febf..6ff4db38c 100644 --- a/Quake/glquake.h +++ b/Quake/glquake.h @@ -614,7 +614,6 @@ extern cvar_t r_gtao_denoise; extern cvar_t r_gtao_bias; extern cvar_t r_gtao_multibounce; extern cvar_t r_gtao_halfres; -extern cvar_t r_gtao_halfres_depth_aware; extern cvar_t r_gtao_liquid_water; extern cvar_t r_gtao_liquid_slime; extern cvar_t r_gtao_liquid_lava; diff --git a/Shaders/gtao.comp b/Shaders/gtao.comp index 93182a3ff..e9b2534e4 100644 --- a/Shaders/gtao.comp +++ b/Shaders/gtao.comp @@ -29,7 +29,6 @@ const float HALF_PI = 1.57079632679489661923f; const uint STENCIL_MASK_SKY = 0x1u; const uint STENCIL_MASK_VIEWMODEL = 0x2u; const uint GTAO_FLAG_HALFRES = 0x1u; -const uint GTAO_FLAG_HALFRES_DEPTH_AWARE = 0x2u; const float DEPTH_MIP_OFFSET = 3.30f; float sample_depth (ivec2 pixel) @@ -145,7 +144,7 @@ void main () return; ivec2 pixel = ao_pixel * working_stride; uint representative_index = 0u; - if (half_res && (push_constants.flags & GTAO_FLAG_HALFRES_DEPTH_AWARE) != 0u) + if (half_res) { float closest_depth = 3.402823466e+38f; for (uint index = 0u; index < 4u; ++index) diff --git a/Shaders/gtao_denoise.comp b/Shaders/gtao_denoise.comp index 4da4425aa..1191336ea 100644 --- a/Shaders/gtao_denoise.comp +++ b/Shaders/gtao_denoise.comp @@ -11,7 +11,7 @@ layout (push_constant) uniform DenoiseConsts uvec2 clamp_size; uint sample_stride; float center_weight; - uint depth_aware; + uint half_res; } push_constants; @@ -59,7 +59,7 @@ vec4 denoise_value ( edges_c *= vec4 (edges_l.y, edges_r.x, edges_t.w, edges_b.z); vec4 depth_weights = vec4 (1.0f); vec4 diagonal_depth_weights = vec4 (1.0f); - if (push_constants.depth_aware != 0u) + if (push_constants.half_res != 0u) { int stride = int (push_constants.sample_stride); ivec2 limit = ivec2 (push_constants.clamp_size); @@ -136,7 +136,7 @@ void main () return; ivec2 pixel_next = pixel_base + ivec2 (stride, 0); - bool pair_inside = push_constants.depth_aware == 0u && stride == 1 && pixel_base.x > 0 && pixel_next.x < limit.x && pixel_base.y > 0 && pixel_base.y < limit.y; + bool pair_inside = push_constants.half_res == 0u && stride == 1 && pixel_base.x > 0 && pixel_next.x < limit.x && pixel_base.y > 0 && pixel_base.y < limit.y; if (!pair_inside) { imageStore (output_ao_image, pixel_base, denoise_fetch (pixel_base)); diff --git a/Shaders/screen_effects.inc b/Shaders/screen_effects.inc index c40994c80..68ad7353e 100644 --- a/Shaders/screen_effects.inc +++ b/Shaders/screen_effects.inc @@ -36,7 +36,6 @@ layout (push_constant) uniform PushConsts uint gtao_denoise; float gtao_multibounce; uint gtao_halfres; - uint gtao_halfres_depth_aware; float gtao_liquid_water; float gtao_liquid_slime; float gtao_liquid_lava; @@ -115,8 +114,6 @@ vec4 gtao_fetch (ivec2 pixel) ivec2 gtao_representative_offset (vec4 ao_sample) { - if (push_constants.gtao_halfres_depth_aware == 0u) - return ivec2 (0); uint index = uint (round (clamp (ao_sample.a, 0.0f, 1.0f) * 3.0f)); return ivec2 (int (index & 1u), int (index >> 1u)); } @@ -128,13 +125,6 @@ float gtao_depth_similarity (float center_depth, float sample_depth) return clamp (1.25f - abs (sample_depth - center_depth) / max (center_depth * 0.011f, 0.00001f), 0.0f, 1.0f); } -float gtao_relative_depth_difference (float center_depth, float sample_depth) -{ - if (center_depth <= 0.0f || sample_depth <= 0.0f) - return center_depth == sample_depth ? 0.0f : 1e30f; - return abs (sample_depth - center_depth) / max (center_depth, 0.00001f); -} - vec4 gtao_resolve_depth_aware (ivec2 target_pixel) { ivec2 source_max = ivec2 (push_constants.clamp_size); @@ -182,55 +172,7 @@ vec4 gtao_resolve (ivec2 target_pixel, vec2 target_uv, bool warped) target_pixel = clamp (ivec2 (target_uv / push_constants.screen_size_rcp), ivec2 (0), ivec2 (push_constants.clamp_size)); if (push_constants.gtao_halfres == 0u) return gtao_fetch (target_pixel); - if (push_constants.gtao_halfres_depth_aware != 0u) - return gtao_resolve_depth_aware (target_pixel); - - // Half-resolution AO is stored contiguously in the upper-left working - // region. AO texel q represents source pixel 2*q. - ivec2 source_max = ivec2 (push_constants.clamp_size); - ivec2 working_max = source_max / 2; - ivec2 base = clamp (target_pixel / 2, ivec2 (0), working_max); - if (all (equal (target_pixel, base * 2))) - return gtao_fetch (base); - ivec2 candidates[4] = ivec2[4] ( - base, min (base + ivec2 (1, 0), working_max), - min (base + ivec2 (0, 1), working_max), min (base + ivec2 (1, 1), working_max)); - float target_depth = texelFetch (gtao_view_depth_tex, target_pixel, 0).r; - vec2 fraction = clamp (vec2 (target_pixel - base * 2) * 0.5f, 0.0f, 1.0f); - vec4 spatial_weights = vec4 ((1.0f - fraction.x) * (1.0f - fraction.y), fraction.x * (1.0f - fraction.y), - (1.0f - fraction.x) * fraction.y, fraction.x * fraction.y); - vec4 closest_result = gtao_fetch (base); - vec4 best_result = closest_result; - float visibility_sum = 0.0f; - float weight_sum = 0.0f; - float best_weight = 0.0f; - float closest_depth_difference = 1e30f; - for (int i = 0; i < 4; ++i) - { - if (spatial_weights[i] <= 0.0f) - continue; - vec4 candidate = i == 0 ? closest_result : gtao_fetch (candidates[i]); - ivec2 source_pixel = min (candidates[i] * 2, source_max); - float sample_depth = texelFetch (gtao_view_depth_tex, source_pixel, 0).r; - float depth_difference = gtao_relative_depth_difference (target_depth, sample_depth); - if (depth_difference < closest_depth_difference) - { - closest_depth_difference = depth_difference; - closest_result = candidate; - } - float weight = spatial_weights[i] * gtao_depth_similarity (target_depth, sample_depth); - visibility_sum += candidate.r * weight; - weight_sum += weight; - if (weight > best_weight) - { - best_weight = weight; - best_result = candidate; - } - } - vec4 result = weight_sum > 0.0001f ? best_result : closest_result; - if (weight_sum > 0.0001f) - result.r = visibility_sum / weight_sum; - return result; + return gtao_resolve_depth_aware (target_pixel); } vec3 gtao_multibounce_visibility (float visibility, vec3 albedo) From 2624c5be6cbf679ec7405caabf5baeaf8b85c0c7 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 23:09:30 -0500 Subject: [PATCH 20/26] Use half-resolution GTAO for menu presets --- Quake/menu.c | 10 +++++----- Shaders/gtao.comp | 5 ++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Quake/menu.c b/Quake/menu.c index 976a299ad..236bf93aa 100644 --- a/Quake/menu.c +++ b/Quake/menu.c @@ -1849,9 +1849,9 @@ static int M_GraphicsOptions_GTAOPreset (void) { if (r_gtao.value <= 0.0f) return 0; - if (r_gtao_halfres.value > 0.0f) - return r_gtao_quality.value >= 3.0f ? 2 : 1; - return 3; + if (r_gtao_quality.value >= 4.0f) + return 3; + return r_gtao_quality.value >= 3.0f ? 2 : 1; } static void M_GraphicsOptions_ChooseNextGTAOPreset (int dir) @@ -1864,8 +1864,8 @@ static void M_GraphicsOptions_ChooseNextGTAOPreset (int dir) } R_RestoreGTAODefaults (); - Cvar_SetValueQuick (&r_gtao_halfres, preset < 3 ? 1.0f : 0.0f); - Cvar_SetValueQuick (&r_gtao_quality, preset == 1 ? 2.0f : 3.0f); + Cvar_SetValueQuick (&r_gtao_halfres, 1.0f); + Cvar_SetValueQuick (&r_gtao_quality, (float)(preset + 1)); } static void M_GraphicsOptions_AdjustSliders (int dir, qboolean mouse) diff --git a/Shaders/gtao.comp b/Shaders/gtao.comp index e9b2534e4..bf608c2cb 100644 --- a/Shaders/gtao.comp +++ b/Shaders/gtao.comp @@ -285,9 +285,8 @@ void main () } float screen_space_radius = effect_radius / pixel_size_x; float visibility = clamp ((10.0f - screen_space_radius) / 100.0f, 0.0f, 1.0f) * 0.5f; - // Use a denser 4x4 spatial tier for vkQuake's non-TAA default; retain - // XeGTAO's low, medium and ultra tiers at 1x2, 2x2 and 9x3 respectively. - // Quality 4 is an experimental no-TAA tier with 12 slices and 4 steps. + // Use denser spatial tiers for vkQuake's non-TAA renderer. Quality 4 is the + // high menu tier, with 12 slices and 4 steps. int slice_count = push_constants.quality == 0u ? 1 : (push_constants.quality == 1u ? 2 : (push_constants.quality == 2u ? 4 : (push_constants.quality == 3u ? 9 : 12))); int steps_per_slice = push_constants.quality < 2u ? 2 : (push_constants.quality == 2u ? 4 : (push_constants.quality == 3u ? 3 : 4)); From aafc4d2d408b4cd5a5cdd60d2c9522c903b95c26 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 23:20:04 -0500 Subject: [PATCH 21/26] Shorten GTAO menu label --- Quake/menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/menu.c b/Quake/menu.c index 236bf93aa..c929563ef 100644 --- a/Quake/menu.c +++ b/Quake/menu.c @@ -2088,7 +2088,7 @@ static void M_GraphicsOptions_Draw (cb_context_t *cbx) cbx, MENU_VALUE_X, top + CHARACTER_SIZE * GRAPHICS_OPT_PARTICLES, ((int)r_particles.value == 0) ? "off" : (((int)r_particles.value == 2) ? "Classic" : "glQuake")); - M_Print (cbx, MENU_LABEL_X, top + CHARACTER_SIZE * GRAPHICS_OPT_GTAO, "Ambient Occlusion"); + M_Print (cbx, MENU_LABEL_X, top + CHARACTER_SIZE * GRAPHICS_OPT_GTAO, "Ambient Occl."); { const char *gtao_presets[] = {"off", "low", "medium", "high"}; M_Print (cbx, MENU_VALUE_X, top + CHARACTER_SIZE * GRAPHICS_OPT_GTAO, gtao_presets[M_GraphicsOptions_GTAOPreset ()]); From 11eebbaeec69b2ce53fea963f3529d023ac88179 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 23:35:39 -0500 Subject: [PATCH 22/26] Add XeGTAO attribution and license --- Shaders/XeGTAO-LICENSE.txt | 19 +++++++++++++++++++ Shaders/gtao.comp | 10 ++++++++++ Shaders/gtao_denoise.comp | 10 ++++++++++ Shaders/gtao_depth.comp | 10 ++++++++++ 4 files changed, 49 insertions(+) create mode 100644 Shaders/XeGTAO-LICENSE.txt diff --git a/Shaders/XeGTAO-LICENSE.txt b/Shaders/XeGTAO-LICENSE.txt new file mode 100644 index 000000000..6f7d0cd1b --- /dev/null +++ b/Shaders/XeGTAO-LICENSE.txt @@ -0,0 +1,19 @@ +MIT License + +Copyright (C) 2016-2021, Intel Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Shaders/gtao.comp b/Shaders/gtao.comp index bf608c2cb..b1136d7bc 100644 --- a/Shaders/gtao.comp +++ b/Shaders/gtao.comp @@ -1,3 +1,13 @@ +/* + * Derived from Intel's XeGTAO: + * https://github.com/GameTechDev/XeGTAO + * + * Copyright (C) 2016-2021, Intel Corporation + * Licensed under the MIT License; see XeGTAO-LICENSE.txt. + * + * This is a modified GLSL/Vulkan adaptation for vkQuake. + */ + #version 460 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable diff --git a/Shaders/gtao_denoise.comp b/Shaders/gtao_denoise.comp index 1191336ea..f5b277d19 100644 --- a/Shaders/gtao_denoise.comp +++ b/Shaders/gtao_denoise.comp @@ -1,3 +1,13 @@ +/* + * Derived from Intel's XeGTAO: + * https://github.com/GameTechDev/XeGTAO + * + * Copyright (C) 2016-2021, Intel Corporation + * Licensed under the MIT License; see XeGTAO-LICENSE.txt. + * + * This is a modified GLSL/Vulkan adaptation for vkQuake. + */ + #version 460 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable diff --git a/Shaders/gtao_depth.comp b/Shaders/gtao_depth.comp index d1a59860a..1825fd2f8 100644 --- a/Shaders/gtao_depth.comp +++ b/Shaders/gtao_depth.comp @@ -1,3 +1,13 @@ +/* + * Derived from Intel's XeGTAO: + * https://github.com/GameTechDev/XeGTAO + * + * Copyright (C) 2016-2021, Intel Corporation + * Licensed under the MIT License; see XeGTAO-LICENSE.txt. + * + * This is a modified GLSL/Vulkan adaptation for vkQuake. + */ + #version 460 #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable From a03a8a04226f2753627c00fabb30ce982b870e17 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Tue, 11 Aug 2026 23:48:56 -0500 Subject: [PATCH 23/26] Default GTAO to disabled --- Quake/gl_rmain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Quake/gl_rmain.c b/Quake/gl_rmain.c index 6ecaf20a0..4dbb402c0 100644 --- a/Quake/gl_rmain.c +++ b/Quake/gl_rmain.c @@ -115,7 +115,7 @@ float map_fallbackalpha; qboolean r_drawworld_cheatsafe, r_fullbright_cheatsafe, r_lightmap_cheatsafe; // johnfitz cvar_t r_scale = {"r_scale", "1", CVAR_ARCHIVE}; -cvar_t r_gtao = {"r_gtao", "1", CVAR_ARCHIVE}; +cvar_t r_gtao = {"r_gtao", "0", CVAR_ARCHIVE}; cvar_t r_gtao_radius = {"r_gtao_radius", "32", CVAR_ARCHIVE}; cvar_t r_gtao_falloff = {"r_gtao_falloff", "0.615", CVAR_ARCHIVE}; cvar_t r_gtao_thin_occluder_compensation = {"r_gtao_thin_occluder_compensation", "0", CVAR_ARCHIVE}; From 854bdaca4386fe7e55c994b13607f82da912eae3 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Wed, 12 Aug 2026 11:43:33 -0500 Subject: [PATCH 24/26] Apply clang-format to GTAO changes --- Quake/gl_rmisc.c | 12 ++++---- Quake/gl_vidsdl.c | 72 ++++++++++++++++++++++------------------------- Quake/glquake.h | 28 +++++++++--------- Quake/r_alias.c | 2 +- Quake/r_world.c | 2 +- 5 files changed, 54 insertions(+), 62 deletions(-) diff --git a/Quake/gl_rmisc.c b/Quake/gl_rmisc.c index abe3e92f0..1993dce7d 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -3926,25 +3926,23 @@ R_CreateScreenEffectsPipelines */ static void R_CreateScreenEffectsPipelines () { - const qboolean ten_bit = vulkan_globals.color_format == VK_FORMAT_A2B10G10R10_UNORM_PACK32; - const VkBool32 gtao_enabled = R_GTAOEnabled () ? VK_TRUE : VK_FALSE; + const qboolean ten_bit = vulkan_globals.color_format == VK_FORMAT_A2B10G10R10_UNORM_PACK32; + const VkBool32 gtao_enabled = R_GTAOEnabled () ? VK_TRUE : VK_FALSE; const VkSpecializationMapEntry specialization_entry = {0, 0, sizeof (gtao_enabled)}; - const VkSpecializationInfo specialization_info = {1, &specialization_entry, sizeof (gtao_enabled), >ao_enabled}; + const VkSpecializationInfo specialization_info = {1, &specialization_entry, sizeof (gtao_enabled), >ao_enabled}; R_CreateComputePipeline ( &vulkan_globals.screen_effects_pipeline, ten_bit ? screen_effects_10bit_comp_module : screen_effects_8bit_comp_module, 0, &specialization_info, "screen_effects"); R_CreateComputePipeline ( &vulkan_globals.screen_effects_scale_pipeline, ten_bit ? screen_effects_10bit_scale_comp_module : screen_effects_8bit_scale_comp_module, 0, - &specialization_info, - "screen_effects_scale"); + &specialization_info, "screen_effects_scale"); if (vulkan_globals.screen_effects_sops) R_CreateComputePipeline ( &vulkan_globals.screen_effects_scale_sops_pipeline, ten_bit ? screen_effects_10bit_scale_sops_comp_module : screen_effects_8bit_scale_sops_comp_module, VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT | VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT, - &specialization_info, - "screen_effects_scale_sops"); + &specialization_info, "screen_effects_scale_sops"); } /* diff --git a/Quake/gl_vidsdl.c b/Quake/gl_vidsdl.c index 3ff6059f1..de267d89f 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -1458,10 +1458,10 @@ static void GL_InitDevice (void) vkGetPhysicalDeviceFormatProperties (vulkan_physical_device, vulkan_globals.depth_format, &format_properties); const VkFormatFeatureFlags depth_sampling_features = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT; - qboolean depth_sampling_support = (format_properties.optimalTilingFeatures & depth_sampling_features) == depth_sampling_features; + qboolean depth_sampling_support = (format_properties.optimalTilingFeatures & depth_sampling_features) == depth_sampling_features; vkGetPhysicalDeviceFormatProperties (vulkan_physical_device, VK_FORMAT_R8G8B8A8_UNORM, &format_properties); const VkFormatFeatureFlags gtao_image_features = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT; - qboolean gtao_image_support = (format_properties.optimalTilingFeatures & gtao_image_features) == gtao_image_features; + qboolean gtao_image_support = (format_properties.optimalTilingFeatures & gtao_image_features) == gtao_image_features; vkGetPhysicalDeviceFormatProperties (vulkan_physical_device, VK_FORMAT_R32_SFLOAT, &format_properties); qboolean gtao_depth_pyramid_support = (format_properties.optimalTilingFeatures & gtao_image_features) == gtao_image_features; vkGetPhysicalDeviceFormatProperties (vulkan_physical_device, VK_FORMAT_R8_UNORM, &format_properties); @@ -2126,7 +2126,7 @@ static void GL_CreateGTAOBuffer (void) if (gtao_buffer != VK_NULL_HANDLE) return; - VkResult err; + VkResult err; const uint32_t gtao_width = r_gtao_halfres.value > 0.0f ? (vid.width + 1) / 2 : vid.width; const uint32_t gtao_height = r_gtao_halfres.value > 0.0f ? (vid.height + 1) / 2 : vid.height; @@ -3769,7 +3769,7 @@ void GL_BeginRenderingTask (void *unused) if (scbx_index <= SCBX_OIT_RESOLVE) { const qboolean gtao_enabled = R_GTAOEnabled (); - const int main_render_pass_stencil = (Sky_NeedStencil () || gtao_enabled) ? MAIN_RENDER_PASS_STENCIL_CLEAR : MAIN_RENDER_PASS_NO_STENCIL; + const int main_render_pass_stencil = (Sky_NeedStencil () || gtao_enabled) ? MAIN_RENDER_PASS_STENCIL_CLEAR : MAIN_RENDER_PASS_NO_STENCIL; cbx->render_pass = vulkan_globals.main_render_pass [R_UseMBOIT () ? MAIN_RENDER_PASS_MBOIT : R_UseWBOIT () ? MAIN_RENDER_PASS_OIT @@ -3909,10 +3909,10 @@ qboolean GL_BeginRendering (qboolean use_tasks, task_handle_t *begin_rendering_t const int requested_oit_value = (int)r_oit.value; const oit_mode_t requested_oit_mode = GL_FrameOITModeForCvarValue (requested_oit_value); const qboolean oit_mode_changed = (requested_oit_mode != frame_oit_mode); - const qboolean requested_gtao_enabled = gtao_supported && r_gtao.value > 0.0f; - const qboolean gtao_mode_changed = requested_gtao_enabled != frame_gtao_enabled; - const qboolean requested_gtao_halfres = requested_gtao_enabled && r_gtao_halfres.value > 0.0f; - const qboolean gtao_size_changed = requested_gtao_halfres != frame_gtao_halfres; + const qboolean requested_gtao_enabled = gtao_supported && r_gtao.value > 0.0f; + const qboolean gtao_mode_changed = requested_gtao_enabled != frame_gtao_enabled; + const qboolean requested_gtao_halfres = requested_gtao_enabled && r_gtao_halfres.value > 0.0f; + const qboolean gtao_size_changed = requested_gtao_halfres != frame_gtao_halfres; if (vid.restart_next_frame || (render_resources_created && (oit_mode_changed || gtao_mode_changed || gtao_size_changed))) { @@ -4128,8 +4128,7 @@ static void GL_GTAODepthPyramid (cb_context_t *cbx, end_rendering_parms_t *parms liquid_mask_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; liquid_mask_barrier.subresourceRange.levelCount = 1; liquid_mask_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier ( - cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &liquid_mask_barrier); + vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &liquid_mask_barrier); uint32_t width = parms->vid_width; uint32_t height = parms->vid_height; @@ -4164,7 +4163,10 @@ static void GL_GTAODepthPyramid (cb_context_t *cbx, end_rendering_parms_t *parms R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); vkCmdBindDescriptorSets (cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->layout.handle, 0, 1, &vulkan_globals.gtao_depth_desc_sets[mip], 0, NULL); const gtao_depth_constants_t constants = { - width, height, vulkan_globals.gtao_projection[2], vulkan_globals.gtao_projection[3], + width, + height, + vulkan_globals.gtao_projection[2], + vulkan_globals.gtao_projection[3], q_max (1.0f, r_gtao_radius.value), CLAMP (0.01f, r_gtao_falloff.value, 1.0f)}; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); @@ -4183,8 +4185,7 @@ static void GL_GTAODepthPyramid (cb_context_t *cbx, end_rendering_parms_t *parms liquid_mask_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; liquid_mask_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; liquid_mask_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - vkCmdPipelineBarrier ( - cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &liquid_mask_barrier); + vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &liquid_mask_barrier); gtao_liquid_mask_buffer_initialized = true; R_EndDebugUtilsLabel (cbx); } @@ -4221,17 +4222,13 @@ static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier); R_BindPipeline (cbx, VK_PIPELINE_BIND_POINT_COMPUTE, vulkan_globals.gtao_denoise_pipeline); - vkCmdBindDescriptorSets (cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, vulkan_globals.gtao_denoise_pipeline.layout.handle, 0, 1, - &vulkan_globals.gtao_denoise_desc_sets[0], 0, NULL); - const uint32_t working_stride = r_gtao_halfres.value > 0.0f ? 2u : 1u; - const uint32_t working_width = (parms->vid_width + working_stride - 1) / working_stride; - const uint32_t working_height = (parms->vid_height + working_stride - 1) / working_stride; + vkCmdBindDescriptorSets ( + cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, vulkan_globals.gtao_denoise_pipeline.layout.handle, 0, 1, &vulkan_globals.gtao_denoise_desc_sets[0], 0, NULL); + const uint32_t working_stride = r_gtao_halfres.value > 0.0f ? 2u : 1u; + const uint32_t working_width = (parms->vid_width + working_stride - 1) / working_stride; + const uint32_t working_height = (parms->vid_height + working_stride - 1) / working_stride; gtao_denoise_constants_t constants = { - working_width - 1, - working_height - 1, - 1u, - pass_count == 1 ? 1.2f : 0.24f, - r_gtao_halfres.value > 0.0f ? 1u : 0u, + working_width - 1, working_height - 1, 1u, pass_count == 1 ? 1.2f : 0.24f, r_gtao_halfres.value > 0.0f ? 1u : 0u, }; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); vkCmdDispatch (cbx->cb, (working_width + 15) / 16, (working_height + 7) / 8, 1); @@ -4251,8 +4248,9 @@ static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) barriers[1].newLayout = VK_IMAGE_LAYOUT_GENERAL; barriers[1].image = gtao_buffer; vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 2, barriers); - vkCmdBindDescriptorSets (cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, vulkan_globals.gtao_denoise_pipeline.layout.handle, 0, 1, - &vulkan_globals.gtao_denoise_desc_sets[1], 0, NULL); + vkCmdBindDescriptorSets ( + cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, vulkan_globals.gtao_denoise_pipeline.layout.handle, 0, 1, &vulkan_globals.gtao_denoise_desc_sets[1], 0, + NULL); constants.center_weight = pass_count == 2 ? 1.2f : 0.24f; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); vkCmdDispatch (cbx->cb, (working_width + 15) / 16, (working_height + 7) / 8, 1); @@ -4268,10 +4266,10 @@ static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; barrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - vkCmdPipelineBarrier ( - cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier); - vkCmdBindDescriptorSets (cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, vulkan_globals.gtao_denoise_pipeline.layout.handle, 0, 1, - &vulkan_globals.gtao_denoise_desc_sets[0], 0, NULL); + vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier); + vkCmdBindDescriptorSets ( + cbx->cb, VK_PIPELINE_BIND_POINT_COMPUTE, vulkan_globals.gtao_denoise_pipeline.layout.handle, 0, 1, &vulkan_globals.gtao_denoise_desc_sets[0], 0, + NULL); constants.center_weight = 1.2f; R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof (constants), &constants); vkCmdDispatch (cbx->cb, (working_width + 15) / 16, (working_height + 7) / 8, 1); @@ -4279,8 +4277,7 @@ static void GL_GTAODenoise (cb_context_t *cbx, end_rendering_parms_t *parms) barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - vkCmdPipelineBarrier ( - cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier); + vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier); } } else @@ -4373,8 +4370,7 @@ static void GL_GTAO (cb_context_t *cbx, end_rendering_parms_t *parms) ao_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ao_barrier.subresourceRange.levelCount = 1; ao_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier ( - cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &ao_barrier); + vkCmdPipelineBarrier (cbx->cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &ao_barrier); GL_GTAODenoise (cbx, parms); gtao_buffer_initialized = true; @@ -4496,10 +4492,8 @@ static void GL_ScreenEffects (cb_context_t *cbx, qboolean enabled, end_rendering CLAMP (0.0f, r_gtao_liquid_water.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_WATER), 1.0f); push_constants.gtao_liquid_slime = CLAMP (0.0f, r_gtao_liquid_slime.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_SLIME), 1.0f); - push_constants.gtao_liquid_lava = - CLAMP (0.0f, r_gtao_liquid_lava.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_LAVA), 1.0f); - push_constants.gtao_liquid_tele = - CLAMP (0.0f, r_gtao_liquid_tele.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_TELE), 1.0f); + push_constants.gtao_liquid_lava = CLAMP (0.0f, r_gtao_liquid_lava.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_LAVA), 1.0f); + push_constants.gtao_liquid_tele = CLAMP (0.0f, r_gtao_liquid_tele.value, 1.0f) * CLAMP (0.0f, GL_WaterAlphaForTextureType (TEXTYPE_TELE), 1.0f); push_constant_size = sizeof (push_constants); } R_PushConstants (cbx, VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constant_size, &push_constants); @@ -4806,8 +4800,8 @@ static void GL_EndRenderingTask (end_rendering_parms_t *parms) depth_clear_value.depthStencil.depth = 0.0f; depth_clear_value.depthStencil.stencil = 0; - const qboolean screen_effects = - parms->render_warp || (parms->render_scale >= 2) || parms->vid_palettize || (parms->polyblend && parms->v_blend[3]) || parms->menu || parms->ray_debug || R_GTAOEnabled (); + const qboolean screen_effects = parms->render_warp || (parms->render_scale >= 2) || parms->vid_palettize || (parms->polyblend && parms->v_blend[3]) || + parms->menu || parms->ray_debug || R_GTAOEnabled (); { const qboolean resolve = (vulkan_globals.sample_count != VK_SAMPLE_COUNT_1_BIT); const qboolean use_mboit = parms->use_mboit; diff --git a/Quake/glquake.h b/Quake/glquake.h index 6ff4db38c..c579d94b3 100644 --- a/Quake/glquake.h +++ b/Quake/glquake.h @@ -188,15 +188,15 @@ typedef struct vulkan_memory_s vulkan_memory_type_t type; } vulkan_memory_t; -#define WORLD_PIPELINE_LIQUID_BIT 16 +#define WORLD_PIPELINE_LIQUID_BIT 16 #define WORLD_PIPELINE_COUNT 32 #define STENCIL_MASK_SKY 0x01u -#define STENCIL_MASK_VIEWMODEL 0x02u -#define STENCIL_MASK_WATER 0x04u -#define STENCIL_MASK_SLIME 0x08u -#define STENCIL_MASK_LAVA 0x10u -#define STENCIL_MASK_TELE 0x20u -#define STENCIL_MASK_LIQUID (STENCIL_MASK_WATER | STENCIL_MASK_SLIME | STENCIL_MASK_LAVA | STENCIL_MASK_TELE) +#define STENCIL_MASK_VIEWMODEL 0x02u +#define STENCIL_MASK_WATER 0x04u +#define STENCIL_MASK_SLIME 0x08u +#define STENCIL_MASK_LAVA 0x10u +#define STENCIL_MASK_TELE 0x20u +#define STENCIL_MASK_LIQUID (STENCIL_MASK_WATER | STENCIL_MASK_SLIME | STENCIL_MASK_LAVA | STENCIL_MASK_TELE) // slot layout of the alias/md5 pipeline arrays: 0..3 encode alpha test/blend, 4..5 are the r_showtris variants #define MODEL_PIPELINE_ALPHA_TEST_BIT 1 #define MODEL_PIPELINE_ALPHA_BLEND_BIT 2 @@ -280,7 +280,7 @@ typedef enum } oit_mode_t; extern oit_mode_t frame_oit_mode; -extern qboolean frame_gtao_enabled; +extern qboolean frame_gtao_enabled; static inline qboolean R_UseOIT (void) { @@ -522,14 +522,14 @@ typedef struct VkSampler linear_aniso_sampler_lod_bias; // Matrices - float projection_matrix[16]; - float view_matrix[16]; - float view_projection_matrix[16]; - int32_t gtao_viewport_x; - int32_t gtao_viewport_y; + float projection_matrix[16]; + float view_matrix[16]; + float view_projection_matrix[16]; + int32_t gtao_viewport_x; + int32_t gtao_viewport_y; uint32_t gtao_viewport_width; uint32_t gtao_viewport_height; - float gtao_projection[4]; + float gtao_projection[4]; // Dispatch table PFN_vkCmdBindPipeline vk_cmd_bind_pipeline; diff --git a/Quake/r_alias.c b/Quake/r_alias.c index 6107e8101..240672b85 100644 --- a/Quake/r_alias.c +++ b/Quake/r_alias.c @@ -110,7 +110,7 @@ static void GL_DrawAliasFrame ( if (oit_pass && (showtris != 0 || !has_alpha)) return; const main_render_pass_variant_t main_variant = R_MainPassPipelineVariant (cbx->render_pass_index); - const qboolean viewmodel = R_GTAOEnabled () && e == &cl.viewent && showtris == 0 && !oit_pass; + const qboolean viewmodel = R_GTAOEnabled () && e == &cl.viewent && showtris == 0 && !oit_pass; if (paliashdr->poseverttype == PV_MD5) pipeline = R_PipelineForRenderPass ( diff --git a/Quake/r_world.c b/Quake/r_world.c index c40bc0256..bb3d865d8 100644 --- a/Quake/r_world.c +++ b/Quake/r_world.c @@ -1159,7 +1159,7 @@ static void R_FlushBatch ( if (cbx->num_vbo_indices > 0) { const qboolean gtao_liquid = TEXTYPE_ISLIQUID (liquid_type) && R_GTAOEnabled (); - int pipeline_index = + int pipeline_index = (fullbright_enabled ? 1 : 0) + (alpha_test ? 2 : 0) + (alpha_blend ? 4 : 0) + (vid_filter.value != 0 && vid_palettize.value != 0 ? 8 : 0); if (gtao_liquid) pipeline_index |= WORLD_PIPELINE_LIQUID_BIT; From 2ad240aa886162cec8ead0fb9028eec1f8d80dd2 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Wed, 12 Aug 2026 11:45:40 -0500 Subject: [PATCH 25/26] Add GTAO shaders to GNU builds --- Quake/common.make | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Quake/common.make b/Quake/common.make index 90f0d391b..1f8c20972 100644 --- a/Quake/common.make +++ b/Quake/common.make @@ -164,6 +164,13 @@ SHADER_OBJS = \ screen_effects_10bit_comp.o \ screen_effects_10bit_scale_comp.o \ screen_effects_10bit_scale_sops_comp.o \ + gtao_comp.o \ + gtao_depth_comp.o \ + gtao_depth_msaa_comp.o \ + gtao_depth_r8_comp.o \ + gtao_depth_msaa_r8_comp.o \ + gtao_depth_downsample_comp.o \ + gtao_denoise_comp.o \ cs_tex_warp_comp.o \ indirect_comp.o \ indirect_clear_comp.o \ @@ -352,6 +359,10 @@ $(eval $(call SHADER_VARIANT,screen_effects_8bit_scale_sops_comp,screen_effects. $(eval $(call SHADER_VARIANT,screen_effects_10bit_comp,screen_effects.comp,-DUSE_10BIT=1)) $(eval $(call SHADER_VARIANT,screen_effects_10bit_scale_comp,screen_effects.comp,-DUSE_10BIT=1 -DSCALING=1)) $(eval $(call SHADER_VARIANT,screen_effects_10bit_scale_sops_comp,screen_effects.comp,--target-env vulkan1.1 -DUSE_10BIT=1 -DSCALING=1 -DUSE_SUBGROUP_OPS=1)) +$(eval $(call SHADER_VARIANT,gtao_depth_msaa_comp,gtao_depth.comp,-DGTAO_DEPTH_MSAA=1)) +$(eval $(call SHADER_VARIANT,gtao_depth_r8_comp,gtao_depth.comp,-DGTAO_LIQUID_MASK_R8=1)) +$(eval $(call SHADER_VARIANT,gtao_depth_msaa_r8_comp,gtao_depth.comp,-DGTAO_DEPTH_MSAA=1 -DGTAO_LIQUID_MASK_R8=1)) +$(eval $(call SHADER_VARIANT,gtao_depth_downsample_comp,gtao_depth.comp,-DGTAO_DEPTH_DOWNSAMPLE=1)) $(eval $(call SHADER_VARIANT,update_lightmap_8bit_comp,update_lightmap.comp,)) $(eval $(call SHADER_VARIANT,update_lightmap_8bit_rt_comp,update_lightmap.comp,-DRAY_QUERIES=1)) $(eval $(call SHADER_VARIANT,update_lightmap_10bit_comp,update_lightmap.comp,-DUSE_10BIT=1)) From 91746aa30e5414fa86c48ac932cd2b2de1e92dd9 Mon Sep 17 00:00:00 2001 From: Erik Hardesty Date: Wed, 12 Aug 2026 22:55:25 -0500 Subject: [PATCH 26/26] Fix enabling GTAO from the graphics menu --- Quake/menu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Quake/menu.c b/Quake/menu.c index c929563ef..65acf5442 100644 --- a/Quake/menu.c +++ b/Quake/menu.c @@ -1864,6 +1864,7 @@ static void M_GraphicsOptions_ChooseNextGTAOPreset (int dir) } R_RestoreGTAODefaults (); + Cvar_SetValueQuick (&r_gtao, 1.0f); Cvar_SetValueQuick (&r_gtao_halfres, 1.0f); Cvar_SetValueQuick (&r_gtao_quality, (float)(preset + 1)); }