separate buffer space allocation from buffer uploading

Now each geometry is first allocated a slot with VK_RenderBufferAlloc,
and then VK_RenderBufferLock/Unlock are used to upload buffer contents.

This allow for two things:
1. Uploading buffer data to GPU memory on/after Unlock.
2. (Re)building BLAS for RTX on/after Unlock.

These buffers are now directly referenced by render_draw_t, which also
will be helpful in the future for both renderers.
This commit is contained in:
Ivan Avdeev 2021-02-22 18:54:13 -08:00
parent 4349d6b7fa
commit 973ef4c677
10 changed files with 330 additions and 184 deletions

View File

@ -1,15 +1,12 @@
# 2021-02-20
- [ ] refactor vk_render interface:
- [x] move uniform_data_t to global render state ~inside render_draw_t, remove any mentions of uniform/slots from api; alt: global render state?~
- [x] rename RenderDraw to SubmitDraw
- [x] ~add debug label to render_draw_t?;~ alt: VK_RenderDebugNameBegin/End
- [x] perform 3d rendering on corresponding refapi calls, not endframe
- [ ] restore debug labels
- [x] fix sprite blending
# Next
- [ ] (RTX; common) Staging vs on-GPU buffers
- [ ] (RTX) BLAS construction on buffer unlock
- [ ] (RTX) ray trace compute shader
- [ ] (RTX) geometry indexing
- [ ] (RTX) textures
# Planned
- [ ] restore render debug labels
- [ ] make 2nd commad buffer for resource upload
- [ ] fix sprite blending; there are commented out functions that we really need (see tunnel before the helicopter in the very beginning)
- [ ] RTX: make projection matrix independent render global/current/static state
@ -55,6 +52,7 @@
- [ ] not visibly watertight map brushes
- [ ] collect render_draw_t w/o submitting them to cmdbuf, then sort by render_mode, trans depth, and other parameters, trying to batch as much stuff as possible; only then submit
# Previously
## 2021-02-06
- [x] alpha test
@ -91,3 +89,14 @@
## 2021-02-17
- [x] draw some beams
## 2021-02-20
- [x] refactor vk_render interface:
- [x] move uniform_data_t to global render state ~inside render_draw_t, remove any mentions of uniform/slots from api; alt: global render state?~
- [x] rename RenderDraw to SubmitDraw
- [x] ~add debug label to render_draw_t?;~ alt: VK_RenderDebugNameBegin/End
- [x] perform 3d rendering on corresponding refapi calls, not endframe
- [x] fix sprite blending
## 2021-02-22
- [x] RTX: load extensions with -rtx arg
- [x] vk_render: buffer-alloc-centric upload and draw api

View File

