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)) diff --git a/Quake/gl_rmain.c b/Quake/gl_rmain.c index cae7c244d..4dbb402c0 100644 --- a/Quake/gl_rmain.c +++ b/Quake/gl_rmain.c @@ -115,6 +115,21 @@ 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", "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}; +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_quality = {"r_gtao_quality", "2", 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_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}; +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}; @@ -417,6 +432,17 @@ static void R_SetupViewBeforeMark (void *unused) R_SetFrustum (r_fovx, r_fovy); // johnfitz -- use r_fov* vars R_SetupMatrices (); + 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]; + } // johnfitz -- cheat-protect some draw modes r_fullbright_cheatsafe = false; @@ -1153,6 +1179,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); + 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 1115497c5..1993dce7d 100644 --- a/Quake/gl_rmisc.c +++ b/Quake/gl_rmisc.c @@ -60,6 +60,21 @@ 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_falloff; +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_quality; +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_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; @@ -86,6 +101,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; @@ -1436,7 +1452,7 @@ void R_CreateDescriptorSetLayouts () } { - ZEROED_STRUCT_ARRAY (VkDescriptorSetLayoutBinding, screen_effects_layout_bindings, 5); + 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; @@ -1457,12 +1473,28 @@ 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; + 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 = 2; + 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); @@ -1471,6 +1503,91 @@ 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, 4); + 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; + 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 = 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); + 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, 4); + 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; + 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 = 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); + 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_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 = 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 +2038,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) + 14 * sizeof (float); push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; ZEROED_STRUCT (VkPipelineLayoutCreateInfo, pipeline_layout_create_info); @@ -1942,6 +2059,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 = 7 * sizeof (uint32_t) + 8 * 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; + } + + { + 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_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; + } + + { + 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 = 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; + 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] = { @@ -2316,7 +2500,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; @@ -2403,6 +2587,13 @@ 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_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); DECLARE_SHADER_MODULE (indirect_comp); DECLARE_SHADER_MODULE (indirect_clear_comp); @@ -3134,6 +3325,17 @@ static void R_CreateSkyPipelines () base.depth_stencil_state.depthTestEnable = VK_TRUE; base.depth_stencil_state.depthWriteEnable = VK_TRUE; + 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) @@ -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 = 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 ( @@ -3290,6 +3492,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 @@ -3343,68 +3559,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 < (R_GTAOEnabled () ? 2 : 1); ++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)); + } } } } @@ -3412,6 +3640,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 +3690,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)); + 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) @@ -3548,6 +3796,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)); + 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) @@ -3671,19 +3926,39 @@ R_CreateScreenEffectsPipelines */ static void R_CreateScreenEffectsPipelines () { - const qboolean ten_bit = vulkan_globals.color_format == VK_FORMAT_A2B10G10R10_UNORM_PACK32; + 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, - "screen_effects_scale"); + &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, - "screen_effects_scale_sops"); + 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"); +} + +/* +=============== +R_CreateGTAOPipelines +=============== +*/ +static void R_CreateGTAOPipelines () +{ + R_CreateComputePipeline (&vulkan_globals.gtao_pipeline, gtao_comp_module, 0, NULL, "gtao"); + 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"); } /* @@ -3781,6 +4056,16 @@ 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); + if (R_GTAOEnabled ()) + { + CREATE_SHADER_MODULE (gtao_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); @@ -3851,6 +4136,13 @@ 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_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); DESTROY_SHADER_MODULE (indirect_comp); DESTROY_SHADER_MODULE (indirect_clear_comp); @@ -3889,6 +4181,8 @@ void R_CreatePipelines () R_CreateMD5Pipelines (); R_CreatePostprocessPipelines (); R_CreateScreenEffectsPipelines (); + if (R_GTAOEnabled ()) + R_CreateGTAOPipelines (); R_CreateUpdateLightmapPipelines (); R_CreateIndirectComputePipelines (); R_CreateRayDebugPipelines (); @@ -4012,8 +4306,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 +4347,20 @@ 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_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); + vulkan_globals.gtao_denoise_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 +4417,41 @@ static void R_ScaleChanged_f (cvar_t *var) R_InitSamplers (); } +/* +=================== +R_RestoreGTAODefaults +=================== +*/ +void R_RestoreGTAODefaults (void) +{ + cvar_t *gtao_cvars[] = { + &r_gtao, + &r_gtao_radius, + &r_gtao_falloff, + &r_gtao_thin_occluder_compensation, + &r_gtao_strength, + &r_gtao_debug, + &r_gtao_quality, + &r_gtao_denoise, + &r_gtao_bias, + &r_gtao_multibounce, + &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) + 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"); +} + /* =============== R_Init @@ -4116,6 +4463,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 +4524,21 @@ 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_falloff); + Cvar_RegisterVariable (&r_gtao_thin_occluder_compensation); + Cvar_RegisterVariable (&r_gtao_strength); + Cvar_RegisterVariable (&r_gtao_debug); + Cvar_RegisterVariable (&r_gtao_quality); + Cvar_RegisterVariable (&r_gtao_denoise); + Cvar_RegisterVariable (&r_gtao_bias); + Cvar_RegisterVariable (&r_gtao_multibounce); + 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_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..de267d89f 100644 --- a/Quake/gl_vidsdl.c +++ b/Quake/gl_vidsdl.c @@ -92,6 +92,7 @@ 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); @@ -152,6 +153,26 @@ 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_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 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]; static vulkan_memory_t oit_accum_buffer_memory; @@ -1435,6 +1456,21 @@ 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; + 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"); + Con_Printf ("\n"); GET_GLOBAL_DEVICE_PROC_ADDR (vk_cmd_bind_pipeline, vkCmdBindPipeline); @@ -1597,9 +1633,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 = (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 = 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) { @@ -2016,7 +2052,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 | (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); @@ -2066,6 +2102,181 @@ 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"); + + 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"); + } +} + +/* +=============== +GL_CreateGTAOBuffer +=============== +*/ +static void GL_CreateGTAOBuffer (void) +{ + Sys_Printf ("Creating GTAO buffer\n"); + + if (gtao_buffer != VK_NULL_HANDLE) + 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 = 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; + 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; + + 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.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); + 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.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) + 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.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; + 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.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; + 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 +2839,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 +2874,128 @@ 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 (R_GTAOEnabled ()) + { + vulkan_globals.gtao_desc_set = R_AllocateDescriptorSet (&vulkan_globals.gtao_set_layout); + + ZEROED_STRUCT (VkDescriptorImageInfo, gtao_depth_image_info); + 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); + 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 = 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_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; + 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; + + 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, 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; + 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; + 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 || 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); + } + + 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 = pass == 0 ? gtao_denoise_buffer_view : gtao_buffer_view; + image_infos[1].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + 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]; + 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); + } + 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 = 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 = 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 = 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; + + 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; @@ -2688,6 +3036,38 @@ 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; + + 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) @@ -3130,6 +3510,8 @@ static void GL_CreateRenderResources (void) GL_CreateColorBuffer (); GL_CreateDepthBuffer (); + if (R_GTAOEnabled ()) + GL_CreateGTAOBuffer (); GL_CreateRenderPasses (); GL_CreateFrameBuffers (); R_CreatePipelines (); @@ -3195,6 +3577,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 +3619,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_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); + gtao_denoise_buffer_view = VK_NULL_HANDLE; + gtao_denoise_buffer = VK_NULL_HANDLE; + gtao_denoise_buffer_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 +3768,8 @@ 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 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 @@ -3466,19 +3902,35 @@ 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 (); 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; + 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)) + 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 + // until that synchronization is complete. + 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; - frame_oit_mode = GL_FrameOITModeForCvarValue (requested_oit_value); + } + else + { + frame_oit_mode = requested_oit_mode; + frame_gtao_enabled = requested_gtao_enabled; + frame_gtao_halfres = requested_gtao_halfres; } if (!render_resources_created) @@ -3571,8 +4023,36 @@ 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; + 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 +{ + 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 thin_occluder_compensation; + uint32_t debug_mode; + uint32_t quality; + float bias; + uint32_t flags; + float falloff_range; +} gtao_constants_t; + typedef struct ray_debug_constants_s { float screen_size_rcp_x; @@ -3621,6 +4101,281 @@ 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 + +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"); + 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) + { + 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) + { + 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); + 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.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; + 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); +} + +typedef struct gtao_denoise_constants_s +{ + uint32_t clamp_width; + uint32_t clamp_height; + uint32_t sample_stride; + float center_weight; + uint32_t half_res; +} 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, 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; + + 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_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); + + 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 + 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; + 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 + 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; + 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) +{ + const int gtao_debug = CLAMP (0, (int)r_gtao_debug.value, 6); + R_BeginDebugUtilsLabel (cbx, "GTAO"); + 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; + 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; + + 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.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); + + 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), + .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, 4), + .bias = CLAMP (0.0f, r_gtao_bias.value, 0.5f), + .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); + 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); + + 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; + R_EndDebugUtilsLabel (cbx); +} /* =============== @@ -3708,8 +4463,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 (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, @@ -3722,7 +4479,24 @@ 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_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, 6); + push_constants.gtao_denoise = + (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 = + 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 @@ -4026,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; + 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; @@ -4066,11 +4840,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 () ? 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); @@ -4104,6 +4879,9 @@ static void GL_EndRenderingTask (end_rendering_parms_t *parms) vkCmdEndRenderPass (render_passes_cb); } + 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 d9dd9ca67..c579d94b3 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 @@ -187,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 @@ -271,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) { @@ -287,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) @@ -428,10 +443,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 +458,13 @@ 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_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; vulkan_pipeline_t showtris_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT]; vulkan_pipeline_t showtris_indirect_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT]; @@ -470,6 +494,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; @@ -492,9 +522,14 @@ typedef struct VkSampler linear_aniso_sampler_lod_bias; // Matrices - float projection_matrix[16]; - float view_matrix[16]; - float view_projection_matrix[16]; + 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]; // Dispatch table PFN_vkCmdBindPipeline vk_cmd_bind_pipeline; @@ -568,6 +603,21 @@ 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_falloff; +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_quality; +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_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/menu.c b/Quake/menu.c index cf3a98ee0..65acf5442 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,30 @@ 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_quality.value >= 4.0f) + return 3; + return r_gtao_quality.value >= 3.0f ? 2 : 1; +} + +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, 1.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) { float f, clamped_mouse = CLAMP (SLIDER_START, (float)m_mouse_x, SLIDER_END); @@ -1923,6 +1948,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 +2089,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 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 ()]); + } + if (vulkan_globals.ray_query) { M_Print (cbx, MENU_LABEL_X, top + CHARACTER_SIZE * GRAPHICS_OPT_SHADOWS, "Dynamic Shadows"); diff --git a/Quake/r_alias.c b/Quake/r_alias.c index bb519b457..240672b85 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 = R_GTAOEnabled () && 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/Quake/r_brush.c b/Quake/r_brush.c index 33e057cce..963197399 100644 --- a/Quake/r_brush.c +++ b/Quake/r_brush.c @@ -976,13 +976,24 @@ 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 (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 (gtao_liquid) + { + 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..bb3d865d8 100644 --- a/Quake/r_world.c +++ b/Quake/r_world.c @@ -1154,17 +1154,28 @@ 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 = + 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 (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 (gtao_liquid) + { + 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 +1223,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 +1334,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 +1419,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/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 ( 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 new file mode 100644 index 000000000..b1136d7bc --- /dev/null +++ b/Shaders/gtao.comp @@ -0,0 +1,388 @@ +/* + * 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 + +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 (push_constant) uniform GTAOConsts +{ + ivec2 viewport_origin; + uvec2 viewport_size; + vec4 projection; + float radius; + float thin_occluder_compensation; + uint debug_mode; + uint quality; + float bias; + uint flags; + float falloff_range; +} +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_HALFRES = 0x1u; +const float DEPTH_MIP_OFFSET = 3.30f; + +float sample_depth (ivec2 pixel) +{ + return texelFetch (view_depth_pyramid_tex, pixel, 0).r; +} + +ivec2 depth_texture_size () +{ + return textureSize (view_depth_pyramid_tex, 0); +} + +uint sample_stencil (ivec2 pixel) +{ + return uint (round (texelFetch (classification_mask_tex, pixel, 0).r * 255.0f)); +} + +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 = -depth; + + 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, out bool valid) +{ + float depth = sample_depth (pixel); + valid = inside_viewport (pixel) && depth > 0.00001f; + if (valid) + valid = (sample_stencil (pixel) & (STENCIL_MASK_SKY | STENCIL_MASK_VIEWMODEL)) == 0u; + if (!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)); + 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 = 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; + return valid ? view_depth : 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)); +} + +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; + uint representative_index = 0u; + if (half_res) + { + 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; + 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) + { + 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; + } + + 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); + + 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) + normal = -normal; + center *= 0.99999f; + + if (push_constants.debug_mode == 2u) + { + imageStore (ao_image, ao_pixel, vec4 (normal * 0.5f + 0.5f, representative_value)); + return; + } + if (push_constants.debug_mode == 3u) + { + vec4 color = classification_color (center_stencil, true, true); + color.a = representative_value; + imageStore (ao_image, ao_pixel, color); + return; + } + + // Noise belongs to the compact AO working grid. + ivec2 noise_pixel = ao_pixel; + noise_pixel &= ivec2 (63); + 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); + 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 == 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)); + 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, representative_value)); + 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; + // 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)); + 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)) + { + 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); + 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.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); + horizon_cos_0 = max (horizon_cos_0, sample_horizon_cos_0); + } + } + } + + ivec2 sample_pixel_1 = pixel - sample_offset; + if (inside_viewport (sample_pixel_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); + 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.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); + 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); + } + + 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, representative_value)); +} diff --git a/Shaders/gtao_denoise.comp b/Shaders/gtao_denoise.comp new file mode 100644 index 000000000..f5b277d19 --- /dev/null +++ b/Shaders/gtao_denoise.comp @@ -0,0 +1,186 @@ +/* + * 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 + +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 half_res; +} +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; +} + +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.rgb, vec3 (0.999f)))) + return center; + + 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); + vec4 depth_weights = vec4 (1.0f); + vec4 diagonal_depth_weights = vec4 (1.0f); + if (push_constants.half_res != 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) * 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; + 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); + 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.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), + 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 = 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)); + 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 ( + 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), + 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 ( + 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), + 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); +} diff --git a/Shaders/gtao_depth.comp b/Shaders/gtao_depth.comp new file mode 100644 index 000000000..1825fd2f8 --- /dev/null +++ b/Shaders/gtao_depth.comp @@ -0,0 +1,114 @@ +/* + * 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 + +#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; +#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 +{ + 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; + 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; + 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) + 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))); + // 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/screen_effects.inc b/Shaders/screen_effects.inc index b987853cb..68ad7353e 100644 --- a/Shaders/screen_effects.inc +++ b/Shaders/screen_effects.inc @@ -9,6 +9,10 @@ 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 = 8) uniform sampler2D gtao_liquid_mask_tex; layout (set = 0, binding = 3) uniform samplerBuffer palette_colors; layout (set = 0, binding = 4) uniform PaletteOctree { @@ -27,9 +31,55 @@ 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; + 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) { @@ -57,6 +107,82 @@ 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); +} + +ivec2 gtao_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 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); +} + +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); + return gtao_resolve_depth_aware (target_pixel); +} + +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 +190,9 @@ 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 + +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 @@ -128,6 +257,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 +272,37 @@ 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 (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; + const vec4 ao_sample = gtao_resolve (ao_pixel, effect_texcoord, gtao_warped); + 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 == 6u) + color.rgb = gtao_liquid_debug_color (liquid_mask); + 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; + 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); + 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)); + } + } + [[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..51e8e1579 100644 --- a/Shaders/shaders.h +++ b/Shaders/shaders.h @@ -72,6 +72,13 @@ 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_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); 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..a09b45f56 100644 --- a/Windows/VisualStudio/embedded.vcxproj +++ b/Windows/VisualStudio/embedded.vcxproj @@ -243,6 +243,56 @@ $(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 -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" + $(SolutionDir)..\..\Shaders\Compiled\Debug\gtao.comp.c + $(SolutionDir)..\..\Shaders\Compiled\Release\gtao.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_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" +$(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_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_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 + $(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) @@ -432,4 +482,4 @@ $(VULKAN_SDK)\bin\spirv-opt.exe -Os --canonicalize-ids --strip-debug "$(Solution - \ No newline at end of file + 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..c8f156a8c 100644 --- a/Windows/VisualStudio/vkquake.vcxproj +++ b/Windows/VisualStudio/vkquake.vcxproj @@ -1,4 +1,4 @@ - + @@ -477,6 +477,41 @@ 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 + NotUsing + true NotUsing @@ -772,6 +807,41 @@ 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 + NotUsing + true NotUsing @@ -933,4 +1003,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..a443dae93 100644 --- a/Windows/VisualStudio/vkquake.vcxproj.filters +++ b/Windows/VisualStudio/vkquake.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -397,6 +397,27 @@ Shaders\Debug\Screen Effects + + Shaders\Debug + + + Shaders\Debug + + + Shaders\Debug + + + Shaders\Debug + + + Shaders\Debug + + + Shaders\Debug + + + Shaders\Debug + Shaders\Debug @@ -495,6 +516,27 @@ Shaders\Release\Screen Effects + + + Shaders\Release + + + Shaders\Release + + + Shaders\Release + + + Shaders\Release + + + Shaders\Release + + + Shaders\Release + + + Shaders\Release Shaders\Release diff --git a/meson.build b/meson.build index e32e66533..cf26b8e94 100644 --- a/meson.build +++ b/meson.build @@ -150,6 +150,13 @@ 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_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', []], ['Shaders/update_lightmap.comp', 'update_lightmap_8bit_rt.comp', ['-DRAY_QUERIES=1']], ['Shaders/update_lightmap.comp', 'update_lightmap_10bit.comp', ['-DUSE_10BIT=1']],