From a2661fe95d58f57eebec7f4ca5e4fbd4436a65c0 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Mon, 5 Feb 2024 10:57:50 -0500 Subject: [PATCH 1/6] vk: add skybox pipeline for traditional renderer It is not wired up to render anything yet, but we just made sure that it builds and gets destroyed. Also, fixup dynamic BLAS double-free. --- ref/vk/TODO.md | 8 +- ref/vk/r_textures.c | 2 +- ref/vk/vk_brush.c | 2 +- ref/vk/vk_descriptor.h | 5 - ref/vk/vk_framectl.c | 13 -- ref/vk/vk_ray_model.c | 1 + ref/vk/vk_render.c | 291 +++++++++++++++++++++++++++-------------- ref/vk/vk_render.h | 2 + 8 files changed, 206 insertions(+), 118 deletions(-) diff --git a/ref/vk/TODO.md b/ref/vk/TODO.md index e18321390d..d70f6883dd 100644 --- a/ref/vk/TODO.md +++ b/ref/vk/TODO.md @@ -1,6 +1,13 @@ ## Next - [ ] performance profiling and comparison +## 2024-02-05 E373 +- [ ] Skybox for traditional renderer + - [-] Sky pipeline + - [ ] Submit SURF_DRAWSKY draw commands + - [ ] Use original skybox for trad renderer + +# Previously ## 2024-02-01 E371 - [x] tune A-Trous step widths for different channels - [x] multiple passes -- core of the paper lol @@ -11,7 +18,6 @@ - [ ] :x: temporal glitches with dontBlurSamples() and ATrous → no longer reproduces - [x] add `-vkdbg_shaderprintf` arg to explicitly enable shader debug printfs -# Previously ## 2024-01-29 E370 - [x] bounce > 1 brighness - [ ] tune A-Trous step widths for different channels diff --git a/ref/vk/r_textures.c b/ref/vk/r_textures.c index 951afc4c46..9be158e4b6 100644 --- a/ref/vk/r_textures.c +++ b/ref/vk/r_textures.c @@ -872,7 +872,7 @@ static qboolean skyboxTryLoad( const char *skyboxname, qboolean force_reload ) { static const char *k_skybox_default = "desert"; -void skyboxSetup( const char *skyboxname, qboolean is_custom, qboolean force_reload ) { +static void skyboxSetup( const char *skyboxname, qboolean is_custom, qboolean force_reload ) { DEBUG("%s: skyboxname='%s' is_custom=%d force_reload=%d", __FUNCTION__, skyboxname, is_custom, force_reload); if (!skyboxTryLoad(skyboxname, force_reload)) { diff --git a/ref/vk/vk_brush.c b/ref/vk/vk_brush.c index abd6b914b9..26b007f6c2 100644 --- a/ref/vk/vk_brush.c +++ b/ref/vk/vk_brush.c @@ -1412,8 +1412,8 @@ static qboolean fillBrushSurfaces(fill_geometries_args_t args) { model_geometry->index_offset = index_offset; if ( type == BrushSurface_Sky ) { -#define TEX_BASE_SKYBOX 0x0f000000u // FIXME ray_interop.h model_geometry->material.tex_base_color = TEX_BASE_SKYBOX; + model_geometry->ye_olde_texture = TEX_BASE_SKYBOX; } else { ASSERT(!FBitSet( surf->flags, SURF_DRAWTILED )); VK_CreateSurfaceLightmap( surf, args.mod ); diff --git a/ref/vk/vk_descriptor.h b/ref/vk/vk_descriptor.h index f9a39e076e..8464ba3b79 100644 --- a/ref/vk/vk_descriptor.h +++ b/ref/vk/vk_descriptor.h @@ -52,8 +52,3 @@ typedef struct { void VK_DescriptorsCreate(vk_descriptors_t *desc); void VK_DescriptorsWrite(const vk_descriptors_t *desc, int set_slot); void VK_DescriptorsDestroy(const vk_descriptors_t *desc); - -// typedef enum { -// VK_DescType_SingleTexture, -// } vk_desc_type_t; -// VkDescriptorSet VK_DescriptorGetSet( vk_desc_type_t type ); diff --git a/ref/vk/vk_framectl.c b/ref/vk/vk_framectl.c index 4be5680caa..1a6d937cf7 100644 --- a/ref/vk/vk_framectl.c +++ b/ref/vk/vk_framectl.c @@ -335,19 +335,6 @@ static void submit( vk_combuf_t* combuf, qboolean wait, qboolean draw ) { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, }; - -#define BOUNDED_ARRAY(NAME, TYPE, MAX_SIZE) \ - struct { \ - TYPE items[MAX_SIZE]; \ - int count; \ - } NAME - -#define BOUNDED_ARRAY_APPEND(var, item) \ - do { \ - ASSERT(var.count < COUNTOF(var.items)); \ - var.items[var.count++] = item; \ - } while(0) - // TODO for RT renderer we only touch framebuffer at the very end of rendering/cmdbuf. // Can we postpone waitinf for framebuffer semaphore until we actually need it. BOUNDED_ARRAY(waitophores, VkSemaphore, 2) = {0}; diff --git a/ref/vk/vk_ray_model.c b/ref/vk/vk_ray_model.c index eb9ef03ed4..d16bbf31b8 100644 --- a/ref/vk/vk_ray_model.c +++ b/ref/vk/vk_ray_model.c @@ -459,6 +459,7 @@ qboolean RT_DynamicModelInit(void) { void RT_DynamicModelShutdown(void) { for (int i = 0; i < MATERIAL_MODE_COUNT; ++i) { RT_BlasDestroy(g_dyn.groups[i].blas); + g_dyn.groups[i].blas = NULL; } } diff --git a/ref/vk/vk_render.c b/ref/vk/vk_render.c index 16acc3fa9f..ac3b405677 100644 --- a/ref/vk/vk_render.c +++ b/ref/vk/vk_render.c @@ -3,7 +3,6 @@ #include "vk_core.h" #include "vk_buffer.h" #include "vk_geometry.h" -#include "vk_staging.h" #include "vk_const.h" #include "vk_common.h" #include "vk_cvar.h" @@ -12,7 +11,6 @@ #include "vk_math.h" #include "vk_rtx.h" #include "vk_descriptor.h" -#include "vk_framectl.h" // FIXME needed for dynamic models cmdbuf #include "alolcator.h" #include "profiler.h" #include "r_speeds.h" @@ -41,9 +39,32 @@ typedef struct { vec4_t color; } uniform_data_t; +enum { + // These correspond to kVkRenderType* + kVkPipeline_Solid, // no blending, depth RW + kVkPipeline_A_1mA_RW, // blend: src*a + dst*(1-a), depth: RW + kVkPipeline_A_1mA_R, // blend: src*a + dst*(1-a), depth test + kVkPipeline_A_1, // blend: src*a + dst, no depth test or write + kVkPipeline_A_1_R, // blend: src*a + dst, depth test + kVkPipeline_AT, // no blend, depth RW, alpha test + kVkPipeline_1_1_R, // blend: src + dst, depth test + + // Special pipeline for skybox (tex = TEX_BASE_SKYBOX) + //kVkPipeline_Sky, + kVkPipeline_COUNT, +}; + +typedef struct { + VkPipeline pipeline; + VkDescriptorSet sets[1]; + vk_descriptors_t descs; +} r_pipeline_sky_t; + static struct { VkPipelineLayout pipeline_layout; - VkPipeline pipelines[kVkRenderType_COUNT]; + VkPipeline pipelines[kVkPipeline_COUNT]; + + r_pipeline_sky_t pipeline_sky; vk_buffer_t uniform_buffer; uint32_t ubo_align; @@ -56,6 +77,94 @@ static struct { } stats; } g_render; +static qboolean createPipeline( VkPipeline* out, const char *name, const vk_pipeline_graphics_create_info_t *ci ) { + *out = VK_PipelineGraphicsCreate(ci); + + if (*out == VK_NULL_HANDLE) + { + gEngine.Con_Printf(S_ERROR "Cannot create render pipeline \"%s\"\n", name); + return false; + } + + if (vk_core.debug) + { + VkDebugUtilsObjectNameInfoEXT debug_name = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, + .objectHandle = (uint64_t)*out, + .objectType = VK_OBJECT_TYPE_PIPELINE, + .pObjectName = name, + }; + XVK_CHECK(vkSetDebugUtilsObjectNameEXT(vk_core.device, &debug_name)); + } + + return true; +} + +static qboolean createSkyboxPipeline( void ) { + const vk_shader_stage_t sky_shaders[] = { + { + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .filename = "sky.vert.spv", + .specialization_info = NULL, + }, { + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .filename = "sky.frag.spv", + .specialization_info = NULL, + }}; + + const VkVertexInputAttributeDescription attribs[] = { + {.binding = 0, .location = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(vk_vertex_t, pos)}, + }; + + const VkDescriptorSetLayoutBinding bindings[] = {{ + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = NULL, + }, { + .binding = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = NULL, + }}; + + g_render.pipeline_sky.descs = (vk_descriptors_t){ + .bindings = bindings, + .num_bindings = COUNTOF(bindings), + + .desc_sets = g_render.pipeline_sky.sets, + .num_sets = COUNTOF(g_render.pipeline_sky.sets), + + .push_constants = (VkPushConstantRange){0}, + }; + + VK_DescriptorsCreate(&g_render.pipeline_sky.descs); + + vk_pipeline_graphics_create_info_t ci = { + .layout = g_render.pipeline_sky.descs.pipeline_layout, + + .attribs = attribs, + .num_attribs = ARRAYSIZE(attribs), + + .stages = sky_shaders, + .num_stages = ARRAYSIZE(sky_shaders), + + .vertex_stride = sizeof(vk_vertex_t), + + .depthTestEnable = VK_TRUE, + .depthWriteEnable = VK_TRUE, + .depthCompareOp = VK_COMPARE_OP_LESS, + + .blendEnable = VK_FALSE, + + .cullMode = VK_CULL_MODE_FRONT_BIT, + }; + + return createPipeline(&g_render.pipeline_sky.pipeline, "sky", &ci); +} + static qboolean createPipelines( void ) { /* VkPushConstantRange push_const = { */ @@ -105,7 +214,7 @@ static qboolean createPipelines( void ) {.binding = 0, .location = 2, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(vk_vertex_t, gl_tc)}, {.binding = 0, .location = 3, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(vk_vertex_t, lm_tc)}, {.binding = 0, .location = 4, .format = VK_FORMAT_R8G8B8A8_UNORM, .offset = offsetof(vk_vertex_t, color)}, - {.binding = 0, .location = 6, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(vk_vertex_t, prev_pos)}, + // Not used {.binding = 0, .location = 6, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(vk_vertex_t, prev_pos)}, }; const vk_shader_stage_t shader_stages[] = { @@ -138,107 +247,88 @@ static qboolean createPipelines( void ) .cullMode = VK_CULL_MODE_FRONT_BIT, }; - for (int i = 0; i < kVkRenderType_COUNT; ++i) { - const char *name = "UNDEFINED"; - switch (i) - { - case kVkRenderTypeSolid: - spec_data.alpha_test_threshold = 0.f; - ci.blendEnable = VK_FALSE; - ci.depthWriteEnable = VK_TRUE; - ci.depthTestEnable = VK_TRUE; - name = "kVkRenderTypeSolid"; - break; - - case kVkRenderType_A_1mA_RW: - spec_data.alpha_test_threshold = 0.f; - ci.depthWriteEnable = VK_TRUE; - ci.depthTestEnable = VK_TRUE; - ci.blendEnable = VK_TRUE; - ci.colorBlendOp = VK_BLEND_OP_ADD; - ci.srcAlphaBlendFactor = ci.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - ci.dstAlphaBlendFactor = ci.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - name = "kVkRenderType_A_1mA_RW"; - break; - - case kVkRenderType_A_1mA_R: - spec_data.alpha_test_threshold = 0.f; - ci.depthWriteEnable = VK_FALSE; - ci.depthTestEnable = VK_TRUE; - ci.blendEnable = VK_TRUE; - ci.colorBlendOp = VK_BLEND_OP_ADD; - ci.srcAlphaBlendFactor = ci.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - ci.dstAlphaBlendFactor = ci.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - name = "kVkRenderType_A_1mA_R"; - break; - - case kVkRenderType_A_1: - spec_data.alpha_test_threshold = 0.f; - ci.depthWriteEnable = VK_FALSE; - ci.depthTestEnable = VK_FALSE; // Fake bloom, should be over geometry too - ci.blendEnable = VK_TRUE; - ci.colorBlendOp = VK_BLEND_OP_ADD; - ci.srcAlphaBlendFactor = ci.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - ci.dstAlphaBlendFactor = ci.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; - name = "kVkRenderType_A_1"; - break; - - case kVkRenderType_A_1_R: - spec_data.alpha_test_threshold = 0.f; - ci.depthWriteEnable = VK_FALSE; - ci.depthTestEnable = VK_TRUE; - ci.blendEnable = VK_TRUE; - ci.colorBlendOp = VK_BLEND_OP_ADD; - ci.srcAlphaBlendFactor = ci.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - ci.dstAlphaBlendFactor = ci.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; - name = "kVkRenderType_A_1_R"; - break; - - case kVkRenderType_AT: - spec_data.alpha_test_threshold = .25f; - ci.depthWriteEnable = VK_TRUE; - ci.depthTestEnable = VK_TRUE; - ci.blendEnable = VK_FALSE; - name = "kVkRenderType_AT"; - break; - - case kVkRenderType_1_1_R: - spec_data.alpha_test_threshold = 0.f; - ci.depthWriteEnable = VK_FALSE; - ci.depthTestEnable = VK_TRUE; - ci.blendEnable = VK_TRUE; - ci.colorBlendOp = VK_BLEND_OP_ADD; - ci.srcAlphaBlendFactor = ci.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; - ci.dstAlphaBlendFactor = ci.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; - name = "kVkRenderType_1_1_R"; - break; - - default: - ASSERT(!"Unreachable"); - } + spec_data.alpha_test_threshold = 0.f; + ci.blendEnable = VK_FALSE; + ci.depthWriteEnable = VK_TRUE; + ci.depthTestEnable = VK_TRUE; + if (!createPipeline(g_render.pipelines + kVkPipeline_Solid, "solid", &ci)) + return false; + } - g_render.pipelines[i] = VK_PipelineGraphicsCreate(&ci); + { + spec_data.alpha_test_threshold = 0.f; + ci.depthWriteEnable = VK_TRUE; + ci.depthTestEnable = VK_TRUE; + ci.blendEnable = VK_TRUE; + ci.colorBlendOp = VK_BLEND_OP_ADD; + ci.srcAlphaBlendFactor = ci.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + ci.dstAlphaBlendFactor = ci.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + if (!createPipeline(g_render.pipelines + kVkPipeline_A_1mA_RW, "A_1ma_RW", &ci)) + return false; + } - if (!g_render.pipelines[i]) - { - // TODO complain + { + spec_data.alpha_test_threshold = 0.f; + ci.depthWriteEnable = VK_FALSE; + ci.depthTestEnable = VK_TRUE; + ci.blendEnable = VK_TRUE; + ci.colorBlendOp = VK_BLEND_OP_ADD; + ci.srcAlphaBlendFactor = ci.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + ci.dstAlphaBlendFactor = ci.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + if (!createPipeline(g_render.pipelines + kVkPipeline_A_1mA_R, "A_1ma_R", &ci)) return false; - } + } - if (vk_core.debug) - { - VkDebugUtilsObjectNameInfoEXT debug_name = { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectHandle = (uint64_t)g_render.pipelines[i], - .objectType = VK_OBJECT_TYPE_PIPELINE, - .pObjectName = name, - }; - XVK_CHECK(vkSetDebugUtilsObjectNameEXT(vk_core.device, &debug_name)); - } + { + spec_data.alpha_test_threshold = 0.f; + ci.depthWriteEnable = VK_FALSE; + ci.depthTestEnable = VK_FALSE; // Fake bloom, should be over geometry too + ci.blendEnable = VK_TRUE; + ci.colorBlendOp = VK_BLEND_OP_ADD; + ci.srcAlphaBlendFactor = ci.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + ci.dstAlphaBlendFactor = ci.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + if (!createPipeline(g_render.pipelines + kVkPipeline_A_1, "A_1", &ci)) + return false; + } + + { + spec_data.alpha_test_threshold = 0.f; + ci.depthWriteEnable = VK_FALSE; + ci.depthTestEnable = VK_TRUE; + ci.blendEnable = VK_TRUE; + ci.colorBlendOp = VK_BLEND_OP_ADD; + ci.srcAlphaBlendFactor = ci.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + ci.dstAlphaBlendFactor = ci.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + if (!createPipeline(g_render.pipelines + kVkPipeline_A_1_R, "A_1_R", &ci)) + return false; + } + + { + spec_data.alpha_test_threshold = .25f; + ci.depthWriteEnable = VK_TRUE; + ci.depthTestEnable = VK_TRUE; + ci.blendEnable = VK_FALSE; + if (!createPipeline(g_render.pipelines + kVkPipeline_AT, "AT", &ci)) + return false; + } + + { + spec_data.alpha_test_threshold = 0.f; + ci.depthWriteEnable = VK_FALSE; + ci.depthTestEnable = VK_TRUE; + ci.blendEnable = VK_TRUE; + ci.colorBlendOp = VK_BLEND_OP_ADD; + ci.srcAlphaBlendFactor = ci.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + ci.dstAlphaBlendFactor = ci.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + if (!createPipeline(g_render.pipelines + kVkPipeline_1_1_R, "1_1_R", &ci)) + return false; } } + if (!createSkyboxPipeline()) + return false; + return true; } @@ -355,6 +445,9 @@ void VK_RenderShutdown( void ) vkDestroyPipeline(vk_core.device, g_render.pipelines[i], NULL); vkDestroyPipelineLayout( vk_core.device, g_render.pipeline_layout, NULL ); + vkDestroyPipeline(vk_core.device, g_render.pipeline_sky.pipeline, NULL); + VK_DescriptorsDestroy(&g_render.pipeline_sky.descs); + VK_BufferDestroy( &g_render.uniform_buffer ); } @@ -743,6 +836,10 @@ static void submitToTraditionalRender( trad_submit_t args ) { if (tex < 0) continue; + // TODO + if (tex == TEX_BASE_SKYBOX) + continue; + if (split) { if (element_count) { render_draw_t draw = { diff --git a/ref/vk/vk_render.h b/ref/vk/vk_render.h index 00e4ea19dc..836fbeae7c 100644 --- a/ref/vk/vk_render.h +++ b/ref/vk/vk_render.h @@ -10,6 +10,8 @@ void VK_RenderShutdown( void ); struct ref_viewpass_s; void VK_RenderSetupCamera( const struct ref_viewpass_s *rvp ); +#define TEX_BASE_SKYBOX 0x0f000000u // FIXME ray_interop.h + typedef struct vk_render_geometry_s { int index_offset, vertex_offset; From 30f2b6372b3223a9079373915e94111a34802b46 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Mon, 5 Feb 2024 12:52:20 -0500 Subject: [PATCH 2/6] vk: make trad render draw skybox It does it in a super dirty way, trad renderer needs a massive refactoring. It also displays patched skybox, however, we need it to display the oridingal one. --- ref/vk/TODO.md | 4 +- ref/vk/vk_descriptor.c | 1 + ref/vk/vk_framectl.c | 5 +- ref/vk/vk_render.c | 250 ++++++++++++++++++++++++++++++----------- ref/vk/vk_render.h | 2 +- 5 files changed, 195 insertions(+), 67 deletions(-) diff --git a/ref/vk/TODO.md b/ref/vk/TODO.md index d70f6883dd..c03289e68a 100644 --- a/ref/vk/TODO.md +++ b/ref/vk/TODO.md @@ -3,8 +3,8 @@ ## 2024-02-05 E373 - [ ] Skybox for traditional renderer - - [-] Sky pipeline - - [ ] Submit SURF_DRAWSKY draw commands + - [x] Sky pipeline + - [x] Submit SURF_DRAWSKY draw commands - [ ] Use original skybox for trad renderer # Previously diff --git a/ref/vk/vk_descriptor.c b/ref/vk/vk_descriptor.c index 05ede5aaab..93727e26a1 100644 --- a/ref/vk/vk_descriptor.c +++ b/ref/vk/vk_descriptor.c @@ -189,6 +189,7 @@ void VK_DescriptorsWrite(const vk_descriptors_t *desc, int set_slot) switch (binding->descriptorType) { case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: // TODO ASSERT(wds[i].descriptorCount == 1); wds[i].pBufferInfo = &desc->values[i].buffer; diff --git a/ref/vk/vk_framectl.c b/ref/vk/vk_framectl.c index 1a6d937cf7..5ad5c1c193 100644 --- a/ref/vk/vk_framectl.c +++ b/ref/vk/vk_framectl.c @@ -300,7 +300,10 @@ static void enqueueRendering( vk_combuf_t* combuf, qboolean draw ) { } if (!vk_frame.rtx_enabled) - VK_RenderEnd( cmdbuf, draw ); + VK_RenderEnd( cmdbuf, draw, + g_frame.current.framebuffer.width, g_frame.current.framebuffer.height, + g_frame.current.index + ); R_VkOverlay_DrawAndFlip( cmdbuf, draw ); diff --git a/ref/vk/vk_render.c b/ref/vk/vk_render.c index ac3b405677..98785a2f67 100644 --- a/ref/vk/vk_render.c +++ b/ref/vk/vk_render.c @@ -39,6 +39,14 @@ typedef struct { vec4_t color; } uniform_data_t; +typedef struct { + matrix4x4 mvp; + matrix4x4 inv_proj; + matrix4x4 inv_view; + vec2_t resolution; + float pad_[2]; +} sky_uniform_data_t; + enum { // These correspond to kVkRenderType* kVkPipeline_Solid, // no blending, depth RW @@ -56,7 +64,10 @@ enum { typedef struct { VkPipeline pipeline; - VkDescriptorSet sets[1]; +#define MAX_CONCURRENT_FRAMES 2 + VkDescriptorSet sets[MAX_CONCURRENT_FRAMES]; + VkDescriptorSetLayoutBinding bindings[2]; + vk_descriptor_value_t values[2]; vk_descriptors_t descs; } r_pipeline_sky_t; @@ -116,28 +127,31 @@ static qboolean createSkyboxPipeline( void ) { {.binding = 0, .location = 0, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(vk_vertex_t, pos)}, }; - const VkDescriptorSetLayoutBinding bindings[] = {{ + g_render.pipeline_sky.bindings[0] = (VkDescriptorSetLayoutBinding){ .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = NULL, - }, { + }; + g_render.pipeline_sky.bindings[1] = (VkDescriptorSetLayoutBinding) { .binding = 1, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = NULL, - }}; + }; g_render.pipeline_sky.descs = (vk_descriptors_t){ - .bindings = bindings, - .num_bindings = COUNTOF(bindings), + .num_bindings = COUNTOF(g_render.pipeline_sky.bindings), + .bindings = g_render.pipeline_sky.bindings, - .desc_sets = g_render.pipeline_sky.sets, - .num_sets = COUNTOF(g_render.pipeline_sky.sets), + .values = g_render.pipeline_sky.values, .push_constants = (VkPushConstantRange){0}, + + .num_sets = COUNTOF(g_render.pipeline_sky.sets), + .desc_sets = g_render.pipeline_sky.sets, }; VK_DescriptorsCreate(&g_render.pipeline_sky.descs); @@ -353,10 +367,17 @@ typedef struct render_draw_s { uint32_t index_offset, vertex_offset; } render_draw_t; +typedef struct render_draw_sky_s { + uint32_t element_count; + uint32_t index_offset, vertex_offset; + // TODO matrix4x4 model; +} render_draw_sky_t; + enum draw_command_type_e { DrawLabelBegin, DrawLabelEnd, - DrawDraw + DrawDraw, + DrawSky, }; typedef struct { @@ -364,6 +385,7 @@ typedef struct { union { char debug_label[MAX_DEBUG_NAME_LENGTH]; render_draw_t draw; + render_draw_sky_t draw_sky; }; } draw_command_t; @@ -547,6 +569,7 @@ static void drawCmdPushDraw( const render_draw_t *draw ) ASSERT(draw->pipeline_index < ARRAYSIZE(g_render.pipelines)); ASSERT(draw->lightmap >= 0); ASSERT(draw->texture >= 0); + ASSERT(draw->texture < MAX_TEXTURES); if (g_render_state.num_draw_commands >= ARRAYSIZE(g_render_state.draw_commands)) { gEngine.Con_Printf( S_ERROR "Maximum number of draw commands reached\n" ); @@ -566,6 +589,20 @@ static void drawCmdPushDraw( const render_draw_t *draw ) draw_command->type = DrawDraw; } +static void drawCmdPushDrawSky( const render_draw_sky_t *draw_sky ) +{ + draw_command_t *draw_command; + + if (g_render_state.num_draw_commands >= ARRAYSIZE(g_render_state.draw_commands)) { + gEngine.Con_Printf( S_ERROR "Maximum number of draw commands reached\n" ); + return; + } + + draw_command = drawCmdAlloc(); + draw_command->draw_sky = *draw_sky; + draw_command->type = DrawSky; +} + // Return offset of dlights data into UBO buffer static uint32_t writeDlightsToUBO( void ) { @@ -626,7 +663,7 @@ void VK_Render_FIXME_Barrier( VkCommandBuffer cmdbuf ) { } } -void VK_RenderEnd( VkCommandBuffer cmdbuf, qboolean draw ) +void VK_RenderEnd( VkCommandBuffer cmdbuf, qboolean draw, uint32_t width, uint32_t height, int frame_index ) { if (!draw) return; @@ -634,10 +671,17 @@ void VK_RenderEnd( VkCommandBuffer cmdbuf, qboolean draw ) // TODO we can sort collected draw commands for more efficient and correct rendering // that requires adding info about distance to camera for correct order-dependent blending - int pipeline = -1; - int texture = -1; - int lightmap = -1; - uint32_t ubo_offset = -1; + struct { + VkPipeline pipeline; + int texture; + int lightmap; + uint32_t ubo_offset; + } cur = { + .pipeline = VK_NULL_HANDLE, + .texture = -1, + .lightmap = -1, + .ubo_offset = -1, + }; const uint32_t dlights_ubo_offset = writeDlightsToUBO(); if (dlights_ubo_offset == UINT32_MAX) @@ -645,7 +689,6 @@ void VK_RenderEnd( VkCommandBuffer cmdbuf, qboolean draw ) ASSERT(!g_render_state.current_frame_is_ray_traced); - { const VkBuffer geom_buffer = R_GeometryBuffer_Get(); const VkDeviceSize offset = 0; @@ -653,51 +696,115 @@ void VK_RenderEnd( VkCommandBuffer cmdbuf, qboolean draw ) vkCmdBindIndexBuffer(cmdbuf, geom_buffer, 0, VK_INDEX_TYPE_UINT16); } - vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 3, 1, vk_desc_fixme.ubo_sets + 1, 1, &dlights_ubo_offset); - for (int i = 0; i < g_render_state.num_draw_commands; ++i) { const draw_command_t *const draw = g_render_state.draw_commands + i; switch (draw->type) { case DrawLabelBegin: - { - VkDebugUtilsLabelEXT label = { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, - .pLabelName = draw->debug_label, - }; - vkCmdBeginDebugUtilsLabelEXT(cmdbuf, &label); - } + { + const VkDebugUtilsLabelEXT label = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, + .pLabelName = draw->debug_label, + }; + vkCmdBeginDebugUtilsLabelEXT(cmdbuf, &label); continue; + } + case DrawLabelEnd: vkCmdEndDebugUtilsLabelEXT(cmdbuf); continue; + case DrawSky: + { + const render_draw_sky_t *draw_sky = &draw->draw_sky; + + if (cur.pipeline != g_render.pipeline_sky.pipeline) { + const uint32_t ubo_offset = allocUniform(sizeof(sky_uniform_data_t), 16 /*?*/); + if (g_render_state.current_ubo_offset_FIXME == ALO_ALLOC_FAILED) + continue; + + // Compute and upload UBO stuff + { + sky_uniform_data_t* const sky_ubo = (sky_uniform_data_t*)((byte*)g_render.uniform_buffer.mapped + ubo_offset); + + // FIXME model matrix + Matrix4x4_ToArrayFloatGL(g_render_state.projection_view, (float*)sky_ubo->mvp); + + sky_ubo->resolution[0] = width; + sky_ubo->resolution[1] = height; + + // TODO DRY, this is copypasted from vk_rtx.c + matrix4x4 proj_inv, view_inv; + Matrix4x4_Invert_Full(proj_inv, g_render_state.vk_projection); + Matrix4x4_ToArrayFloatGL(proj_inv, (float*)sky_ubo->inv_proj); + + // TODO there's a more efficient way to construct an inverse view matrix + // from vforward/right/up vectors and origin in g_camera + Matrix4x4_Invert_Full(view_inv, g_camera.viewMatrix); + Matrix4x4_ToArrayFloatGL(view_inv, (float*)sky_ubo->inv_view); + } + + cur.pipeline = g_render.pipeline_sky.pipeline; + vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, cur.pipeline); + + g_render.pipeline_sky.values[0].buffer = (VkDescriptorBufferInfo){ + .buffer = g_render.uniform_buffer.buffer, + .offset = 0, + .range = sizeof(sky_uniform_data_t), + }; + g_render.pipeline_sky.values[1].image = R_VkTexturesGetSkyboxDescriptorImageInfo(); + VK_DescriptorsWrite(&g_render.pipeline_sky.descs, frame_index); + + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, + g_render.pipeline_sky.descs.pipeline_layout, 0, 1, g_render.pipeline_sky.sets + frame_index, 1, &ubo_offset); + } + + ASSERT(draw_sky->index_offset >= 0); + vkCmdDrawIndexed(cmdbuf, draw_sky->element_count, 1, draw_sky->index_offset, draw_sky->vertex_offset, 0); + + // Reset current draw state + cur.texture = -1; + cur.lightmap = -1; + cur.ubo_offset = -1; + + continue; + } + case DrawDraw: // Continue drawing below break; } - if (ubo_offset != draw->draw.ubo_offset) - { - ubo_offset = draw->draw.ubo_offset; - vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 0, 1, vk_desc_fixme.ubo_sets, 1, &ubo_offset); + ASSERT(draw->draw.pipeline_index >= 0); + ASSERT(draw->draw.pipeline_index < COUNTOF(g_render.pipelines)); + const VkPipeline pipeline = g_render.pipelines[draw->draw.pipeline_index]; + + if (cur.pipeline != pipeline) { + cur.pipeline = pipeline; + vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, cur.pipeline); + + // Make sure that after pipeline change we have this bound correctly + // Pipeline change might be due to previous pipeline being skybox, which has + // incompatible layout + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 3, 1, vk_desc_fixme.ubo_sets + 1, 1, &dlights_ubo_offset); } - if (pipeline != draw->draw.pipeline_index) { - pipeline = draw->draw.pipeline_index; - vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipelines[pipeline]); + if (cur.ubo_offset != draw->draw.ubo_offset) + { + cur.ubo_offset = draw->draw.ubo_offset; + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 0, 1, vk_desc_fixme.ubo_sets, 1, &cur.ubo_offset); } - if (lightmap != draw->draw.lightmap) { - lightmap = draw->draw.lightmap; - const VkDescriptorSet lm_unorm = R_VkTextureGetDescriptorUnorm(lightmap); + if (cur.lightmap != draw->draw.lightmap) { + cur.lightmap = draw->draw.lightmap; + const VkDescriptorSet lm_unorm = R_VkTextureGetDescriptorUnorm(cur.lightmap); vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 2, 1, &lm_unorm, 0, NULL); } - if (texture != draw->draw.texture) + if (cur.texture != draw->draw.texture) { - texture = draw->draw.texture; - const VkDescriptorSet tex_unorm = R_VkTextureGetDescriptorUnorm(texture); + cur.texture = draw->draw.texture; + const VkDescriptorSet tex_unorm = R_VkTextureGetDescriptorUnorm(cur.texture); // TODO names/enums for binding points vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 1, 1, &tex_unorm, 0, NULL); } @@ -812,10 +919,10 @@ static void submitToTraditionalRender( trad_submit_t args ) { int index_offset = -1; int vertex_offset = 0; - uboComputeAndSetMVPFromModel( *args.transform ); - // TODO get rid of this dirty ubo thing + uboComputeAndSetMVPFromModel( *args.transform ); Vector4Copy(*args.color, g_render_state.dirty_uniform_data.color); + ASSERT(args.lightmap <= MAX_LIGHTMAPS); const int lightmap = args.lightmap > 0 ? tglob.lightmapTextures[args.lightmap - 1] : tglob.whiteTexture; @@ -826,7 +933,8 @@ static void submitToTraditionalRender( trad_submit_t args ) { const int tex_mat = geom->material.tex_base_color; const int geom_tex = g_render.use_material_textures->value && (tex_mat > 0 && tex_mat < MAX_TEXTURES) ? tex_mat : geom->ye_olde_texture; const int tex = args.textures_override > 0 ? args.textures_override : geom_tex; - const qboolean split = current_texture != tex + const qboolean split = + current_texture != tex || vertex_offset != geom->vertex_offset || (index_offset + element_count) != geom->index_offset; @@ -836,22 +944,30 @@ static void submitToTraditionalRender( trad_submit_t args ) { if (tex < 0) continue; - // TODO - if (tex == TEX_BASE_SKYBOX) - continue; - + // TODO consider tracking contiguousness in drawCmdPushDraw(Sky)() + // Why: we could easily check that the previous command in the command list + // is contiguous, and could just increase its counts w/o submitting a new command + // This would make this code here a bit more readable and single-purpose. if (split) { if (element_count) { - render_draw_t draw = { - .lightmap = lightmap, - .texture = current_texture, - .pipeline_index = args.render_type, - .element_count = element_count, - .vertex_offset = vertex_offset, - .index_offset = index_offset, - }; + if (current_texture == TEX_BASE_SKYBOX) { + drawCmdPushDrawSky(&(render_draw_sky_t){ + .element_count = element_count, + .vertex_offset = vertex_offset, + .index_offset = index_offset, + }); + } else { + render_draw_t draw = { + .lightmap = lightmap, + .texture = current_texture, + .pipeline_index = args.render_type, + .element_count = element_count, + .vertex_offset = vertex_offset, + .index_offset = index_offset, + }; - drawCmdPushDraw( &draw ); + drawCmdPushDraw( &draw ); + } } current_texture = tex; @@ -866,16 +982,24 @@ static void submitToTraditionalRender( trad_submit_t args ) { } if (element_count) { - const render_draw_t draw = { - .lightmap = lightmap, - .texture = current_texture, - .pipeline_index = args.render_type, - .element_count = element_count, - .vertex_offset = vertex_offset, - .index_offset = index_offset, - }; - - drawCmdPushDraw( &draw ); + if (current_texture == TEX_BASE_SKYBOX) { + drawCmdPushDrawSky(&(render_draw_sky_t){ + .element_count = element_count, + .vertex_offset = vertex_offset, + .index_offset = index_offset, + }); + } else { + const render_draw_t draw = { + .lightmap = lightmap, + .texture = current_texture, + .pipeline_index = args.render_type, + .element_count = element_count, + .vertex_offset = vertex_offset, + .index_offset = index_offset, + }; + + drawCmdPushDraw( &draw ); + } } drawCmdPushDebugLabelEnd(); diff --git a/ref/vk/vk_render.h b/ref/vk/vk_render.h index 836fbeae7c..e9a5747949 100644 --- a/ref/vk/vk_render.h +++ b/ref/vk/vk_render.h @@ -165,7 +165,7 @@ void VK_RenderDebugLabelBegin( const char *label ); void VK_RenderDebugLabelEnd( void ); void VK_RenderBegin( qboolean ray_tracing ); -void VK_RenderEnd( VkCommandBuffer cmdbuf, qboolean draw ); +void VK_RenderEnd( VkCommandBuffer cmdbuf, qboolean draw, uint32_t width, uint32_t height, int frame_index ); struct vk_combuf_s; void VK_RenderEndRTX( struct vk_combuf_s* combuf, VkImageView img_dst_view, VkImage img_dst, uint32_t w, uint32_t h ); From 9fc1f85bcd472a03227c47002ce5865bc891374f Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Mon, 5 Feb 2024 13:21:21 -0500 Subject: [PATCH 3/6] vk: use original skybox for trad renderer Also fix: - a bunch of texture/image destruction issues - `_xvk_remove_all_sky_surfaces` getting stuck --- ref/vk/TODO.md | 6 ++--- ref/vk/r_textures.c | 19 ++++++-------- ref/vk/r_textures.h | 8 ++++++ ref/vk/vk_image.c | 3 ++- ref/vk/vk_mapents.c | 1 + ref/vk/vk_render.c | 2 +- ref/vk/vk_rtx.c | 2 +- ref/vk/vk_textures.c | 60 ++++++++++++++++++++++++++++++-------------- ref/vk/vk_textures.h | 4 +-- 9 files changed, 67 insertions(+), 38 deletions(-) diff --git a/ref/vk/TODO.md b/ref/vk/TODO.md index c03289e68a..77e5038f7b 100644 --- a/ref/vk/TODO.md +++ b/ref/vk/TODO.md @@ -1,13 +1,13 @@ ## Next - [ ] performance profiling and comparison +# Previously ## 2024-02-05 E373 -- [ ] Skybox for traditional renderer +- [x] Skybox for traditional renderer - [x] Sky pipeline - [x] Submit SURF_DRAWSKY draw commands - - [ ] Use original skybox for trad renderer + - [x] Use original skybox for trad renderer -# Previously ## 2024-02-01 E371 - [x] tune A-Trous step widths for different channels - [x] multiple passes -- core of the paper lol diff --git a/ref/vk/r_textures.c b/ref/vk/r_textures.c index 9be158e4b6..0256175a4f 100644 --- a/ref/vk/r_textures.c +++ b/ref/vk/r_textures.c @@ -5,16 +5,12 @@ #include "vk_const.h" #include "vk_mapents.h" // wadlist #include "vk_logs.h" -#include "r_speeds.h" #include "profiler.h" #include "unordered_roadmap.h" #include "stringview.h" #include "xash3d_mathlib.h" #include "crtlib.h" -#include "crclib.h" // COM_HashKey -#include "com_strings.h" -#include "eiface.h" // ARRAYSIZE #include #include @@ -239,7 +235,7 @@ static void createDefaultTextures( void ) memset(pic->buffer, 0, pic->size); const qboolean is_placeholder = true; - R_VkTexturesSkyboxUpload( "skybox_placeholder", pic, kColorspaceGamma, is_placeholder ); + R_VkTexturesSkyboxUpload( "skybox_placeholder", pic, kColorspaceGamma, kSkyboxPlaceholder ); } } @@ -777,7 +773,7 @@ static void skyboxParseInfo( const char *name ) { Mem_Free(data); } -static qboolean skyboxLoadF(const char *fmt, ...) { +static qboolean skyboxLoadF(skybox_slot_e slot, const char *fmt, ...) { qboolean success = false; char buffer[MAX_STRING]; @@ -805,7 +801,7 @@ static qboolean skyboxLoadF(const char *fmt, ...) { { const qboolean is_placeholder = false; - success = R_VkTexturesSkyboxUpload( buffer, pic, kColorspaceGamma, is_placeholder ); + success = R_VkTexturesSkyboxUpload( buffer, pic, kColorspaceGamma, slot ); } if (success) @@ -854,12 +850,13 @@ static qboolean skyboxTryLoad( const char *skyboxname, qboolean force_reload ) { if (!force_reload && svCmp(basename, g_textures.skybox.current_name) == 0) return true; + // Try loading original game skybox + const qboolean original = skyboxLoadF(kSkyboxOriginal, "gfx/env/%.*s", basename.len, basename.s); + // Try loading newer "PBR" upscaled skybox first - if (skyboxLoadF("pbr/env/%.*s", basename.len, basename.s)) - goto success; + const qboolean patched = skyboxLoadF(kSkyboxPatched, "pbr/env/%.*s", basename.len, basename.s); - // Try loading original game skybox - if (skyboxLoadF("gfx/env/%.*s", basename.len, basename.s)) + if (original || patched) goto success; skyboxUnload(); diff --git a/ref/vk/r_textures.h b/ref/vk/r_textures.h index ab897f0acd..c6eb523e5c 100644 --- a/ref/vk/r_textures.h +++ b/ref/vk/r_textures.h @@ -58,6 +58,14 @@ typedef enum { kColorspaceGamma, } colorspace_hint_e; +typedef enum { + kSkyboxPlaceholder, + kSkyboxOriginal, + kSkyboxPatched, + + kSkybox_COUNT, +} skybox_slot_e; + int R_TextureUploadFromFileExAcquire( const char *filename, colorspace_hint_e colorspace, qboolean force_reload ); int R_TextureFindByNameF( const char *fmt, ...); diff --git a/ref/vk/vk_image.c b/ref/vk/vk_image.c index 11297ee271..b875d42f5d 100644 --- a/ref/vk/vk_image.c +++ b/ref/vk/vk_image.c @@ -118,7 +118,8 @@ void R_VkImageDestroy(r_vk_image_t *img) { if (img->view != VK_NULL_HANDLE) vkDestroyImageView(vk_core.device, img->view, NULL); - vkDestroyImage(vk_core.device, img->image, NULL); + if (img->image != VK_NULL_HANDLE) + vkDestroyImage(vk_core.device, img->image, NULL); VK_DevMemFree(&img->devmem); *img = (r_vk_image_t){0}; diff --git a/ref/vk/vk_mapents.c b/ref/vk/vk_mapents.c index 3929b3309c..172f5e2280 100644 --- a/ref/vk/vk_mapents.c +++ b/ref/vk/vk_mapents.c @@ -869,6 +869,7 @@ void XVK_ParseMapEntities( void ) { for (int i = 0; i < g_map_entities.smoothing.groups_count; ++i) g_map_entities.smoothing.groups[i].count = 0; g_map_entities.smoothing.groups_count = 0; + g_map_entities.remove_all_sky_surfaces = 0; parseEntities( map->entities, false ); orientSpotlights(); diff --git a/ref/vk/vk_render.c b/ref/vk/vk_render.c index 98785a2f67..c72fa3eb6a 100644 --- a/ref/vk/vk_render.c +++ b/ref/vk/vk_render.c @@ -752,7 +752,7 @@ void VK_RenderEnd( VkCommandBuffer cmdbuf, qboolean draw, uint32_t width, uint32 .offset = 0, .range = sizeof(sky_uniform_data_t), }; - g_render.pipeline_sky.values[1].image = R_VkTexturesGetSkyboxDescriptorImageInfo(); + g_render.pipeline_sky.values[1].image = R_VkTexturesGetSkyboxDescriptorImageInfo( kSkyboxOriginal ); VK_DescriptorsWrite(&g_render.pipeline_sky.descs, frame_index); vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, diff --git a/ref/vk/vk_rtx.c b/ref/vk/vk_rtx.c index 34769e9f50..004fdb2351 100644 --- a/ref/vk/vk_rtx.c +++ b/ref/vk/vk_rtx.c @@ -128,7 +128,7 @@ void VK_RayNewMapEnd( void ) { g_rtx.res[ExternalResource_skybox].resource = (vk_resource_t){ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .value = (vk_descriptor_value_t){ - .image = R_VkTexturesGetSkyboxDescriptorImageInfo(), + .image = R_VkTexturesGetSkyboxDescriptorImageInfo( kSkyboxPatched ), }, }; diff --git a/ref/vk/vk_textures.c b/ref/vk/vk_textures.c index 36b002db35..80600e0caa 100644 --- a/ref/vk/vk_textures.c +++ b/ref/vk/vk_textures.c @@ -4,11 +4,8 @@ #include "vk_descriptor.h" #include "vk_staging.h" #include "vk_logs.h" -#include "vk_combuf.h" #include "r_textures.h" #include "r_speeds.h" -#include "alolcator.h" -#include "profiler.h" #include "xash3d_mathlib.h" // bound @@ -17,8 +14,6 @@ #define PCG_IMPLEMENT #include "pcg.h" -#include // sqrt - #define LOG_MODULE tex #define MODULE_NAME "textures" @@ -43,8 +38,7 @@ static struct { // All textures descriptors in their native formats used for RT VkDescriptorImageInfo dii_all_textures[MAX_TEXTURES]; - vk_texture_t skybox_cube; - vk_texture_t cubemap_placeholder; + vk_texture_t skybox[kSkybox_COUNT]; vk_texture_t blue_noise; } g_vktextures; @@ -178,7 +172,7 @@ qboolean R_VkTexturesInit( void ) { if (vk_core.rtx) loadBlueNoiseTextures(); - + return true; } @@ -186,10 +180,14 @@ static void textureDestroy( unsigned int index ); void R_VkTexturesShutdown( void ) { R_VkTexturesSkyboxUnload(); - R_VkTextureDestroy(-1, &g_vktextures.cubemap_placeholder); + + for (int i = 0; i < COUNTOF(g_vktextures.skybox); ++i) { + R_VkTextureDestroy(-1, g_vktextures.skybox + i); + } + if (vk_core.rtx) R_VkTextureDestroy(-1, &g_vktextures.blue_noise); - + for (int i = 0; i < COUNTOF(g_vktextures.samplers); ++i) { if (g_vktextures.samplers[i].sampler != VK_NULL_HANDLE) vkDestroySampler(vk_core.device, g_vktextures.samplers[i].sampler, NULL); @@ -611,6 +609,12 @@ qboolean R_VkTextureUpload(int index, vk_texture_t *tex, const rgbdata_t *pic, c } void R_VkTextureDestroy( int index, vk_texture_t *tex ) { + if (!tex) + return; + + if (tex->vk.image.image == VK_NULL_HANDLE) + return; + // Need to make sure that there are no references to this texture anywhere. // It might have been added to staging and then immediately deleted, leaving references to its vkimage // in the staging command buffer. See https://github.com/w23/xash3d-fwgs/issues/464 @@ -633,24 +637,42 @@ void R_VkTextureDestroy( int index, vk_texture_t *tex ) { void R_VkTexturesSkyboxUnload(void) { DEBUG("%s", __FUNCTION__); - if (g_vktextures.skybox_cube.vk.image.image) { - R_VkTextureDestroy( -1, &g_vktextures.skybox_cube ); - memset(&g_vktextures.skybox_cube, 0, sizeof(g_vktextures.skybox_cube)); + + for (int i = 0; i < kSkybox_COUNT; ++i) { + if (i == kSkyboxPlaceholder) + continue; + + vk_texture_t* const skybox = g_vktextures.skybox + i; + if (skybox->vk.image.image) { + R_VkTextureDestroy( -1, skybox ); + memset(skybox, 0, sizeof(*skybox)); + } } } -VkDescriptorImageInfo R_VkTexturesGetSkyboxDescriptorImageInfo( void ) { +VkDescriptorImageInfo R_VkTexturesGetSkyboxDescriptorImageInfo( skybox_slot_e slot ) { + vk_texture_t *skybox = g_vktextures.skybox + slot; + + if (skybox->vk.image.view == VK_NULL_HANDLE) + skybox = g_vktextures.skybox + kSkyboxOriginal; + + if (skybox->vk.image.view == VK_NULL_HANDLE) + skybox = g_vktextures.skybox + kSkyboxPlaceholder; + + ASSERT(skybox->vk.image.view != VK_NULL_HANDLE); + return (VkDescriptorImageInfo){ .sampler = g_vktextures.default_sampler, - .imageView = g_vktextures.skybox_cube.vk.image.view - ? g_vktextures.skybox_cube.vk.image.view - : g_vktextures.cubemap_placeholder.vk.image.view, + .imageView = skybox->vk.image.view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }; } -qboolean R_VkTexturesSkyboxUpload( const char *name, const rgbdata_t *pic, colorspace_hint_e colorspace_hint, qboolean placeholder) { - vk_texture_t *const dest = placeholder ? &g_vktextures.cubemap_placeholder : &g_vktextures.skybox_cube; +qboolean R_VkTexturesSkyboxUpload( const char *name, const rgbdata_t *pic, colorspace_hint_e colorspace_hint, skybox_slot_e skybox_slot ) { + ASSERT(skybox_slot >= 0); + ASSERT(skybox_slot < kSkybox_COUNT); + + vk_texture_t *const dest = g_vktextures.skybox + skybox_slot; Q_strncpy( TEX_NAME(dest), name, sizeof( TEX_NAME(dest) )); dest->flags |= TF_NOMIPMAP; ASSERT(pic->flags & IMAGE_CUBEMAP); diff --git a/ref/vk/vk_textures.h b/ref/vk/vk_textures.h index a31e01c394..d261b8c8fa 100644 --- a/ref/vk/vk_textures.h +++ b/ref/vk/vk_textures.h @@ -34,13 +34,13 @@ typedef struct vk_texture_s qboolean R_VkTexturesInit( void ); void R_VkTexturesShutdown( void ); -qboolean R_VkTexturesSkyboxUpload( const char *name, const rgbdata_t *pic, colorspace_hint_e colorspace_hint, qboolean placeholder); +qboolean R_VkTexturesSkyboxUpload( const char *name, const rgbdata_t *pic, colorspace_hint_e colorspace_hint, skybox_slot_e skybox_slot ); void R_VkTexturesSkyboxUnload(void); qboolean R_VkTextureUpload(int index, vk_texture_t *tex, const rgbdata_t *pic, colorspace_hint_e colorspace_hint); void R_VkTextureDestroy(int index, vk_texture_t *tex); -VkDescriptorImageInfo R_VkTexturesGetSkyboxDescriptorImageInfo( void ); +VkDescriptorImageInfo R_VkTexturesGetSkyboxDescriptorImageInfo( skybox_slot_e slot ); const VkDescriptorImageInfo* R_VkTexturesGetAllDescriptorsArray( void ); VkDescriptorSet R_VkTextureGetDescriptorUnorm( uint index ); From e5a33ea0c3c4a36a08cd08fcfb0e255456811ffc Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Tue, 6 Feb 2024 10:20:35 -0500 Subject: [PATCH 4/6] vk: don't forget to add new shaders --- ref/vk/shaders/sky.frag | 30 ++++++++++++++++++++++++++++++ ref/vk/shaders/sky.vert | 14 ++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 ref/vk/shaders/sky.frag create mode 100644 ref/vk/shaders/sky.vert diff --git a/ref/vk/shaders/sky.frag b/ref/vk/shaders/sky.frag new file mode 100644 index 0000000000..ade97bdbcd --- /dev/null +++ b/ref/vk/shaders/sky.frag @@ -0,0 +1,30 @@ +#version 450 + +layout(set = 0, binding = 0) uniform UBO { + mat4 mvp; + mat4 inv_proj; + mat4 inv_view; + vec2 resolution; +} ubo; + +layout(set = 0, binding = 1) uniform samplerCube skybox; + +layout(location = 0) out vec4 out_color; + +vec3 clipToWorldSpace(vec3 clip) { + const vec4 eye_space = ubo.inv_proj * vec4(clip, 1.); + return (ubo.inv_view * vec4(eye_space.xyz / eye_space.w, 1.)).xyz; +} + +vec3 getDirection(in vec2 uv) { + uv = uv * 2. - 1.; + const vec3 world_near = clipToWorldSpace(vec3(uv, 0.)); + const vec3 world_far = clipToWorldSpace(vec3(uv, 1.)); + return normalize(world_far - world_near); +} + +void main() { + const vec2 uv = gl_FragCoord.xy / ubo.resolution; + const vec3 direction = getDirection(uv); + out_color = texture(skybox, direction); +} diff --git a/ref/vk/shaders/sky.vert b/ref/vk/shaders/sky.vert new file mode 100644 index 0000000000..a44f80f434 --- /dev/null +++ b/ref/vk/shaders/sky.vert @@ -0,0 +1,14 @@ +#version 450 + +layout(set=0,binding=0) uniform UBO { + mat4 mvp; + mat4 inv_proj; + mat4 inv_view; + vec2 resolution; +} ubo; + +layout(location=0) in vec3 a_pos; + +void main() { + gl_Position = ubo.mvp * vec4(a_pos.xyz, 1.); +} From f9c77060d711be814692ca0ec78ed20e458adef3 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Wed, 7 Feb 2024 09:40:33 -0800 Subject: [PATCH 5/6] vk: unload previous skybox prior to loading a new one Fixes #737 --- ref/vk/r_textures.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ref/vk/r_textures.c b/ref/vk/r_textures.c index 0256175a4f..4a581d267c 100644 --- a/ref/vk/r_textures.c +++ b/ref/vk/r_textures.c @@ -850,16 +850,18 @@ static qboolean skyboxTryLoad( const char *skyboxname, qboolean force_reload ) { if (!force_reload && svCmp(basename, g_textures.skybox.current_name) == 0) return true; + // Unload previous skybox + skyboxUnload(); + // Try loading original game skybox const qboolean original = skyboxLoadF(kSkyboxOriginal, "gfx/env/%.*s", basename.len, basename.s); - // Try loading newer "PBR" upscaled skybox first + // Try loading newer "PBR" upscaled skybox const qboolean patched = skyboxLoadF(kSkyboxPatched, "pbr/env/%.*s", basename.len, basename.s); if (original || patched) goto success; - skyboxUnload(); return false; success: From cf966b38cb127b250f668488bb70741c9bde5b56 Mon Sep 17 00:00:00 2001 From: Ivan Avdeev Date: Wed, 7 Feb 2024 09:41:45 -0800 Subject: [PATCH 6/6] vk: fix a couple of misc warnings --- ref/vk/vk_rtx.c | 4 +++- ref/vk/wscript | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ref/vk/vk_rtx.c b/ref/vk/vk_rtx.c index 004fdb2351..9f1420fd66 100644 --- a/ref/vk/vk_rtx.c +++ b/ref/vk/vk_rtx.c @@ -212,7 +212,9 @@ static void parseDebugFlags( void ) { } else LIST_DEBUG_FLAGS(X) #undef X - /* else: no valid flags found */ { + + /* else: no valid flags found */ + if (Q_strlen(cvalue) > 0) { gEngine.Con_Printf("Invalid rt_debug_flags value %s. Valid flags are:\n", cvalue); #define X(suffix, info) gEngine.Con_Printf("\t%s -- %s\n", #suffix, info); LIST_DEBUG_FLAGS(X) diff --git a/ref/vk/wscript b/ref/vk/wscript index 6270e95791..4b71986b8c 100644 --- a/ref/vk/wscript +++ b/ref/vk/wscript @@ -109,7 +109,7 @@ def build(bld): if bld.env.DEST_OS == 'win32': includes.append(bld.env.VULKAN_SDK + '\\Include') - includes.append(bld.env.VULKAN_SDK + '\\Source\SPIRV-Reflect\include') # for spirv.h + includes.append(bld.env.VULKAN_SDK + '\\Source\\SPIRV-Reflect\\include') # for spirv.h if bld.env.HAVE_AFTERMATH: defines.append('USE_AFTERMATH')