@ -163,9 +163,10 @@ static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, f
beamseg_t curSeg;
int total_vertices = 0;
int total_indices = 0;
vk_buffer_alloc_t vertex_buffer, index_buffer;
vk_vertex_t *pvtx = NULL;
uint16_t *pidx = NULL;
vk_buffer_handle_t vertex_buffer, index_buffer;
vk_buffer_lock_t vertex_lock, index_lock;
vk_vertex_t *dst_vtx;
uint16_t *dst_idx;
if( segments < 2 ) return;
@ -223,16 +224,19 @@ static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, f
total_indices = (total_vertices - 2) * 3; // STRIP unrolled into LIST (TODO get rid of this)
ASSERT(total_vertices < UINT16_MAX );
vertex_buffer = VK_RenderTempBufferAlloc(sizeof(vk_vertex_t), total_vertices);
index_buffer = VK_RenderTempBufferAlloc(sizeof(uint16_t), total_indices);
if (!vertex_buffer.ptr || !index_buffer.ptr) {
gEngine.Con_Printf(S_ERROR "Couldn't allocate %d vertices or %d indices for beam\n", total_vertices, total_indices);
vertex_buffer = VK_RenderBufferAlloc( sizeof(vk_vertex_t), total_vertices, LifetimeSingleFrame );
index_buffer = VK_RenderBufferAlloc( sizeof(uint16_t), total_indices, LifetimeSingleFrame );
if (vertex_buffer == InvalidHandle || index_buffer == InvalidHandle)
{
// TODO should we free one of the above if it still succeeded?
gEngine.Con_Printf(S_ERROR "Ran out of buffer space\n");
return;
}
pvtx = vertex_buffer.ptr;
pidx = index_buffer.ptr;
vertex_lock = VK_RenderBufferLock( vertex_buffer );
index_lock = VK_RenderBufferLock( index_buffer );
dst_vtx = vertex_lock.ptr;
dst_idx = index_lock.ptr;
// specify all the segments.
for( i = 0; i < segments; i++ )
@ -297,21 +301,21 @@ static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, f
VectorMA( curSeg.pos, ( curSeg.width * 0.5f ), vAveNormal, vPoint1 );
VectorMA( curSeg.pos, (-curSeg.width * 0.5f ), vAveNormal, vPoint2 );
pvtx->lm_tc[0] = pvtx->lm_tc[1] = 0.f;
pvtx->gl_tc[0] = 0.0f;
pvtx->gl_tc[1] = curSeg.texcoord;
//FIXME VK applyBrightness( brightness, color, pvtx->color );
dst_vtx->lm_tc[0] = dst_vtx->lm_tc[1] = 0.f;
dst_vtx->gl_tc[0] = 0.0f;
dst_vtx->gl_tc[1] = curSeg.texcoord;
//FIXME VK applyBrightness( brightness, color, dst_vtx->color );
// FIXME VK pglNormal3fv( vAveNormal );
VectorCopy( vPoint1, pvtx->pos );
++pvtx;
VectorCopy( vPoint1, dst_vtx->pos );
++dst_vtx;
pvtx->lm_tc[0] = pvtx->lm_tc[1] = 0.f;
pvtx->gl_tc[0] = 1.0f;
pvtx->gl_tc[1] = curSeg.texcoord;
//FIXME VK applyBrightness( brightness, color, pvtx->color );
dst_vtx->lm_tc[0] = dst_vtx->lm_tc[1] = 0.f;
dst_vtx->gl_tc[0] = 1.0f;
dst_vtx->gl_tc[1] = curSeg.texcoord;
//FIXME VK applyBrightness( brightness, color, dst_vtx->color );
// FIXME VK pglNormal3fv( vAveNormal );
VectorCopy( vPoint2, pvtx->pos );
++pvtx;
VectorCopy( vPoint2, dst_vtx->pos );
++dst_vtx;
}
curSeg = nextSeg;
@ -337,21 +341,21 @@ static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, f
VectorMA( curSeg.pos, ( curSeg.width * 0.5f ), vLastNormal, vPoint1 );
VectorMA( curSeg.pos, (-curSeg.width * 0.5f ), vLastNormal, vPoint2 );
pvtx->lm_tc[0] = pvtx->lm_tc[1] = 0.f;
pvtx->gl_tc[0] = 0.0f;
pvtx->gl_tc[1] = curSeg.texcoord;
//FIXME VK applyBrightness( brightness, color, pvtx->color );
dst_vtx->lm_tc[0] = dst_vtx->lm_tc[1] = 0.f;
dst_vtx->gl_tc[0] = 0.0f;
dst_vtx->gl_tc[1] = curSeg.texcoord;
//FIXME VK applyBrightness( brightness, color, dst_vtx->color );
// FIXME VK pglNormal3fv( vLastNormal );
VectorCopy( vPoint1, pvtx->pos );
++pvtx;
VectorCopy( vPoint1, dst_vtx->pos );
++dst_vtx;
pvtx->lm_tc[0] = pvtx->lm_tc[1] = 0.f;
pvtx->gl_tc[0] = 1.0f;
pvtx->gl_tc[1] = curSeg.texcoord;
//FIXME VK applyBrightness( brightness, color, pvtx->color );
dst_vtx->lm_tc[0] = dst_vtx->lm_tc[1] = 0.f;
dst_vtx->gl_tc[0] = 1.0f;
dst_vtx->gl_tc[1] = curSeg.texcoord;
//FIXME VK applyBrightness( brightness, color, dst_vtx->color );
// FIXME VK pglNormal3fv( vLastNormal );
VectorCopy( vPoint2, pvtx->pos );
++pvtx;
VectorCopy( vPoint2, dst_vtx->pos );
++dst_vtx;
}
vLast += vStep; // Advance texture scroll (v axis only)
@ -362,26 +366,32 @@ static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, f
if( i & 1 )
{
// draw triangle [n-1 n-2 n]
pidx[(i-2)*3+0] = i - 1;
pidx[(i-2)*3+1] = i - 2;
pidx[(i-2)*3+2] = i;
dst_idx[(i-2)*3+0] = i - 1;
dst_idx[(i-2)*3+1] = i - 2;
dst_idx[(i-2)*3+2] = i;
}
else
{
// draw triangle [n-2 n-1 n]
pidx[(i-2)*3+0] = i - 2;
pidx[(i-2)*3+1] = i - 1;
pidx[(i-2)*3+2] = i;
dst_idx[(i-2)*3+0] = i - 2;
dst_idx[(i-2)*3+1] = i - 1;
dst_idx[(i-2)*3+2] = i;
}
}
VK_RenderBufferUnlock( index_buffer );
VK_RenderBufferUnlock( vertex_buffer );
{
const render_draw_t draw = {
.lightmap = tglob.whiteTexture,
.texture = texture,
.render_mode = render_mode,
.element_count = total_indices,
.vertex_offset = vertex_buffer.buffer_offset_in_units,
.index_offset = index_buffer.buffer_offset_in_units,
.vertex_offset = 0,
.index_offset = 0,
.vertex_buffer = vertex_buffer,
.index_buffer = index_buffer,
};
VK_RenderScheduleDraw( &draw );

View File

@ -21,16 +21,13 @@ typedef struct vk_brush_model_surface_s {
int texture_num;
msurface_t *surf;
// Offset into g_brush.index_buffer in vertices
uint32_t index_offset;
uint16_t index_count;
} vk_brush_model_surface_t;
typedef struct vk_brush_model_s {
//model_t *model;
// Offset into g_brush.vertex_buffer in vertices
uint32_t vertex_offset;
vk_buffer_handle_t vertex_buffer;
vk_buffer_handle_t index_buffer;
int num_surfaces;
vk_brush_model_surface_t surfaces[];
@ -163,7 +160,9 @@ void VK_BrushDrawModel( const cl_entity_t *ent, int render_mode )
.texture = current_texture,
.render_mode = render_mode,
.element_count = index_count,
.vertex_offset = bmodel->vertex_offset,
.vertex_buffer = bmodel->vertex_buffer,
.index_buffer = bmodel->index_buffer,
.vertex_offset = 0,
.index_offset = index_offset,
};
@ -188,7 +187,9 @@ void VK_BrushDrawModel( const cl_entity_t *ent, int render_mode )
.texture = current_texture,
.render_mode = render_mode,
.element_count = index_count,
.vertex_offset = bmodel->vertex_offset,
.vertex_buffer = bmodel->vertex_buffer,
.index_buffer = bmodel->index_buffer,
.vertex_offset = 0,
.index_offset = index_offset,
};
@ -202,7 +203,8 @@ static int loadBrushSurfaces( const model_t *mod, vk_brush_model_surface_t *out_
vk_brush_model_t *bmodel = mod->cache.data;
uint32_t vertex_offset = 0;
int num_surfaces = 0;
vk_buffer_alloc_t vertex_alloc, index_alloc;
vk_buffer_handle_t vertex_buffer, index_buffer;
vk_buffer_lock_t vertex_lock, index_lock;
vk_vertex_t *bvert = NULL;
uint16_t *bind = NULL;
uint32_t index_offset = 0;
@ -229,18 +231,20 @@ static int loadBrushSurfaces( const model_t *mod, vk_brush_model_surface_t *out_
max_texture_id = surf->texinfo->texture->gl_texturenum;
}
vertex_alloc = VK_RenderBufferAlloc( sizeof(vk_vertex_t), num_vertices );
bvert = vertex_alloc.ptr;
bmodel->vertex_offset = vertex_alloc.buffer_offset_in_units;
if (!bvert)
vertex_buffer = VK_RenderBufferAlloc( sizeof(vk_vertex_t), num_vertices, LifetimeMap );
index_buffer = VK_RenderBufferAlloc( sizeof(uint16_t), num_indices, LifetimeMap );
if (vertex_buffer == InvalidHandle || index_buffer == InvalidHandle)
{
gEngine.Con_Printf(S_ERROR "Ran out of buffer vertex space\n");
// TODO should we free one of the above if it still succeeded?
gEngine.Con_Printf(S_ERROR "Ran out of buffer space\n");
return -1;
}
index_alloc = VK_RenderBufferAlloc( sizeof(uint16_t), num_indices );
bind = index_alloc.ptr;
index_offset = index_alloc.buffer_offset_in_units;
vertex_lock = VK_RenderBufferLock( vertex_buffer );
index_lock = VK_RenderBufferLock( index_buffer );
bvert = vertex_lock.ptr;
bind = index_lock.ptr;
if (!bind)
{
gEngine.Con_Printf(S_ERROR "Ran out of buffer index space\n");
@ -273,7 +277,6 @@ static int loadBrushSurfaces( const model_t *mod, vk_brush_model_surface_t *out_
//gEngine.Con_Reportf( "surface %d: numverts=%d numedges=%d\n", i, surf->polys ? surf->polys->numverts : -1, surf->numedges );
if (vertex_offset + surf->numedges >= UINT16_MAX)
{
gEngine.Con_Printf(S_ERROR "Model %s indices don't fit into 16 bits\n", mod->name);
@ -321,8 +324,6 @@ static int loadBrushSurfaces( const model_t *mod, vk_brush_model_surface_t *out_
vertex.lm_tc[0] = s;
vertex.lm_tc[1] = t;
//gEngine.Con_Printf("VERT %u %f %f %f\n", g_brush.num_vertices, in_vertex->position[0], in_vertex->position[1], in_vertex->position[2]);
*(bvert++) = vertex;
// TODO contemplate triangle_strip (or fan?) + primitive restart
@ -339,6 +340,12 @@ static int loadBrushSurfaces( const model_t *mod, vk_brush_model_surface_t *out_
}
}
VK_RenderBufferUnlock( index_buffer );
VK_RenderBufferUnlock( vertex_buffer );
bmodel->vertex_buffer = vertex_buffer;
bmodel->index_buffer = index_buffer;
return num_surfaces;
}
@ -363,15 +370,6 @@ qboolean VK_LoadBrushModel( model_t *mod, const byte *buffer )
return false;
}
/* gEngine.Con_Reportf("Model %s, vertex_offset=%d, first surface index_offset=%d\n", */
/* mod->name, */
/* bmodel->vertex_offset, bmodel->num_surfaces ? bmodel->surfaces[0].index_offset : -1); */
/* for (int i = 0; i < bmodel->num_surfaces; ++i) */
/* gEngine.Con_Reportf("\t%d: tex=%d, off=%d, cnt=%d\n", i, */
/* bmodel->surfaces[i].texture_num, */
/* bmodel->surfaces[i].index_offset, */
/* bmodel->surfaces[i].index_count); */
gEngine.Con_Reportf("Model %s loaded surfaces: %d (of %d); total vertices: %u, total indices: %u\n", mod->name, bmodel->num_surfaces, mod->nummodelsurfaces, g_brush.stat.num_vertices, g_brush.stat.num_indices);
return true;
}

View File

@ -361,8 +361,24 @@ static qboolean pickAndCreateDevice( void )
.queueCount = 1,
.pQueuePriorities = &prio,
};
VkPhysicalDeviceBufferDeviceAddressFeatures buffer_address_feature = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES,
.bufferDeviceAddress = VK_TRUE,
};
VkPhysicalDeviceAccelerationStructureFeaturesKHR accel_feature = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR,
.accelerationStructure = VK_TRUE,
.pNext = &buffer_address_feature,
};
VkPhysicalDeviceRayQueryFeaturesKHR ray_query_feature = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR,
.rayQuery = VK_TRUE,
.pNext = &accel_feature,
};
VkDeviceCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = vk_core.rtx ? &ray_query_feature : NULL,
.flags = 0,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queue_info,

