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:
parent
4349d6b7fa
commit
973ef4c677
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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); */
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
Loading…
Reference in New Issue