diff --git a/ref_vk/shaders/rtx.comp b/ref_vk/shaders/rtx.comp index 2b264705..2939a751 100644 --- a/ref_vk/shaders/rtx.comp +++ b/ref_vk/shaders/rtx.comp @@ -138,6 +138,8 @@ float rand01() { return uintBitsToFloat(0x3f800000 | (rand() & 0x007fffff)) - 1.; } +float hash(float f) { return fract(sin(f)*53478.4327); } + layout (push_constant) uniform PC { float t; int bounces; @@ -180,6 +182,9 @@ void main() { vec3 pos = O+D*l; + C = fract(pos / 100.); + break; + //const int instance_index = rayQueryGetIntersectionInstanceIdEXT(rayQuery, true); const int instance_index = rayQueryGetIntersectionInstanceCustomIndexEXT(rayQuery, true); @@ -206,7 +211,7 @@ void main() { const Kusok kusok = kusochki[kusok_index]; if (kusok_index == instance_index) { // TODO do we need to do this when we have textures? - //C += kc * kusok.emissive.rgb; + C += kc * vec3(hash(float(instance_index)), hash(float(instance_index)+15.43), hash(float(instance_index)+34.));//kusok.emissive.rgb; continue; } @@ -286,6 +291,11 @@ void main() { C += kc * baseColor.rgb * light_color * dot_ld_norm * attenuation; } // for all lights + const Kusok kusok = kusochki[instance_index]; + if (any(greaterThan(kusok.emissive.rgb, vec3(0.)))) { + C += kc * vec3(hash(float(instance_index)-102.3), hash(float(instance_index)+15.43), hash(float(instance_index)+34.));//kusok.emissive.rgb; + } + kc *= .9; const float rough = .4; O = pos + .01 * normal; @@ -297,9 +307,10 @@ void main() { )); } // for all bounces - C = mix(C, vec3(1.), printText(vec2(1.,-1.) * vec2(gl_GlobalInvocationID.xy) + vec2(0., imageSize(image).y))); + //C = mix(C, vec3(1.), printText(vec2(1.,-1.) * vec2(gl_GlobalInvocationID.xy) + vec2(0., imageSize(image).y))); //if (gl_GlobalInvocationID.x > imageSize(image).x / 2) + if (false) { const float wet = .5; C = mix(C, imageLoad(previous_frame, ivec2(gl_GlobalInvocationID.xy)).rgb, wet); diff --git a/ref_vk/vk_brush.c b/ref_vk/vk_brush.c index 1df48e3e..22f19a65 100644 --- a/ref_vk/vk_brush.c +++ b/ref_vk/vk_brush.c @@ -240,6 +240,7 @@ static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) { model_geometry->index_offset = index_offset; model_geometry->vertex_offset = 0; model_geometry->texture = t; + model_geometry->vertex_count = surf->numedges; VK_CreateSurfaceLightmap( surf, mod ); diff --git a/ref_vk/vk_render.c b/ref_vk/vk_render.c index de159a43..7e18c8f9 100644 --- a/ref_vk/vk_render.c +++ b/ref_vk/vk_render.c @@ -30,8 +30,6 @@ typedef struct vk_buffer_alloc_s { uint32_t count; qboolean locked; vk_lifetime_t lifetime; - - vk_ray_model_handle_t rtx_model; } vk_buffer_alloc_t; // TODO estimate @@ -481,6 +479,9 @@ void VK_RenderBegin( void ) { memset(&g_render_state.dirty_uniform_data, 0, sizeof(g_render_state.dirty_uniform_data)); g_render_state.num_draw_commands = 0; + + if (vk_core.rtx) + VK_RayFrameBegin(); } void VK_RenderStateSetColor( float r, float g, float b, float a ) @@ -752,35 +753,37 @@ void VK_RenderEndRTX( VkCommandBuffer cmdbuf, VkImageView img_dst_view, VkImage return; ASSERT(vk_core.rtx); - VK_RaySceneBegin(); - for (int i = 0; i < g_render_state.num_draw_commands; ++i) { - const draw_command_t *const draw = g_render_state.draw_commands + i; - const vk_buffer_alloc_t *vertex_buffer = getBufferFromHandle( draw->draw.vertex_buffer ); - const vk_buffer_alloc_t *index_buffer = draw->draw.index_buffer != InvalidHandle ? getBufferFromHandle( draw->draw.index_buffer ) : NULL; - const uint32_t vertex_offset = vertex_buffer->buffer_offset_in_units + draw->draw.vertex_offset; + // FIXME fix me + if (!vk_core.rtx) { + for (int i = 0; i < g_render_state.num_draw_commands; ++i) { + const draw_command_t *const draw = g_render_state.draw_commands + i; + const vk_buffer_alloc_t *vertex_buffer = getBufferFromHandle( draw->draw.vertex_buffer ); + const vk_buffer_alloc_t *index_buffer = draw->draw.index_buffer != InvalidHandle ? getBufferFromHandle( draw->draw.index_buffer ) : NULL; + const uint32_t vertex_offset = vertex_buffer->buffer_offset_in_units + draw->draw.vertex_offset; - // TODO there's a more complex story with lifetimes and rebuilds && vertex_buffer->lifetime < LifetimeSingleFrame) - // TODO it would make sense to join logical models into a single ray model - // but here we've completely lost this info, as models are now just a stream - // of independent draws + // TODO there's a more complex story with lifetimes and rebuilds && vertex_buffer->lifetime < LifetimeSingleFrame) + // TODO it would make sense to join logical models into a single ray model + // but here we've completely lost this info, as models are now just a stream + // of independent draws - const vk_ray_model_dynamic_t dynamic_model = { - .element_count = draw->draw.element_count, - .max_vertex = vertex_buffer->count, // TODO this is an upper bound for brushes at least, it can be lowered - .index_offset = index_buffer ? (draw->draw.index_offset + index_buffer->buffer_offset_in_units) : UINT32_MAX, - .vertex_offset = (draw->draw.vertex_offset + vertex_buffer->buffer_offset_in_units), - .buffer = g_render.buffer.buffer, - .transform_row = &draw->transform, - .emissive = { draw->draw.emissive.r, draw->draw.emissive.g, draw->draw.emissive.b }, - .texture_id = draw->draw.texture, - }; + const vk_ray_model_dynamic_t dynamic_model = { + .element_count = draw->draw.element_count, + .max_vertex = vertex_buffer->count, // TODO this is an upper bound for brushes at least, it can be lowered + .index_offset = index_buffer ? (draw->draw.index_offset + index_buffer->buffer_offset_in_units) : UINT32_MAX, + .vertex_offset = (draw->draw.vertex_offset + vertex_buffer->buffer_offset_in_units), + .buffer = g_render.buffer.buffer, + .transform_row = &draw->transform, + .emissive = { draw->draw.emissive.r, draw->draw.emissive.g, draw->draw.emissive.b }, + .texture_id = draw->draw.texture, + }; - VK_RaySceneAddModelDynamic(cmdbuf, &dynamic_model); + VK_RayFrameAddModelDynamic(cmdbuf, &dynamic_model); + } } { - const vk_ray_scene_render_args_t args = { + const vk_ray_frame_render_args_t args = { .cmdbuf = cmdbuf, .dst = { .image_view = img_dst_view, @@ -824,14 +827,23 @@ void VK_RenderEndRTX( VkCommandBuffer cmdbuf, VkImageView img_dst_view, VkImage Matrix4x4_ToArrayFloatGL(view_inv, (float*)ubo_matrices[1]); } - VK_RaySceneEnd(&args); + VK_RayFrameEnd(&args); } } qboolean VK_RenderModelInit( vk_render_model_t *model) { if (vk_core.rtx) { - PRINT_NOT_IMPLEMENTED(); - return false; + // TODO runtime rtx switch: ??? + const vk_buffer_alloc_t *vertex_buffer = getBufferFromHandle( model->vertex_buffer ); + const vk_buffer_alloc_t *index_buffer = model->index_buffer != InvalidHandle ? getBufferFromHandle( model->index_buffer ) : NULL; + const vk_ray_model_init_t args = { + .buffer = g_render.buffer.buffer, + .index_offset = index_buffer ? index_buffer->buffer_offset_in_units : UINT32_MAX, + .vertex_offset = vertex_buffer->buffer_offset_in_units, + .model = model, + }; + model->rtx.blas = VK_NULL_HANDLE; + return VK_RayModelInit(args); } // TODO pre-bake optimal draws @@ -839,14 +851,17 @@ qboolean VK_RenderModelInit( vk_render_model_t *model) { } void VK_RenderModelDestroy( vk_render_model_t* model ) { - (void)model; - if (vk_core.rtx) { - PRINT_NOT_IMPLEMENTED(); + VK_RayModelDestroy(model); } } void VK_RenderModelDraw( vk_render_model_t* model ) { + if (vk_core.rtx) { + VK_RayFrameAddModel(model, g_render_state.model); + return; + } + int current_texture = -1; int index_count = 0; int index_offset = -1; diff --git a/ref_vk/vk_render.h b/ref_vk/vk_render.h index 4d911d11..11ac1f9e 100644 --- a/ref_vk/vk_render.h +++ b/ref_vk/vk_render.h @@ -73,9 +73,10 @@ typedef struct { int texture; uint32_t element_count; uint32_t index_offset, vertex_offset; + uint32_t vertex_count; } vk_render_geometry_t; -typedef struct { +typedef struct vk_render_model_s { const char *debug_name; int render_mode; int num_geometries; @@ -88,7 +89,7 @@ typedef struct { //qboolean dynamic; // whether this model will require data reupload struct { - void *blas; // FIXME + VkAccelerationStructureKHR blas; } rtx; } vk_render_model_t; diff --git a/ref_vk/vk_rtx.c b/ref_vk/vk_rtx.c index ca2cfa63..d2f2650c 100644 --- a/ref_vk/vk_rtx.c +++ b/ref_vk/vk_rtx.c @@ -54,11 +54,6 @@ typedef struct { } vk_lighttexture_data_t; typedef struct { - //int lightmap, texture; - //int render_mode; - //uint32_t element_count; - //uint32_t index_offset, vertex_offset; - //VkBuffer buffer; matrix3x4 transform_row; VkAccelerationStructureKHR accel; } vk_ray_model_t; @@ -86,21 +81,27 @@ static struct { // TODO this should really be a single uniform buffer for matrices and light data vk_buffer_t lighttextures_buffer; - vk_ray_model_t models[MAX_ACCELS]; VkAccelerationStructureKHR tlas; + // Data that is alive longer than one frame, usually within one map + struct { + uint32_t buffer_offset; + } map; + + // Per-frame data that is accumulated between RayFrameBegin and End calls + struct { + int num_models; + int num_lighttextures; + vk_ray_model_t models[MAX_ACCELS]; + uint32_t scratch_offset; // for building dynamic blases + } frame; + unsigned frame_number; vk_image_t frames[2]; qboolean reload_pipeline; } g_rtx; -static struct { - int num_models; - int num_lighttextures; - uint32_t scratch_offset, buffer_offset; -} g_rtx_scene; - static VkDeviceAddress getBufferDeviceAddress(VkBuffer buffer) { const VkBufferDeviceAddressInfo bdai = {.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, .buffer = buffer}; return vkGetBufferDeviceAddress(vk_core.device, &bdai); @@ -114,102 +115,104 @@ static VkDeviceAddress getASAddress(VkAccelerationStructureKHR as) { return vkGetAccelerationStructureDeviceAddressKHR(vk_core.device, &asdai); } -static VkAccelerationStructureKHR createAndBuildAccelerationStructure(VkCommandBuffer cmdbuf, const VkAccelerationStructureGeometryKHR *geoms, const uint32_t *max_prim_counts, const VkAccelerationStructureBuildRangeInfoKHR **build_ranges, uint32_t n_geoms, VkAccelerationStructureTypeKHR type) { - VkAccelerationStructureKHR accel; +typedef struct { + VkAccelerationStructureKHR *accel; + const VkAccelerationStructureGeometryKHR *geoms; + const uint32_t *max_prim_counts; + const VkAccelerationStructureBuildRangeInfoKHR **build_ranges; + uint32_t n_geoms; + VkAccelerationStructureTypeKHR type; + qboolean dynamic; +} as_build_args_t; +static qboolean createOrUpdateAccelerationStructure(VkCommandBuffer cmdbuf, const as_build_args_t *args) { + const qboolean is_update = *args->accel != VK_NULL_HANDLE; VkAccelerationStructureBuildGeometryInfoKHR build_info = { .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR, - .type = type, - .flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR | VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR, - .mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR, - .geometryCount = n_geoms, - .pGeometries = geoms, + .type = args->type, + .flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR | ( args->dynamic ? VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR : 0), + .mode = is_update ? VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR : VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR, + .geometryCount = args->n_geoms, + .pGeometries = args->geoms, + .srcAccelerationStructure = *args->accel, }; VkAccelerationStructureBuildSizesInfoKHR build_size = { .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR}; - VkAccelerationStructureCreateInfoKHR asci = { - .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR, - .buffer = g_rtx.accels_buffer.buffer, - .offset = g_rtx_scene.buffer_offset, - .type = type, - }; + uint32_t scratch_buffer_size = 0; vkGetAccelerationStructureBuildSizesKHR( - vk_core.device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &build_info, max_prim_counts, &build_size); + vk_core.device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &build_info, args->max_prim_counts, &build_size); + + scratch_buffer_size = is_update ? build_size.updateScratchSize : build_size.buildScratchSize; if (0) { uint32_t max_prims = 0; - for (int i = 0; i < n_geoms; ++i) - max_prims += max_prim_counts[i]; + for (int i = 0; i < args->n_geoms; ++i) + max_prims += args->max_prim_counts[i]; gEngine.Con_Reportf( - "AS max_prims=%u, n_geoms=%u, build size: %d, scratch size: %d\n", max_prims, n_geoms, build_size.accelerationStructureSize, build_size.buildScratchSize); + "AS max_prims=%u, n_geoms=%u, build size: %d, scratch size: %d\n", max_prims, args->n_geoms, build_size.accelerationStructureSize, build_size.buildScratchSize); } - if (MAX_SCRATCH_BUFFER - g_rtx_scene.scratch_offset < build_size.buildScratchSize) { + if (MAX_SCRATCH_BUFFER - g_rtx.frame.scratch_offset < scratch_buffer_size) { gEngine.Con_Printf(S_ERROR "Scratch buffer overflow: left %u bytes, but need %u\n", - MAX_SCRATCH_BUFFER - g_rtx_scene.scratch_offset, - build_size.buildScratchSize); - return VK_NULL_HANDLE; + MAX_SCRATCH_BUFFER - g_rtx.frame.scratch_offset, + scratch_buffer_size); + return false; } - if (MAX_ACCELS_BUFFER - g_rtx_scene.buffer_offset < build_size.accelerationStructureSize) { - gEngine.Con_Printf(S_ERROR "Accels buffer overflow: left %u bytes, but need %u\n", - MAX_ACCELS_BUFFER - g_rtx_scene.buffer_offset, - build_size.accelerationStructureSize); - return VK_NULL_HANDLE; + if (*args->accel == VK_NULL_HANDLE) { + VkAccelerationStructureCreateInfoKHR asci = { + .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR, + .buffer = g_rtx.accels_buffer.buffer, + .offset = g_rtx.map.buffer_offset, + .type = args->type, + .size = build_size.accelerationStructureSize, + }; + + if (MAX_ACCELS_BUFFER - g_rtx.map.buffer_offset < build_size.accelerationStructureSize) { + gEngine.Con_Printf(S_ERROR "Accels buffer overflow: left %u bytes, but need %u\n", + MAX_ACCELS_BUFFER - g_rtx.map.buffer_offset, + build_size.accelerationStructureSize); + return false; + } + + XVK_CHECK(vkCreateAccelerationStructureKHR(vk_core.device, &asci, NULL, args->accel)); + + g_rtx.map.buffer_offset += build_size.accelerationStructureSize; + g_rtx.map.buffer_offset = (g_rtx.map.buffer_offset + 255) & ~255; // Buffer must be aligned to 256 according to spec } - asci.size = build_size.accelerationStructureSize; - XVK_CHECK(vkCreateAccelerationStructureKHR(vk_core.device, &asci, NULL, &accel)); + build_info.dstAccelerationStructure = *args->accel; + build_info.scratchData.deviceAddress = g_rtx.scratch_buffer_addr + g_rtx.frame.scratch_offset; + g_rtx.frame.scratch_offset += scratch_buffer_size; - // TODO this function has weird semantics: it allocates data in buffers, but doesn't allocate the AS itself - g_rtx_scene.buffer_offset += build_size.accelerationStructureSize; - g_rtx_scene.buffer_offset = (g_rtx_scene.buffer_offset + 255) & ~255; // Buffer must be aligned to 256 according to spec - - build_info.dstAccelerationStructure = accel; - build_info.scratchData.deviceAddress = g_rtx.scratch_buffer_addr + g_rtx_scene.scratch_offset; - g_rtx_scene.scratch_offset += build_size.buildScratchSize; - - vkCmdBuildAccelerationStructuresKHR(cmdbuf, 1, &build_info, build_ranges); - return accel; + vkCmdBuildAccelerationStructuresKHR(cmdbuf, 1, &build_info, args->build_ranges); + return true; } -static void cleanupASFIXME(void) -{ - // FIXME we really should not do this; cache ASs per model - for (int i = 0; i < g_rtx_scene.num_models; ++i) { - if (g_rtx.models[i].accel != VK_NULL_HANDLE) - vkDestroyAccelerationStructureKHR(vk_core.device, g_rtx.models[i].accel, NULL); - } - if (g_rtx.tlas != VK_NULL_HANDLE) - vkDestroyAccelerationStructureKHR(vk_core.device, g_rtx.tlas, NULL); +void VK_RayNewMap( void ) { + ASSERT(vk_core.rtx); - g_rtx_scene.num_models = 0; - g_rtx_scene.num_lighttextures = 0; + g_rtx.map.buffer_offset = 0; } -void VK_RaySceneBegin( void ) +void VK_RayFrameBegin( void ) { ASSERT(vk_core.rtx); - // FIXME this buffer might have objects that live longer - g_rtx_scene.buffer_offset = 0; - g_rtx_scene.scratch_offset = 0; - - cleanupASFIXME(); + g_rtx.frame.scratch_offset = 0; + g_rtx.frame.num_models = 0; + g_rtx.frame.num_lighttextures = 0; } -/* -static vk_ray_model_t *getModelByHandle(vk_ray_model_handle_t handle) +void VK_RayFrameAddModelDynamic( VkCommandBuffer cmdbuf, const vk_ray_model_dynamic_t *dynamic) { -} -*/ + PRINT_NOT_IMPLEMENTED(); -void VK_RaySceneAddModelDynamic( VkCommandBuffer cmdbuf, const vk_ray_model_dynamic_t *dynamic) -{ +#if 0 vk_ray_model_t* model = g_rtx.models + g_rtx_scene.num_models; ASSERT(g_rtx_scene.num_models <= ARRAYSIZE(g_rtx.models)); @@ -246,7 +249,7 @@ void VK_RaySceneAddModelDynamic( VkCommandBuffer cmdbuf, const vk_ray_model_dyna }; const VkAccelerationStructureBuildRangeInfoKHR* build_ranges[ARRAYSIZE(geom)] = { &build_range_tri }; - model->accel = createAndBuildAccelerationStructure(cmdbuf, + model->accel = createOrUpdateAccelerationStructure(cmdbuf, geom, max_prim_counts, build_ranges, ARRAYSIZE(geom), VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR); if (!model->accel) { @@ -287,6 +290,7 @@ void VK_RaySceneAddModelDynamic( VkCommandBuffer cmdbuf, const vk_ray_model_dyna g_rtx_scene.num_models++; } +#endif } static void createPipeline( void ) @@ -300,11 +304,11 @@ static void createPipeline( void ) ASSERT(g_rtx.pipeline); } -void VK_RaySceneEnd(const vk_ray_scene_render_args_t* args) +void VK_RayFrameEnd(const vk_ray_frame_render_args_t* args) { const VkCommandBuffer cmdbuf = args->cmdbuf; - const vk_image_t* frame_src = g_rtx.frames + ((g_rtx.frame_number+1)%2); - const vk_image_t* frame_dst = g_rtx.frames + (g_rtx.frame_number%2); + const vk_image_t* frame_src = g_rtx.frames + ((g_rtx.frame_number + 1) % 2); + const vk_image_t* frame_dst = g_rtx.frames + (g_rtx.frame_number % 2); ASSERT(vk_core.rtx); // ubo should contain two matrices @@ -323,9 +327,9 @@ void VK_RaySceneEnd(const vk_ray_scene_render_args_t* args) // Upload all blas instances references to GPU mem { - VkAccelerationStructureInstanceKHR *inst = g_rtx.tlas_geom_buffer.mapped; - for (int i = 0; i < g_rtx_scene.num_models; ++i) { - const vk_ray_model_t * const model = g_rtx.models + i; + VkAccelerationStructureInstanceKHR* inst = g_rtx.tlas_geom_buffer.mapped; + for (int i = 0; i < g_rtx.frame.num_models; ++i) { + const vk_ray_model_t* const model = g_rtx.frame.models + i; ASSERT(model->accel != VK_NULL_HANDLE); inst[i] = (VkAccelerationStructureInstanceKHR){ .instanceCustomIndex = i, @@ -348,7 +352,7 @@ void VK_RaySceneEnd(const vk_ray_scene_render_args_t* args) .buffer = g_rtx.accels_buffer.buffer, .offset = 0, .size = VK_WHOLE_SIZE, - }}; + } }; vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, @@ -356,6 +360,8 @@ void VK_RaySceneEnd(const vk_ray_scene_render_args_t* args) } // 2. Create TLAS + /* FIXME once*/ + if (!g_rtx.tlas && g_rtx.frame.num_models > 0) { const VkAccelerationStructureGeometryKHR tl_geom[] = { { @@ -370,189 +376,204 @@ void VK_RaySceneEnd(const vk_ray_scene_render_args_t* args) }, }, }; - - const uint32_t tl_max_prim_counts[ARRAYSIZE(tl_geom)] = {g_rtx_scene.num_models}; + const uint32_t tl_max_prim_counts[ARRAYSIZE(tl_geom)] = { MAX_ACCELS }; const VkAccelerationStructureBuildRangeInfoKHR tl_build_range = { - .primitiveCount = g_rtx_scene.num_models, + .primitiveCount = g_rtx.frame.num_models, }; - const VkAccelerationStructureBuildRangeInfoKHR *tl_build_ranges[] = {&tl_build_range}; - g_rtx.tlas = createAndBuildAccelerationStructure(cmdbuf, - tl_geom, tl_max_prim_counts, tl_build_ranges, ARRAYSIZE(tl_geom), VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR); + const VkAccelerationStructureBuildRangeInfoKHR* tl_build_ranges[] = { &tl_build_range }; + const as_build_args_t asrgs = { + .geoms = tl_geom, + .max_prim_counts = tl_max_prim_counts, + .build_ranges = tl_build_ranges, + .n_geoms = ARRAYSIZE(tl_geom), + .type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, + .dynamic = true, + .accel = &g_rtx.tlas, + }; + if (!createOrUpdateAccelerationStructure(cmdbuf, &asrgs)) { + gEngine.Host_Error("Could not create/update TLAS\n"); + return; + } } - // 3. Update descriptor sets (bind dest image, tlas, projection matrix) + if (g_rtx.tlas != VK_NULL_HANDLE) { - const VkDescriptorImageInfo dii_dst = { - .sampler = VK_NULL_HANDLE, - .imageView = frame_dst->view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - }; - const VkDescriptorImageInfo dii_src = { - .sampler = VK_NULL_HANDLE, - .imageView = frame_src->view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - }; - const VkDescriptorBufferInfo dbi_ubo = { - .buffer = args->ubo.buffer, - .offset = args->ubo.offset, - .range = args->ubo.size, - }; - const VkDescriptorBufferInfo dbi_kusochki = { - .buffer = g_rtx.kusochki_buffer.buffer, - .offset = 0, - .range = VK_WHOLE_SIZE, // TODO fails validation when empty g_rtx_scene.num_models * sizeof(vk_kusok_data_t), - }; - const VkDescriptorBufferInfo dbi_indices = { - .buffer = args->geometry_data.buffer, - .offset = 0, - .range = VK_WHOLE_SIZE, // TODO fails validation when empty args->geometry_data.size, - }; - const VkDescriptorBufferInfo dbi_vertices = { - .buffer = args->geometry_data.buffer, - .offset = 0, - .range = VK_WHOLE_SIZE, // TODO fails validation when empty args->geometry_data.size, - }; - const VkWriteDescriptorSetAccelerationStructureKHR wdsas = { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR, - .accelerationStructureCount = 1, - .pAccelerationStructures = &g_rtx.tlas, - }; - const VkDescriptorBufferInfo dbi_dlights = { - .buffer = args->dlights.buffer, - .offset = args->dlights.offset, - .range = args->dlights.size, - }; - const VkDescriptorBufferInfo dbi_lighttextures = { - .buffer = g_rtx.lighttextures_buffer.buffer, - .offset = 0, - .range = VK_WHOLE_SIZE, - }; - const VkWriteDescriptorSet wds[] = { - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .dstSet = g_rtx.desc_set, - .dstBinding = 0, - .dstArrayElement = 0, - .pImageInfo = &dii_dst, - }, - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, - .dstSet = g_rtx.desc_set, - .dstBinding = 1, - .dstArrayElement = 0, - .pNext = &wdsas, - }, - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .dstSet = g_rtx.desc_set, - .dstBinding = 2, - .dstArrayElement = 0, - .pBufferInfo = &dbi_ubo, - }, - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .dstSet = g_rtx.desc_set, - .dstBinding = 3, - .dstArrayElement = 0, - .pBufferInfo = &dbi_kusochki, - }, - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .dstSet = g_rtx.desc_set, - .dstBinding = 4, - .dstArrayElement = 0, - .pBufferInfo = &dbi_indices, - }, - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .dstSet = g_rtx.desc_set, - .dstBinding = 5, - .dstArrayElement = 0, - .pBufferInfo = &dbi_vertices, - }, - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .dstSet = g_rtx.desc_set, - .dstBinding = 6, - .dstArrayElement = 0, - .pBufferInfo = &dbi_dlights, - }, - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .dstSet = g_rtx.desc_set, - .dstBinding = 7, - .dstArrayElement = 0, - .pBufferInfo = &dbi_lighttextures, - }, - { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .dstSet = g_rtx.desc_set, - .dstBinding = 8, - .dstArrayElement = 0, - .pImageInfo = &dii_src, - }, - }; + // 3. Update descriptor sets (bind dest image, tlas, projection matrix) + { + const VkDescriptorImageInfo dii_dst = { + .sampler = VK_NULL_HANDLE, + .imageView = frame_dst->view, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + const VkDescriptorImageInfo dii_src = { + .sampler = VK_NULL_HANDLE, + .imageView = frame_src->view, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + const VkDescriptorBufferInfo dbi_ubo = { + .buffer = args->ubo.buffer, + .offset = args->ubo.offset, + .range = args->ubo.size, + }; + const VkDescriptorBufferInfo dbi_kusochki = { + .buffer = g_rtx.kusochki_buffer.buffer, + .offset = 0, + .range = VK_WHOLE_SIZE, // TODO fails validation when empty g_rtx_scene.num_models * sizeof(vk_kusok_data_t), + }; + const VkDescriptorBufferInfo dbi_indices = { + .buffer = args->geometry_data.buffer, + .offset = 0, + .range = VK_WHOLE_SIZE, // TODO fails validation when empty args->geometry_data.size, + }; + const VkDescriptorBufferInfo dbi_vertices = { + .buffer = args->geometry_data.buffer, + .offset = 0, + .range = VK_WHOLE_SIZE, // TODO fails validation when empty args->geometry_data.size, + }; + const VkWriteDescriptorSetAccelerationStructureKHR wdsas = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR, + .accelerationStructureCount = 1, + .pAccelerationStructures = &g_rtx.tlas, + }; + const VkDescriptorBufferInfo dbi_dlights = { + .buffer = args->dlights.buffer, + .offset = args->dlights.offset, + .range = args->dlights.size, + }; + const VkDescriptorBufferInfo dbi_lighttextures = { + .buffer = g_rtx.lighttextures_buffer.buffer, + .offset = 0, + .range = VK_WHOLE_SIZE, + }; + const VkWriteDescriptorSet wds[] = { + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .dstSet = g_rtx.desc_set, + .dstBinding = 0, + .dstArrayElement = 0, + .pImageInfo = &dii_dst, + }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, + .dstSet = g_rtx.desc_set, + .dstBinding = 1, + .dstArrayElement = 0, + .pNext = &wdsas, + }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .dstSet = g_rtx.desc_set, + .dstBinding = 2, + .dstArrayElement = 0, + .pBufferInfo = &dbi_ubo, + }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .dstSet = g_rtx.desc_set, + .dstBinding = 3, + .dstArrayElement = 0, + .pBufferInfo = &dbi_kusochki, + }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .dstSet = g_rtx.desc_set, + .dstBinding = 4, + .dstArrayElement = 0, + .pBufferInfo = &dbi_indices, + }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .dstSet = g_rtx.desc_set, + .dstBinding = 5, + .dstArrayElement = 0, + .pBufferInfo = &dbi_vertices, + }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .dstSet = g_rtx.desc_set, + .dstBinding = 6, + .dstArrayElement = 0, + .pBufferInfo = &dbi_dlights, + }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .dstSet = g_rtx.desc_set, + .dstBinding = 7, + .dstArrayElement = 0, + .pBufferInfo = &dbi_lighttextures, + }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .dstSet = g_rtx.desc_set, + .dstBinding = 8, + .dstArrayElement = 0, + .pImageInfo = &dii_src, + }, + }; - vkUpdateDescriptorSets(vk_core.device, ARRAYSIZE(wds), wds, 0, NULL); + vkUpdateDescriptorSets(vk_core.device, ARRAYSIZE(wds), wds, 0, NULL); + } } - // 4. Barrier for TLAS build and dest image layout transfer - { - VkBufferMemoryBarrier bmb[] = { { - .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, - .srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .buffer = g_rtx.accels_buffer.buffer, - .offset = 0, - .size = VK_WHOLE_SIZE, - }}; - VkImageMemoryBarrier image_barrier[] = { { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .image = frame_dst->image, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .subresourceRange = (VkImageSubresourceRange) { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }} }; - vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, ARRAYSIZE(bmb), bmb, ARRAYSIZE(image_barrier), image_barrier); - } + // 4. Barrier for TLAS build and dest image layout transfer + { + VkBufferMemoryBarrier bmb[] = { { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + .buffer = g_rtx.accels_buffer.buffer, + .offset = 0, + .size = VK_WHOLE_SIZE, + } }; + VkImageMemoryBarrier image_barrier[] = { { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .image = frame_dst->image, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }} }; + vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, + 0, NULL, ARRAYSIZE(bmb), bmb, ARRAYSIZE(image_barrier), image_barrier); + } - // 4. dispatch compute - vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_COMPUTE, g_rtx.pipeline); - { - vk_rtx_push_constants_t push_constants = { - .t = gpGlobals->realtime, - .bounces = vk_rtx_bounces->value, - }; - vkCmdPushConstants(cmdbuf, g_rtx.pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(push_constants), &push_constants); + if (g_rtx.tlas) { + // 4. dispatch compute + vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_COMPUTE, g_rtx.pipeline); + { + vk_rtx_push_constants_t push_constants = { + .t = gpGlobals->realtime, + .bounces = vk_rtx_bounces->value, + }; + vkCmdPushConstants(cmdbuf, g_rtx.pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(push_constants), &push_constants); + } + vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_COMPUTE, g_rtx.pipeline_layout, 0, 1, &g_rtx.desc_set, 0, NULL); + vkCmdDispatch(cmdbuf, (FRAME_WIDTH + WG_W - 1) / WG_W, (FRAME_HEIGHT + WG_H - 1) / WG_H, 1); } - vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_COMPUTE, g_rtx.pipeline_layout, 0, 1, &g_rtx.desc_set, 0, NULL); - vkCmdDispatch(cmdbuf, (FRAME_WIDTH+WG_W-1)/WG_W, (FRAME_HEIGHT+WG_H-1)/WG_H, 1); // Blit RTX frame onto swapchain image { @@ -820,8 +841,8 @@ void VK_RayShutdown( void ) vkDestroyPipelineLayout(vk_core.device, g_rtx.pipeline_layout, NULL); vkDestroyDescriptorSetLayout(vk_core.device, g_rtx.desc_layout, NULL); - // TODO dealloc all ASes - cleanupASFIXME(); + if (g_rtx.tlas != VK_NULL_HANDLE) + vkDestroyAccelerationStructureKHR(vk_core.device, g_rtx.tlas, NULL); destroyBuffer(&g_rtx.scratch_buffer); destroyBuffer(&g_rtx.accels_buffer); @@ -830,3 +851,140 @@ void VK_RayShutdown( void ) destroyBuffer(&g_rtx.lighttextures_buffer); } +qboolean VK_RayModelInit( vk_ray_model_init_t args ) { + VkAccelerationStructureGeometryKHR *geoms = Mem_Malloc(vk_core.pool, args.model->num_geometries * sizeof(*geoms)); + uint32_t *geom_max_prim_counts = Mem_Malloc(vk_core.pool, args.model->num_geometries * sizeof(*geom_max_prim_counts)); + VkAccelerationStructureBuildRangeInfoKHR *geom_build_ranges = Mem_Malloc(vk_core.pool, args.model->num_geometries * sizeof(*geom_build_ranges)); + VkAccelerationStructureBuildRangeInfoKHR **geom_build_ranges_ptr = Mem_Malloc(vk_core.pool, args.model->num_geometries * sizeof(*geom_build_ranges)); + const VkDeviceAddress buffer_addr = getBufferDeviceAddress(args.buffer); + qboolean result; + + ASSERT(vk_core.rtx); + + for (int i = 0; i < args.model->num_geometries; ++i) { + const vk_render_geometry_t *mg = args.model->geometries + i; + const uint32_t prim_count = mg->element_count / 3; + + geom_max_prim_counts[i] = prim_count; + geoms[i] = (VkAccelerationStructureGeometryKHR) + { + .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR, + .flags = VK_GEOMETRY_OPAQUE_BIT_KHR, + .geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR, + .geometry.triangles = + (VkAccelerationStructureGeometryTrianglesDataKHR){ + .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR, + .indexType = args.index_offset == UINT32_MAX ? VK_INDEX_TYPE_NONE_KHR : VK_INDEX_TYPE_UINT16, + .maxVertex = mg->vertex_count, + .vertexFormat = VK_FORMAT_R32G32B32_SFLOAT, + .vertexStride = sizeof(vk_vertex_t), + .vertexData.deviceAddress = buffer_addr + (args.vertex_offset + mg->vertex_offset) * sizeof(vk_vertex_t), + .indexData.deviceAddress = buffer_addr + (args.index_offset + mg->index_offset) * sizeof(uint16_t), + }, + }; + + geom_build_ranges[i] = (VkAccelerationStructureBuildRangeInfoKHR) { + .primitiveCount = prim_count, + }; + geom_build_ranges_ptr[i] = geom_build_ranges + i; + + // Store geometry references in kusochki + // FIXME + #if 0 + { + vk_kusok_data_t *kusok = (vk_kusok_data_t*)(g_rtx.kusochki_buffer.mapped) + g_rtx_scene.num_models; + kusok->vertex_offset = dynamic->vertex_offset; + kusok->index_offset = dynamic->index_offset; + ASSERT(dynamic->element_count % 3 == 0); + kusok->triangles = dynamic->element_count / 3; + + ASSERT(dynamic->texture_id < MAX_TEXTURES); + if (dynamic->texture_id >= 0 && g_emissive_texture_table[dynamic->texture_id].set) { + VectorCopy(g_emissive_texture_table[dynamic->texture_id].emissive, kusok->emissive); + } else { + kusok->emissive[0] = dynamic->emissive.r; + kusok->emissive[1] = dynamic->emissive.g; + kusok->emissive[2] = dynamic->emissive.b; + } + + if (kusok->emissive[0] > 0 || kusok->emissive[1] > 0 || kusok->emissive[2] > 0) { + if (g_rtx_scene.num_lighttextures < MAX_LIGHT_TEXTURES) { + vk_lighttexture_data_t *ltd = (vk_lighttexture_data_t*)g_rtx.lighttextures_buffer.mapped; + ltd->lighttexture[g_rtx_scene.num_lighttextures].kusok_index = g_rtx_scene.num_models; + g_rtx_scene.num_lighttextures++; + ltd->num_lighttextures = g_rtx_scene.num_lighttextures; + } else { + gEngine.Con_Printf(S_ERROR "Ran out of light textures space"); + } + } + } + #endif + } + + { + const as_build_args_t asrgs = { + .geoms = geoms, + .max_prim_counts = geom_max_prim_counts, + .build_ranges = geom_build_ranges_ptr, + .n_geoms = args.model->num_geometries, + .type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, + .dynamic = false, //model->dynamic, + .accel = &args.model->rtx.blas, + }; + + // TODO batch building multiple blases together + const VkCommandBufferBeginInfo beginfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + XVK_CHECK(vkBeginCommandBuffer(vk_core.cb, &beginfo)); + + result = createOrUpdateAccelerationStructure(vk_core.cb, &asrgs); + + XVK_CHECK(vkEndCommandBuffer(vk_core.cb)); + } + + { + const VkSubmitInfo subinfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &vk_core.cb, + }; + XVK_CHECK(vkQueueSubmit(vk_core.queue, 1, &subinfo, VK_NULL_HANDLE)); + XVK_CHECK(vkQueueWaitIdle(vk_core.queue)); + g_rtx.frame.scratch_offset = 0; + } + + Mem_Free(geom_build_ranges); + Mem_Free(geom_max_prim_counts); + Mem_Free(geoms); + + return result; +} + +void VK_RayModelDestroy( struct vk_render_model_s *model ) { + ASSERT(vk_core.rtx); + if (model->rtx.blas != VK_NULL_HANDLE) { + vkDestroyAccelerationStructureKHR(vk_core.device, model->rtx.blas, NULL); + model->rtx.blas = VK_NULL_HANDLE; + } +} + +void VK_RayFrameAddModel( const struct vk_render_model_s *model, const matrix3x4 *transform_row ) { + ASSERT(vk_core.rtx); + + ASSERT(g_rtx.frame.num_models <= ARRAYSIZE(g_rtx.frame.models)); + + if (g_rtx.frame.num_models == ARRAYSIZE(g_rtx.frame.models)) { + gEngine.Con_Printf(S_ERROR "Ran out of AccelerationStructure slots\n"); + return; + } + + { + vk_ray_model_t* ray_model = g_rtx.frame.models + g_rtx.frame.num_models; + ASSERT(model->rtx.blas != VK_NULL_HANDLE); + ray_model->accel = model->rtx.blas; + memcpy(ray_model->transform_row, *transform_row, sizeof(ray_model->transform_row)); + g_rtx.frame.num_models++; + } +} diff --git a/ref_vk/vk_rtx.h b/ref_vk/vk_rtx.h index 37706340..378ca762 100644 --- a/ref_vk/vk_rtx.h +++ b/ref_vk/vk_rtx.h @@ -2,6 +2,17 @@ #include "vk_core.h" +struct vk_render_model_s; + +typedef struct { + struct vk_render_model_s *model; + VkBuffer buffer; + uint32_t vertex_offset, index_offset; +} vk_ray_model_init_t; + +qboolean VK_RayModelInit( vk_ray_model_init_t model_init); +void VK_RayModelDestroy( struct vk_render_model_s *model ); + typedef struct { //int lightmap, texture; //int render_mode; @@ -14,8 +25,9 @@ typedef struct { struct { float r,g,b; } emissive; } vk_ray_model_dynamic_t; -void VK_RaySceneBegin( void ); -void VK_RaySceneAddModelDynamic(VkCommandBuffer cmdbuf, const vk_ray_model_dynamic_t* model); +void VK_RayFrameBegin( void ); +void VK_RayFrameAddModel( const struct vk_render_model_s *model, const matrix3x4 *transform_row ); +void VK_RayFrameAddModelDynamic(VkCommandBuffer cmdbuf, const vk_ray_model_dynamic_t* model); typedef struct { VkCommandBuffer cmdbuf; @@ -44,8 +56,10 @@ typedef struct { VkBuffer buffer; // must be the same as in vk_ray_model_create_t TODO: validate or make impossible to specify incorrectly uint32_t size; } geometry_data; -} vk_ray_scene_render_args_t; -void VK_RaySceneEnd(const vk_ray_scene_render_args_t* args); +} vk_ray_frame_render_args_t; +void VK_RayFrameEnd(const vk_ray_frame_render_args_t* args); + +void VK_RayNewMap( void ); qboolean VK_RayInit( void ); void VK_RayShutdown( void ); diff --git a/ref_vk/vk_scene.c b/ref_vk/vk_scene.c index 1b416dfe..c300759a 100644 --- a/ref_vk/vk_scene.c +++ b/ref_vk/vk_scene.c @@ -85,7 +85,15 @@ void R_NewMap( void ) { const int num_models = gEngine.EngineGetParm( PARM_NUMMODELS, 0 ); - gEngine.Con_Reportf( "R_NewMap\n" ); + // Existence of cache.data for the world means that we've already have loaded this map + // and this R_NewMap call is from within loading of a saved game. + const qboolean is_save_load = !!gEngine.pfnGetModelByIndex( 1 )->cache.data; + + gEngine.Con_Reportf( "R_NewMap, loading save: %d\n", is_save_load ); + + // Skip clearing already loaded data if the map hasn't changed. + if (is_save_load) + return; VK_ClearLightmap();