xash3d-fwgs/ref_vk/vk_rtx.c

477 lines
14 KiB
C
Raw Normal View History

2021-02-27 22:43:49 +01:00
#include "vk_rtx.h"
2022-02-01 05:50:39 +01:00
#include "ray_resources.h"
#include "vk_ray_accel.h"
2021-02-27 22:43:49 +01:00
#include "vk_buffer.h"
2022-10-16 00:09:45 +02:00
#include "vk_common.h"
#include "vk_core.h"
#include "vk_cvar.h"
2022-10-16 00:09:45 +02:00
#include "vk_descriptor.h"
#include "vk_light.h"
#include "vk_math.h"
2022-10-16 00:09:45 +02:00
#include "vk_meatpipe.h"
#include "vk_pipeline.h"
#include "vk_ray_internal.h"
#include "vk_staging.h"
#include "vk_textures.h"
2021-02-27 22:43:49 +01:00
2022-07-15 09:35:19 +02:00
#include "alolcator.h"
2021-02-27 22:43:49 +01:00
#include "eiface.h"
#include "xash3d_mathlib.h"
2021-02-27 22:43:49 +01:00
2021-03-21 00:21:26 +01:00
#include <string.h>
#define MAX_FRAMES_IN_FLIGHT 2
2021-02-27 22:43:49 +01:00
// TODO settings/realtime modifiable/adaptive
#if 1
#define FRAME_WIDTH 1280
#define FRAME_HEIGHT 720
#elif 0
#define FRAME_WIDTH 2560
#define FRAME_HEIGHT 1440
#else
#define FRAME_WIDTH 1920
#define FRAME_HEIGHT 1080
#endif
#define MAX_RESOURCES 32
2021-02-27 22:43:49 +01:00
static struct {
// Holds UniformBuffer data
vk_buffer_t uniform_buffer;
2022-01-18 06:12:14 +01:00
uint32_t uniform_unit_size;
// TODO with proper intra-cmdbuf sync we don't really need 2x images
unsigned frame_number;
vk_meatpipe_t *mainpipe;
vk_resource_p *mainpipe_resources;
struct {
char name[64];
vk_resource_t resource;
xvk_image_t image;
} res[MAX_RESOURCES];
2022-10-16 00:09:45 +02:00
qboolean reload_pipeline;
qboolean reload_lighting;
} g_rtx = {0};
2021-02-27 22:43:49 +01:00
static int findResource(const char *name) {
// Find the exact match if exists
// There might be gaps, so we need to check everything
for (int i = 0; i < MAX_RESOURCES; ++i) {
if (strcmp(g_rtx.res[i].name, name) == 0)
return i;
}
return -1;
}
static int getResourceSlotForName(const char *name) {
const int index = findResource(name);
if (index >= 0)
return index;
// Find first free slot
for (int i = 0; i < MAX_RESOURCES; ++i) {
if (!g_rtx.res[i].name[0])
return i;
}
return -1;
}
2021-04-09 23:59:04 +02:00
void VK_RayNewMap( void ) {
RT_VkAccelNewMap();
RT_RayModel_Clear();
2021-02-27 22:43:49 +01:00
}
2021-04-09 23:59:04 +02:00
void VK_RayFrameBegin( void )
{
ASSERT(vk_core.rtx);
RT_VkAccelFrameBegin();
if (g_ray_model_state.freeze_models)
return;
XVK_RayModel_ClearForNextFrame();
2021-05-03 20:17:01 +02:00
// TODO: move all lighting update to scene?
if (g_rtx.reload_lighting) {
g_rtx.reload_lighting = false;
// FIXME temporarily not supported VK_LightsLoadMapStaticLights();
}
// TODO shouldn't we do this in freeze models mode anyway?
2022-07-30 20:37:06 +02:00
RT_LightsFrameBegin();
}
static void prepareUniformBuffer( const vk_ray_frame_render_args_t *args, int frame_index, float fov_angle_y ) {
2022-01-18 06:12:14 +01:00
struct UniformBuffer *ubo = (struct UniformBuffer*)((char*)g_rtx.uniform_buffer.mapped + frame_index * g_rtx.uniform_unit_size);
matrix4x4 proj_inv, view_inv;
Matrix4x4_Invert_Full(proj_inv, *args->projection);
Matrix4x4_ToArrayFloatGL(proj_inv, (float*)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, *args->view);
Matrix4x4_ToArrayFloatGL(view_inv, (float*)ubo->inv_view);
ubo->ray_cone_width = atanf((2.0f*tanf(DEG2RAD(fov_angle_y) * 0.5f)) / (float)FRAME_HEIGHT);
ubo->random_seed = (uint32_t)gEngine.COM_RandomLong(0, INT32_MAX);
}
typedef struct {
const vk_ray_frame_render_args_t* render_args;
int frame_index;
float fov_angle_y;
const vk_lights_bindings_t *light_bindings;
} perform_tracing_args_t;
static void performTracing(VkCommandBuffer cmdbuf, const perform_tracing_args_t* args) {
#if 0
R_VkResourceSetExternal("tlas", VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
(vk_descriptor_value_t){
.accel = (VkWriteDescriptorSetAccelerationStructureKHR) {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR,
.accelerationStructureCount = 1,
.pAccelerationStructures = &g_accel.tlas,
.pNext = NULL,
},
}, 1, (ray_resource_desc_t){ResourceUnknown, 0}, NULL
);
2022-02-01 05:40:41 +01:00
#define RES_SET_BUFFER(name, type_, source_, offset_, size_) \
R_VkResourceSetExternal(#name, type_, (vk_descriptor_value_t){ \
.buffer = (VkDescriptorBufferInfo) { \
.buffer = (source_), \
2022-02-01 05:40:41 +01:00
.offset = (offset_), \
.range = (size_), \
} \
}, 1, (ray_resource_desc_t){ResourceBuffer, 0}, NULL)
RES_SET_BUFFER(ubo, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, g_rtx.uniform_buffer.buffer, args->frame_index * g_rtx.uniform_unit_size, sizeof(struct UniformBuffer));
2022-02-01 05:40:41 +01:00
#define RES_SET_SBUFFER_FULL(name, source_) \
RES_SET_BUFFER(name, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, source_.buffer, 0, source_.size)
2022-02-01 05:40:41 +01:00
RES_SET_SBUFFER_FULL(kusochki, g_ray_model_state.kusochki_buffer);
RES_SET_SBUFFER_FULL(indices, args->render_args->geometry_data);
RES_SET_SBUFFER_FULL(vertices, args->render_args->geometry_data);
2022-02-03 06:47:40 +01:00
RES_SET_BUFFER(lights, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, args->light_bindings->buffer, args->light_bindings->metadata.offset, args->light_bindings->metadata.size);
RES_SET_BUFFER(light_clusters, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, args->light_bindings->buffer, args->light_bindings->grid.offset, args->light_bindings->grid.size);
#undef RES_SET_SBUFFER_FULL
#undef RES_SET_BUFFER
#endif
// Upload kusochki updates
{
const VkBufferMemoryBarrier bmb[] = { {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR,
.buffer = g_ray_model_state.kusochki_buffer.buffer,
.offset = 0,
.size = VK_WHOLE_SIZE,
} };
vkCmdPipelineBarrier(cmdbuf,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR | VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
0, 0, NULL, ARRAYSIZE(bmb), bmb, 0, NULL);
}
2022-01-19 07:21:14 +01:00
DEBUG_BEGIN(cmdbuf, "yay tracing");
RT_VkAccelPrepareTlas(cmdbuf);
prepareUniformBuffer(args->render_args, args->frame_index, args->fov_angle_y);
// 4. Barrier for TLAS build
{
const 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_accel.accels_buffer.buffer,
.offset = 0,
.size = VK_WHOLE_SIZE,
} };
vkCmdPipelineBarrier(cmdbuf,
VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR,
2022-02-01 05:40:41 +01:00
0, 0, NULL, ARRAYSIZE(bmb), bmb, 0, NULL);
}
2022-10-22 23:43:41 +02:00
{ // FIXME this should be done automatically inside meatpipe, TODO
//const uint32_t size = sizeof(struct Lights);
//const uint32_t size = sizeof(struct LightsMetadata); // + 8 * sizeof(uint32_t);
const VkBufferMemoryBarrier bmb[] = {{
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.buffer = args->light_bindings->buffer,
.offset = 0,
.size = VK_WHOLE_SIZE,
}};
vkCmdPipelineBarrier(cmdbuf,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR,
0, 0, NULL, ARRAYSIZE(bmb), bmb, 0, NULL);
}
// FIXME R_VkMeatpipePerform(&g_rtx.mainpipe, cmdbuf, args->frame_index, &res);
{
const r_vkimage_blit_args blit_args = {
.in_stage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
.src = {
// FIXME .image = args->current_frame->denoised.image,
.width = FRAME_WIDTH,
.height = FRAME_HEIGHT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
},
.dst = {
.image = args->render_args->dst.image,
.width = args->render_args->dst.width,
.height = args->render_args->dst.height,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.srcAccessMask = 0,
},
};
R_VkImageBlit( cmdbuf, &blit_args );
}
2022-01-19 07:21:14 +01:00
DEBUG_END(cmdbuf);
}
static void reloadMainpipe() {
vk_meatpipe_t *const newpipe = R_VkMeatpipeCreateFromFile("rt.meat");
if (!newpipe)
return;
const size_t newpipe_resources_size = sizeof(vk_resource_p) * newpipe->resources_count;
vk_resource_p *newpipe_resources = Mem_Calloc(vk_core.pool, newpipe_resources_size);
for (int i = 0; i < newpipe->resources_count; ++i) {
const vk_meatpipe_resource_t *mr = newpipe->resources + i;
gEngine.Con_Reportf("res %d/%d: %s descriptor=%u count=%d flags=[%c%c] image_format=%u\n",
i, newpipe->resources_count, mr->name, mr->descriptor_type, mr->count,
(mr->flags & MEATPIPE_RES_WRITE) ? 'W' : ' ',
(mr->flags & MEATPIPE_RES_CREATE) ? 'C' : ' ',
mr->image_format);
const qboolean create = !!(mr->flags & MEATPIPE_RES_CREATE);
const int index = create ? getResourceSlotForName(mr->name) : findResource(mr->name);
if (index < 0) {
gEngine.Con_Printf(S_ERROR "Couldn't find resource/slot for %s\n", mr->name);
goto fail;
}
// TODO create if creatable
if (create && g_rtx.res[index].image.image == VK_NULL_HANDLE) {
Q_strncpy(g_rtx.res[index].name, mr->name, sizeof(g_rtx.res[index].name));
const xvk_image_create_t create = {
.debug_name = mr->name,
.width = FRAME_WIDTH,
.height = FRAME_HEIGHT,
.mips = 1,
.layers = 1,
.format = mr->image_format,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_STORAGE_BIT,
.has_alpha = true,
.is_cubemap = false,
};
g_rtx.res[index].image = XVK_ImageCreate(&create);
}
g_rtx.mainpipe_resources[i] = &g_rtx.res[index].resource;
}
if (g_rtx.mainpipe) {
R_VkMeatpipeDestroy(g_rtx.mainpipe);
// FIXME also destroy all extra created resources/images
Mem_Free(g_rtx.mainpipe_resources);
}
g_rtx.mainpipe = newpipe;
fail:
if (newpipe_resources) {
for (int i = 0; i < newpipe->resources_count; ++i) {
// FIXME on error we'll leak images
}
Mem_Free(newpipe_resources);
}
R_VkMeatpipeDestroy(newpipe);
}
void VK_RayFrameEnd(const vk_ray_frame_render_args_t* args)
{
const VkCommandBuffer cmdbuf = args->cmdbuf;
// const xvk_ray_frame_images_t* current_frame = g_rtx.frames + (g_rtx.frame_number % 2);
ASSERT(vk_core.rtx);
// ubo should contain two matrices
// FIXME pass these matrices explicitly to let RTX module handle ubo itself
2022-07-30 20:37:06 +02:00
RT_LightsFrameEnd();
const vk_lights_bindings_t light_bindings = VK_LightsUpload(cmdbuf);
2022-07-30 20:37:06 +02:00
g_rtx.frame_number++;
// if (vk_core.debug)
// XVK_RayModel_Validate();
if (g_rtx.reload_pipeline) {
gEngine.Con_Printf(S_WARN "Reloading RTX shaders/pipelines\n");
XVK_CHECK(vkDeviceWaitIdle(vk_core.device));
reloadMainpipe();
g_rtx.reload_pipeline = false;
}
if (g_ray_model_state.frame.num_models == 0) {
const r_vkimage_blit_args blit_args = {
.in_stage = VK_PIPELINE_STAGE_TRANSFER_BIT,
.src = {
// FIXME .image = current_frame->denoised.image,
.width = FRAME_WIDTH,
.height = FRAME_HEIGHT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
},
.dst = {
2021-07-17 21:40:26 +02:00
.image = args->dst.image,
.width = args->dst.width,
.height = args->dst.height,
2021-07-17 21:40:26 +02:00
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.srcAccessMask = 0,
},
};
2021-07-17 21:40:26 +02:00
// FIMXE R_VkImageClear( cmdbuf, current_frame->denoised.image );
R_VkImageBlit( cmdbuf, &blit_args );
} else {
const perform_tracing_args_t trace_args = {
.render_args = args,
.frame_index = (g_rtx.frame_number % 2),
.fov_angle_y = args->fov_angle_y,
.light_bindings = &light_bindings,
};
performTracing( cmdbuf, &trace_args );
2021-11-07 19:07:01 +01:00
}
}
static void reloadPipeline( void ) {
g_rtx.reload_pipeline = true;
2021-02-27 22:43:49 +01:00
}
static void reloadLighting( void ) {
g_rtx.reload_lighting = true;
}
static void freezeModels( void ) {
g_ray_model_state.freeze_models = !g_ray_model_state.freeze_models;
}
2021-02-27 22:43:49 +01:00
qboolean VK_RayInit( void )
{
ASSERT(vk_core.rtx);
// TODO complain and cleanup on failure
if (!RT_VkAccelInit())
return false;
reloadMainpipe();
if (!g_rtx.mainpipe)
return false;
2022-10-16 00:09:45 +02:00
2022-01-18 06:12:14 +01:00
g_rtx.uniform_unit_size = ALIGN_UP(sizeof(struct UniformBuffer), vk_core.physical_device.properties.limits.minUniformBufferOffsetAlignment);
if (!VK_BufferCreate("ray uniform_buffer", &g_rtx.uniform_buffer, g_rtx.uniform_unit_size * MAX_FRAMES_IN_FLIGHT,
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT))
{
return false;
}
if (!VK_BufferCreate("ray kusochki_buffer", &g_ray_model_state.kusochki_buffer, sizeof(vk_kusok_data_t) * MAX_KUSOCHKI,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
// FIXME complain, handle
return false;
}
RT_RayModel_Clear();
2021-05-03 20:17:01 +02:00
2021-11-21 22:40:11 +01:00
#define CREATE_GBUFFER_IMAGE(name, format_, add_usage_bits) \
CREATE_GBUFFER_IMAGE(denoised, VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
/*
R_VkResourceSetExternal("all_textures", VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
(vk_descriptor_value_t){
.image_array = tglob.dii_all_textures,
}, MAX_TEXTURES, (ray_resource_desc_t){ResourceImage, VK_FORMAT_UNDEFINED}, NULL
);
R_VkResourceSetExternal("skybox", VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
(vk_descriptor_value_t){
.image = {
.sampler = vk_core.default_sampler,
.imageView = tglob.skybox_cube.vk.image.view ? tglob.skybox_cube.vk.image.view : tglob.cubemap_placeholder.vk.image.view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
},
}, 1, (ray_resource_desc_t){ResourceImage, VK_FORMAT_UNDEFINED}, NULL
);
#define RES_SET_IMAGE(index, name, format) \
R_VkResourceSetExternal(#name, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, \
(vk_descriptor_value_t){0}, \
1, (ray_resource_desc_t){ResourceImage, format}, \
&args->current_frame->name);
RAY_PRIMARY_OUTPUTS(RES_SET_IMAGE);
RAY_LIGHT_DIRECT_POLY_OUTPUTS(RES_SET_IMAGE);
RAY_LIGHT_DIRECT_POINT_OUTPUTS(RES_SET_IMAGE);
RES_SET_IMAGE(-1, denoised, rgba16f);
#undef RES_SET_IMAGE
*/
2021-11-21 22:40:11 +01:00
#undef CREATE_GBUFFER_IMAGE
gEngine.Cmd_AddCommand("vk_rtx_reload", reloadPipeline, "Reload RTX shader");
gEngine.Cmd_AddCommand("vk_rtx_reload_rad", reloadLighting, "Reload RAD files for static lights");
gEngine.Cmd_AddCommand("vk_rtx_freeze", freezeModels, "Freeze models, do not update/add/delete models from to-draw list");
2021-02-27 22:43:49 +01:00
return true;
}
void VK_RayShutdown( void ) {
2021-02-27 22:43:49 +01:00
ASSERT(vk_core.rtx);
R_VkMeatpipeDestroy(g_rtx.mainpipe);
// FIXME destroy mainpipe resources
/*
for (int i = 0; i < ARRAYSIZE(g_rtx.frames); ++i) {
2021-11-21 22:40:11 +01:00
XVK_ImageDestroy(&g_rtx.frames[i].denoised);
}
*/
VK_BufferDestroy(&g_ray_model_state.kusochki_buffer);
VK_BufferDestroy(&g_rtx.uniform_buffer);
2022-07-15 09:35:19 +02:00
RT_VkAccelShutdown();
2021-02-27 22:43:49 +01:00
}