View File

@ -407,6 +407,12 @@ void R_EndFrame( void )
XVK_CHECK(vkWaitForFences(vk_core.device, 1, &g_frame.fence, VK_TRUE, INT64_MAX));
XVK_CHECK(vkResetFences(vk_core.device, 1, &g_frame.fence));
// TODO better sync implies multiple frames in flight, which means that we must
// retain temporary (SingleFrame) buffer contents for longer, until all users are done.
// (this probably means that we should really have some kind of refcount going on...)
// For now we can just erase these buffers now because of sync with fence
VK_RenderBufferClearFrame();
g_frame.swapchain_image_index = -1;
}

View File

@ -20,12 +20,25 @@ typedef struct {
vec4_t color;
} uniform_data_t;
typedef struct vk_buffer_alloc_s {
// TODO uint32_t sequence
uint32_t unit_size; // if 0 then this alloc slot is free
uint32_t buffer_offset_in_units;
uint32_t count;
qboolean locked;
vk_lifetime_t lifetime;
} vk_buffer_alloc_t;
// TODO estimate
#define MAX_ALLOCS 1024
static struct {
VkPipelineLayout pipeline_layout;
VkPipeline pipelines[kRenderTransAdd + 1];
vk_buffer_t buffer;
uint32_t buffer_free_offset;
uint32_t buffer_frame_begin_offset;
vk_buffer_t uniform_buffer;
uint32_t uniform_unit_size;
@ -34,7 +47,9 @@ static struct {
int align_holes_size;
} stat;
uint32_t temp_buffer_offset;
vk_buffer_alloc_t allocs[MAX_ALLOCS];
int allocs_free[MAX_ALLOCS];
int num_free_allocs;
} g_render;
static qboolean createPipelines( void )
@ -212,6 +227,14 @@ static qboolean createPipelines( void )
return true;
}
static void resetAllocFreeList( void ) {
g_render.num_free_allocs = MAX_ALLOCS;
for (int i = 0; i < MAX_ALLOCS; ++i) {
g_render.allocs_free[i] = MAX_ALLOCS - i - 1;
g_render.allocs[i].unit_size = 0;
}
}
qboolean VK_RenderInit( void )
{
// TODO Better estimates
@ -250,6 +273,8 @@ qboolean VK_RenderInit( void )
if (!createPipelines())
return false;
resetAllocFreeList();
return true;
}
@ -263,38 +288,109 @@ void VK_RenderShutdown( void )
destroyBuffer( &g_render.uniform_buffer );
}
static vk_buffer_alloc_t renderBufferAlloc( uint32_t unit_size, uint32_t count )
vk_buffer_handle_t VK_RenderBufferAlloc( uint32_t unit_size, uint32_t count, vk_lifetime_t lifetime )
{
const uint32_t offset = ALIGN_UP(g_render.buffer_free_offset, unit_size);
const uint32_t alloc_size = unit_size * count;
vk_buffer_alloc_t ret = {0};
vk_buffer_alloc_t *alloc;
vk_buffer_handle_t handle = InvalidHandle;
// FIXME long lifetimes are not supported yet
ASSERT(lifetime != LifetimeLong);
ASSERT(unit_size > 0);
if (offset + alloc_size > g_render.buffer.size) {
gEngine.Con_Printf(S_ERROR "Cannot allocate %u bytes aligned at %u from buffer; only %u are left",
alloc_size, unit_size, g_render.buffer.size - offset);
return (vk_buffer_alloc_t){0};
return InvalidHandle;
}
ret.buffer_offset_in_units = offset / unit_size;
ret.ptr = ((byte*)g_render.buffer.mapped) + offset;
if (!g_render.num_free_allocs) {
gEngine.Con_Printf(S_ERROR "Cannot allocate buffer, allocs count exhausted\n" );
return InvalidHandle;
}
// TODO bake sequence number into handle (to detect buffer lifetime misuse)
handle = g_render.allocs_free[--g_render.num_free_allocs];
alloc = g_render.allocs + handle;
ASSERT(alloc->unit_size == 0);
alloc->buffer_offset_in_units = offset / unit_size;
alloc->unit_size = unit_size;
alloc->lifetime = lifetime;
alloc->count = count;
g_render.stat.align_holes_size += offset - g_render.buffer_free_offset;
g_render.buffer_free_offset = offset + alloc_size;
if (lifetime < LifetimeSingleFrame)
g_render.buffer_frame_begin_offset = g_render.buffer_free_offset;
return handle;
}
static vk_buffer_alloc_t *getBufferFromHandle( vk_buffer_handle_t handle )
{
vk_buffer_alloc_t *alloc;
ASSERT(handle >= 0);
ASSERT(handle < MAX_ALLOCS);
// TODO check sequence number
alloc = g_render.allocs + handle;
ASSERT(alloc->unit_size != 0);
return alloc;
}
vk_buffer_lock_t VK_RenderBufferLock( vk_buffer_handle_t handle )
{
vk_buffer_lock_t ret = {0};
vk_buffer_alloc_t *alloc = getBufferFromHandle( handle );
ASSERT(!alloc->locked);
alloc->locked = true;
ret.unit_size = alloc->unit_size;
ret.count = alloc->count;
ret.ptr = ((byte*)g_render.buffer.mapped) + alloc->unit_size * alloc->buffer_offset_in_units;
return ret;
}
vk_buffer_alloc_t VK_RenderBufferAlloc( uint32_t unit_size, uint32_t count )
void VK_RenderBufferUnlock( vk_buffer_handle_t handle )
{
// FIXME this is not correct: on the very first map load it will trigger a lot
if (g_render.buffer_free_offset >= g_render.temp_buffer_offset)
gEngine.Con_Printf(S_ERROR "Trying to allocate map-permanent storage in temp buffer context\n");
vk_buffer_alloc_t *alloc = getBufferFromHandle( handle );
ASSERT(alloc->locked);
alloc->locked = false;
return renderBufferAlloc( unit_size, count );
// TODO upload from staging to gpumem
}
void VK_RenderBufferClearAll( void )
// Free all LifetimeSingleFrame resources
void VK_RenderBufferClearFrame( void )
{
g_render.buffer_free_offset = 0;
g_render.buffer_free_offset = g_render.buffer_frame_begin_offset;
for (int i = 0; i < MAX_ALLOCS; ++i) {
vk_buffer_alloc_t *alloc = g_render.allocs + i;
if (!alloc->unit_size)
continue;
if (alloc->lifetime != LifetimeSingleFrame)
continue;
alloc->unit_size = 0;
g_render.allocs_free[g_render.num_free_allocs++] = i;
ASSERT(g_render.num_free_allocs <= MAX_ALLOCS);
}
}
// Free all LifetimeMap resources
void VK_RenderBufferClearMap( void )
{
g_render.buffer_free_offset = g_render.buffer_frame_begin_offset = 0;
g_render.stat.align_holes_size = 0;
resetAllocFreeList();
}
void VK_RenderBufferPrintStats( void )
@ -305,21 +401,6 @@ void VK_RenderBufferPrintStats( void )
g_render.stat.align_holes_size);
}
void VK_RenderTempBufferBegin( void )
{
g_render.temp_buffer_offset = g_render.buffer_free_offset;
}
vk_buffer_alloc_t VK_RenderTempBufferAlloc( uint32_t unit_size, uint32_t count )
{
return renderBufferAlloc( unit_size, count );
}
void VK_RenderTempBufferEnd( void )
{
g_render.buffer_free_offset = g_render.temp_buffer_offset;
}
#define MAX_DRAW_COMMANDS 8192 // TODO estimate
#define MAX_DEBUG_NAME_LENGTH 32
@ -396,6 +477,20 @@ void VK_RenderScheduleDraw( const render_draw_t *draw )
ASSERT(draw->lightmap >= 0);
ASSERT(draw->texture >= 0);
{
const vk_buffer_alloc_t *vertex_buffer = getBufferFromHandle(draw->vertex_buffer);
ASSERT(vertex_buffer);
ASSERT(!vertex_buffer->locked);
}
// Index buffer is optional
if (draw->index_buffer != InvalidHandle)
{
const vk_buffer_alloc_t *index_buffer = getBufferFromHandle(draw->index_buffer);
ASSERT(index_buffer);
ASSERT(!index_buffer->locked);
}
if ((g_render_state.uniform_data_set_mask & UNIFORM_SET_ALL) != UNIFORM_SET_ALL) {
gEngine.Con_Printf( S_ERROR "Not all uniform state was initialized prior to rendering\n" );
return;
@ -445,6 +540,9 @@ void VK_RenderEnd( VkCommandBuffer cmdbuf )
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;
if (ubo_offset != draw->ubo_offset)
{
@ -469,44 +567,29 @@ void VK_RenderEnd( VkCommandBuffer cmdbuf )
vkCmdBindDescriptorSets(vk_core.cb, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 1, 1, &findTexture(texture)->vk.descriptor, 0, NULL);
}
if (draw->draw.index_offset == UINT32_MAX) {
/*gEngine.Con_Reportf( "Draw("
"ubo_index=%d, "
"lightmap=%d, "
"texture=%d, "
"render_mode=%d, "
"element_count=%u, "
"index_offset=%u, "
"vertex_offset=%u)\n",
draw->ubo_index,
draw->lightmap,
draw->texture,
draw->render_mode,
draw->element_count,
draw->index_offset,
draw->vertex_offset );
*/
vkCmdDraw(vk_core.cb, draw->draw.element_count, 1, draw->draw.vertex_offset, 0);
if (draw->draw.index_buffer) {
const uint32_t index_offset = index_buffer->buffer_offset_in_units + draw->draw.index_offset;
vkCmdDrawIndexed(vk_core.cb, draw->draw.element_count, 1, index_offset, vertex_offset, 0);
} else {
vkCmdDrawIndexed(vk_core.cb, draw->draw.element_count, 1, draw->draw.index_offset, draw->draw.vertex_offset, 0);
vkCmdDraw(vk_core.cb, draw->draw.element_count, 1, vertex_offset, 0);
}
}
}
void VK_RenderDebugLabelBegin( const char *name )
{
if (vk_core.debug) {
VkDebugUtilsLabelEXT label = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
.pLabelName = name,
};
vkCmdBeginDebugUtilsLabelEXT(vk_core.cb, &label);
}
// TODO fix this
/* if (vk_core.debug) { */
/* VkDebugUtilsLabelEXT label = { */
/* .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, */
/* .pLabelName = name, */
/* }; */
/* vkCmdBeginDebugUtilsLabelEXT(vk_core.cb, &label); */
/* } */
}
void VK_RenderDebugLabelEnd( void )
{
if (vk_core.debug)
vkCmdEndDebugUtilsLabelEXT(vk_core.cb);
/* if (vk_core.debug) */
/* vkCmdEndDebugUtilsLabelEXT(vk_core.cb); */
}

