diff --git a/ref/vk/TODO.md b/ref/vk/TODO.md index e18321390d..77e5038f7b 100644 --- a/ref/vk/TODO.md +++ b/ref/vk/TODO.md @@ -1,6 +1,13 @@ ## Next - [ ] performance profiling and comparison +# Previously +## 2024-02-05 E373 +- [x] Skybox for traditional renderer + - [x] Sky pipeline + - [x] Submit SURF_DRAWSKY draw commands + - [x] Use original skybox for trad renderer + ## 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..4a581d267c 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,15 +850,18 @@ static qboolean skyboxTryLoad( const char *skyboxname, qboolean force_reload ) { if (!force_reload && svCmp(basename, g_textures.skybox.current_name) == 0) return true; - // Try loading newer "PBR" upscaled skybox first - if (skyboxLoadF("pbr/env/%.*s", basename.len, basename.s)) - goto success; + // Unload previous skybox + skyboxUnload(); // Try loading original game skybox - if (skyboxLoadF("gfx/env/%.*s", basename.len, basename.s)) + const qboolean original = skyboxLoadF(kSkyboxOriginal, "gfx/env/%.*s", basename.len, basename.s); + + // 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: @@ -872,7 +871,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/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/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.); +} 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.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_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..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 ); @@ -335,19 +338,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_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_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..c72fa3eb6a 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,43 @@ 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 + 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; +#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; + 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 +88,97 @@ 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)}, + }; + + 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){ + .num_bindings = COUNTOF(g_render.pipeline_sky.bindings), + .bindings = g_render.pipeline_sky.bindings, + + .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); + + 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 +228,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 +261,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; + } + + { + 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; + } - g_render.pipelines[i] = VK_PipelineGraphicsCreate(&ci); + { + 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 (!g_render.pipelines[i]) - { - // TODO complain + { + 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; - } + } - 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_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; } @@ -263,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 { @@ -274,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; @@ -355,6 +467,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 ); } @@ -454,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" ); @@ -473,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 ) { @@ -533,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; @@ -541,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) @@ -552,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; @@ -560,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( kSkyboxOriginal ); + 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); } @@ -719,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; @@ -733,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; @@ -743,18 +944,30 @@ static void submitToTraditionalRender( trad_submit_t args ) { if (tex < 0) 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; @@ -769,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 00e4ea19dc..e9a5747949 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; @@ -163,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 ); diff --git a/ref/vk/vk_rtx.c b/ref/vk/vk_rtx.c index 34769e9f50..9f1420fd66 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 ), }, }; @@ -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/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 ); 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')