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']],