View File

@ -6,21 +6,37 @@
qboolean VK_RenderInit( void );
void VK_RenderShutdown( void );
typedef struct vk_buffer_alloc_s {
uint32_t buffer_offset_in_units;
typedef int vk_buffer_handle_t; // -1 == invalid handle
enum { InvalidHandle = -1 };
typedef struct {
void *ptr;
} vk_buffer_alloc_t;
uint32_t unit_size, count;
} vk_buffer_lock_t;
typedef enum {
LifetimeLong,
LifetimeMap,
LifetimeSingleFrame,
} vk_lifetime_t;
// TODO: allocation lifetime with contents validity lifetime?
vk_buffer_handle_t VK_RenderBufferAlloc( uint32_t unit_size, uint32_t count, vk_lifetime_t lifetime );
vk_buffer_lock_t VK_RenderBufferLock( vk_buffer_handle_t handle );
void VK_RenderBufferUnlock( vk_buffer_handle_t handle );
// TODO buffer refcount when doing RTX AS updates? need to store buffer handles somewhere between frames
// Free all LifetimeSingleFrame resources
void VK_RenderBufferClearFrame( void );
// Free all LifetimeMap resources
void VK_RenderBufferClearMap( void );
// TODO uploading to GPU mem interface
vk_buffer_alloc_t VK_RenderBufferAlloc( uint32_t unit_size, uint32_t count );
void VK_RenderBufferClearAll( void );
void VK_RenderBufferPrintStats( void );
// TODO address cringiness of this when doing buffer upload to GPU RAM properly
void VK_RenderTempBufferBegin( void );
vk_buffer_alloc_t VK_RenderTempBufferAlloc( uint32_t unit_size, uint32_t count );
void VK_RenderTempBufferEnd( void );
// Set UBO state for next VK_RenderScheduleDraw calls
// Why? Xash Ref code is organized in a way where we can't reliably pass this info with
// ScheduleDraw itself, so we need to either set up per-submodule global state, or
@ -42,6 +58,7 @@ typedef struct render_draw_s {
int render_mode;
uint32_t element_count;
uint32_t index_offset, vertex_offset;
vk_buffer_handle_t index_buffer, vertex_buffer;
} render_draw_t;
void VK_RenderBegin( void );

View File

@ -93,7 +93,12 @@ void R_NewMap( void )
// TODO should we do something like VK_BrushBeginLoad?
VK_BrushClear();
VK_RenderBufferClearAll();
// FIXME this is totally incorrect btw.
// When loading a save game from the same map this is called, but brush models
// have not been destroyed, which prevents them from being loaded ("again").
// This leads to ASSERTS firing when trying to draw erased buffers.
VK_RenderBufferClearMap();
// Load all models at once
gEngine.Con_Reportf( "Num models: %d:\n", num_models );
@ -625,8 +630,6 @@ void VK_SceneRender( const ref_viewpass_t *rvp )
gpGlobals->time - gpGlobals->oldtime
/* FIXME VK : 0.f */;
// FIXME this is a bad place for this call -- theoretically we can get multiple calls to VK_SceneRender so we should not erase previous tmp buffer contents
VK_RenderTempBufferBegin();
setupCamera( rvp, mvp );
VK_RenderDebugLabelBegin( "opaque" );
@ -681,8 +684,6 @@ void VK_SceneRender( const ref_viewpass_t *rvp )
gEngine.CL_DrawEFX( g_frametime, true );
VK_RenderDebugLabelEnd();
VK_RenderTempBufferEnd();
}
// FIXME better place?

View File

@ -649,26 +649,25 @@ qboolean R_SpriteOccluded( cl_entity_t *e, vec3_t origin, float *pscale )
static void R_DrawSpriteQuad( mspriteframe_t *frame, vec3_t org, vec3_t v_right, vec3_t v_up, float scale, int texture, int render_mode )
{
vec3_t point;
vk_buffer_alloc_t buf_vertex, buf_index;
vk_buffer_handle_t vertex_buffer, index_buffer;
vk_buffer_lock_t vertex_lock, index_lock;
vk_vertex_t *dst_vtx;
uint16_t *dst_idx;
// Get buffer region for vertices and indices
buf_vertex = VK_RenderTempBufferAlloc( sizeof(vk_vertex_t), 4 );
if (!buf_vertex.ptr)
vertex_buffer = VK_RenderBufferAlloc( sizeof(vk_vertex_t), 4, LifetimeSingleFrame );
index_buffer = VK_RenderBufferAlloc( sizeof(uint16_t), 6, LifetimeSingleFrame );
if (vertex_buffer == InvalidHandle || index_buffer == InvalidHandle)
{
gEngine.Con_Printf(S_ERROR "Cannot render mesh\n"); // TODO mesh signature?
// TODO should we free one of the above if it still succeeded?
gEngine.Con_Printf(S_ERROR "Ran out of buffer space\n");
return;
}
dst_vtx = buf_vertex.ptr;
buf_index = VK_RenderTempBufferAlloc( sizeof(uint16_t), 6 );
if (!buf_index.ptr)
{
gEngine.Con_Printf(S_ERROR "Cannot render mesh\n"); // TODO mesh signature?
return;
}
dst_idx = buf_index.ptr;
vertex_lock = VK_RenderBufferLock( vertex_buffer );
index_lock = VK_RenderBufferLock( index_buffer );
dst_vtx = vertex_lock.ptr;
dst_idx = index_lock.ptr;
// FIXME VK r_stats.c_sprite_polys++;
@ -703,14 +702,19 @@ static void R_DrawSpriteQuad( mspriteframe_t *frame, vec3_t org, vec3_t v_right,
dst_idx[4] = 2;
dst_idx[5] = 3;
VK_RenderBufferUnlock( index_buffer );
VK_RenderBufferUnlock( vertex_buffer );
{
const render_draw_t draw = {
.lightmap = tglob.whiteTexture,
.texture = texture,
.render_mode = render_mode,
.element_count = 6,
.vertex_offset = buf_vertex.buffer_offset_in_units,
.index_offset = buf_index.buffer_offset_in_units,
.vertex_offset = 0,
.index_offset = 0,
.vertex_buffer = vertex_buffer,
.index_buffer = index_buffer,
};
VK_RenderScheduleDraw( &draw );

View File

@ -1911,10 +1911,11 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float
float *lv;
int i;
int num_vertices = 0, num_indices = 0;
vk_buffer_alloc_t buf_vertex, buf_index;
vk_buffer_handle_t vertex_buffer, index_buffer;
vk_buffer_lock_t vertex_lock, index_lock;
vk_vertex_t *dst_vtx;
uint16_t *dst_idx;
uint32_t vertex_offset, index_offset;
uint32_t vertex_offset = 0, index_offset = 0;
short* const ptricmds_initial = ptricmds;
// Compute counts of vertices and indices
@ -1932,23 +1933,19 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float
ASSERT(num_indices > 0);
// Get buffer region for vertices and indices
buf_vertex = VK_RenderTempBufferAlloc( sizeof(vk_vertex_t), num_vertices );
if (!buf_vertex.ptr)
vertex_buffer = VK_RenderBufferAlloc( sizeof(vk_vertex_t), num_vertices, LifetimeSingleFrame );
index_buffer = VK_RenderBufferAlloc( sizeof(uint16_t), num_indices, LifetimeSingleFrame );
if (vertex_buffer == InvalidHandle || index_buffer == InvalidHandle)
{
gEngine.Con_Printf(S_ERROR "Cannot render mesh\n"); // TODO mesh signature?
// TODO should we free one of the above if it still succeeded?
gEngine.Con_Printf(S_ERROR "Ran out of buffer space\n");
return;
}
dst_vtx = buf_vertex.ptr;
vertex_offset = 0;
buf_index = VK_RenderTempBufferAlloc( sizeof(uint16_t), num_indices );
if (!buf_index.ptr)
{
gEngine.Con_Printf(S_ERROR "Cannot render mesh\n"); // TODO mesh signature?
return;
}
dst_idx = buf_index.ptr;
index_offset = buf_index.buffer_offset_in_units;
vertex_lock = VK_RenderBufferLock( vertex_buffer );
index_lock = VK_RenderBufferLock( index_buffer );
dst_vtx = vertex_lock.ptr;
dst_idx = index_lock.ptr;
// Restore ptricmds and upload vertices
ptricmds = ptricmds_initial;
@ -1960,7 +1957,7 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float
for(int j = 0; j < vertices ; ++j, ++dst_vtx, ptricmds += 4 )
{
ASSERT((((vk_vertex_t*)buf_vertex.ptr) + num_vertices) > dst_vtx);
ASSERT((((vk_vertex_t*)vertex_lock.ptr) + vertex_lock.count) > dst_vtx);
VectorCopy(g_studio.verts[ptricmds[0]], dst_vtx->pos);
dst_vtx->lm_tc[0] = dst_vtx->lm_tc[1] = mode == FAN ? .5f : 0.f;
@ -2003,9 +2000,12 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float
}
ASSERT(vertex_offset < UINT16_MAX);
ASSERT(index_offset - buf_index.buffer_offset_in_units == num_indices);
ASSERT(index_offset == num_indices);
ASSERT(vertex_offset == num_vertices);
VK_RenderBufferUnlock( index_buffer );
VK_RenderBufferUnlock( vertex_buffer );
// Render
{
const render_draw_t draw = {
@ -2013,8 +2013,10 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float
.texture = texture,
.render_mode = render_mode,
.element_count = num_indices,
.vertex_offset = buf_vertex.buffer_offset_in_units,
.index_offset = buf_index.buffer_offset_in_units,
.vertex_offset = 0,
.index_offset = 0,
.vertex_buffer = vertex_buffer,
.index_buffer = index_buffer,
};
VK_RenderScheduleDraw( &draw );