Merge pull request #26 from w23/better-buffers

join alloc+lock operations on buffer init

also disable ci results uploading, this is at the very best useless
This commit is contained in:
Ivan Avdeev 2021-08-14 18:42:56 -07:00 committed by GitHub
commit 31520a7c0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 162 additions and 312 deletions

View File

@ -58,15 +58,15 @@ jobs:
- name: Build engine
run: bash scripts/gha/build_${{ matrix.targetos }}.sh
- name: Upload engine (android)
if: matrix.targetos == 'android'
run: bash scripts/continious_upload.sh xashdroid-${{ matrix.targetarch }}.apk
- name: Upload engine (motomagx)
if: matrix.targetos == 'motomagx'
run: bash scripts/continious_upload.sh xash3d-fwgs-magx.7z
- name: Upload engine (linux)
if: matrix.targetos == 'linux'
run: bash scripts/continious_upload.sh xash3d-fwgs-${{ matrix.targetarch }}.AppImage xashds-linux-${{ matrix.targetarch }}
- name: Upload engine (windows)
if: matrix.targetos == 'win32'
run: bash scripts/continious_upload.sh xash3d-fwgs-win32-${{ matrix.targetarch }}.7z xash3d-fwgs-vc2008-sln-${{ matrix.targetarch }}.7z
# - name: Upload engine (android)
# if: matrix.targetos == 'android'
# run: bash scripts/continious_upload.sh xashdroid-${{ matrix.targetarch }}.apk
# - name: Upload engine (motomagx)
# if: matrix.targetos == 'motomagx'
# run: bash scripts/continious_upload.sh xash3d-fwgs-magx.7z
# - name: Upload engine (linux)
# if: matrix.targetos == 'linux'
# run: bash scripts/continious_upload.sh xash3d-fwgs-${{ matrix.targetarch }}.AppImage xashds-linux-${{ matrix.targetarch }}
# - name: Upload engine (windows)
# if: matrix.targetos == 'win32'
# run: bash scripts/continious_upload.sh xash3d-fwgs-win32-${{ matrix.targetarch }}.7z xash3d-fwgs-vc2008-sln-${{ matrix.targetarch }}.7z

View File

@ -1,12 +1,6 @@
## 2021-08-07, E124
- [x] anisotropic texture sampling
- [x] studio model lighting prep
- [x] copy over R_LightVec from GL renderer
- [x] add per-vertex color attribute
- [x] support per-vertex colors
- [x] disable lightmaps, or use white texture for it instead
# Next
- [ ] restore render debug labels
- [ ] rtx: simple convolution denoise (bilateral?)
- [ ] rtx: split ray tracing into modules: pipeline mgmt, buffer mgmt
- [ ] rtx: better light culling: normal, bsp visibility, light volumes and intensity, sort by intensity, etc
- [ ] rtx: cluster dlights
@ -14,15 +8,18 @@
Split into 2 buffers:
struct LightCluster { uint16 offset, length; }
uint8_t data[];
- [ ] studio models: fix lighting: should have white texture instead of lightmap OR we could write nearest surface lightmap coords to fake light
- [ ] make it look correct lol
# Planned
- [ ] make a list of all possible materials, categorize them and figure out what to do
- [ ] possibly split vk_render into (a) rendering/pipeline, (b) buffer management/allocation, (c) render state
- [ ] restore draw call concatenation; brush geoms are generated in a way that makes concatenating them impossible
- [ ] rtx: light styles: need static lights data, not clear how and what to do
- [ ] studio models: fix lighting: should have white texture instead of lightmap OR we could write nearest surface lightmap coords to fake light
- [ ] make it look correct lol
- [ ] studio model types:
- [x] normal
- [ ] float
- [ ] chrome
- [ ] simplify buffer api: do alloc+lock as a single op
- [ ] more beams types
- [ ] more particle types
- [ ] rtx: better mip lods: there's a weird math that operates on fov degrees (not radians) that we copypasted from ray tracing gems 2 chapter 7. When the book is available, get through the math and figure this out.
@ -39,9 +36,7 @@
- [ ] rtx: coalesce all these buffers
- [ ] crash in PM_RecursiveHullCheck
- [ ] rtx: entity lights
- [ ] rtx: light styles
- [ ] run under asan
- [ ] rtx: simple convolution denoise (bilateral?)
- [ ] rtx: emissive beams
- [ ] rtx: emissive particles
- [ ] rtx: better random
@ -64,7 +59,6 @@
- [ ] rtx: cull light sources (dlights and light textures) using bsp
- [ ] enable entity-parsed lights by lightstyles
- [ ] dlight for flashlight seems to be broken
- [ ] 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)
- [ ] fix projection matrix differences w/ gl render
@ -314,3 +308,14 @@
## 2021-08-02..04, E122-123
- [x] mipmaps
## 2021-08-07, E124
- [x] anisotropic texture sampling
- [x] studio model lighting prep
- [x] copy over R_LightVec from GL renderer
- [x] add per-vertex color attribute
- [x] support per-vertex colors
- [x] disable lightmaps, or use white texture for it instead
## 2021-08-11, E125
- [x] simplify buffer api: do alloc+lock as a single op

View File

@ -163,8 +163,7 @@ 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_handle_t vertex_buffer, index_buffer;
vk_buffer_lock_t vertex_lock, index_lock;
xvk_render_buffer_allocation_t vertex_buffer, index_buffer;
vk_vertex_t *dst_vtx;
uint16_t *dst_idx;
@ -224,19 +223,17 @@ 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_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)
vertex_buffer = XVK_RenderBufferAllocAndLock( sizeof(vk_vertex_t), total_vertices );
index_buffer = XVK_RenderBufferAllocAndLock( sizeof(uint16_t), total_indices );
if (!vertex_buffer.ptr || !index_buffer.ptr)
{
// TODO should we free one of the above if it still succeeded?
gEngine.Con_Printf(S_ERROR "Ran out of buffer space\n");
return;
}
vertex_lock = VK_RenderBufferLock( vertex_buffer );
index_lock = VK_RenderBufferLock( index_buffer );
dst_vtx = vertex_lock.ptr;
dst_idx = index_lock.ptr;
dst_vtx = vertex_buffer.ptr;
dst_idx = index_buffer.ptr;
// specify all the segments.
for( i = 0; i < segments; i++ )
@ -380,8 +377,8 @@ static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, f
}
}
VK_RenderBufferUnlock( index_buffer );
VK_RenderBufferUnlock( vertex_buffer );
XVK_RenderBufferUnlock( index_buffer.buffer );
XVK_RenderBufferUnlock( vertex_buffer.buffer );
{
const vk_render_geometry_t geometry = {
@ -389,12 +386,10 @@ static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, f
.material = kXVkMaterialDiffuse,
.vertex_count = total_vertices,
.vertex_buffer = vertex_buffer,
.vertex_offset = 0,
.vertex_offset = vertex_buffer.buffer.unit.offset,
.element_count = total_indices,
.index_offset = 0,
.index_buffer = index_buffer,
.index_offset = index_buffer.buffer.unit.offset,
};
// FIXME .emissive = { color[0], color[1], color[2] },

View File

@ -79,8 +79,7 @@ static void EmitWaterPolys( const cl_entity_t *ent, const msurface_t *warp, qboo
glpoly_t *p;
int i;
int num_vertices = 0, num_indices = 0;
vk_buffer_handle_t vertex_buffer, index_buffer = InvalidHandle;
vk_buffer_lock_t vertex_lock, index_lock;
xvk_render_buffer_allocation_t vertex_buffer, index_buffer = {0};
int vertex_offset = 0;
vk_vertex_t *gpu_vertices;
uint16_t *indices;
@ -108,20 +107,17 @@ static void EmitWaterPolys( const cl_entity_t *ent, const msurface_t *warp, qboo
num_indices += triangles * 3;
}
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)
vertex_buffer = XVK_RenderBufferAllocAndLock( sizeof(vk_vertex_t), num_vertices );
index_buffer = XVK_RenderBufferAllocAndLock( sizeof(uint16_t), num_indices );
if (vertex_buffer.ptr == NULL || index_buffer.ptr == NULL)
{
// TODO should we free one of the above if it still succeeded?
gEngine.Con_Printf(S_ERROR "Ran out of buffer space\n");
return;
}
vertex_lock = VK_RenderBufferLock( vertex_buffer );
index_lock = VK_RenderBufferLock( index_buffer );
gpu_vertices = vertex_lock.ptr;
indices = index_lock.ptr;
gpu_vertices = vertex_buffer.ptr;
indices = index_buffer.ptr;
for( p = warp->polys; p; p = p->next )
{
@ -189,7 +185,8 @@ static void EmitWaterPolys( const cl_entity_t *ent, const msurface_t *warp, qboo
if( reverse )
v -= VERTEXSIZE;
else v += VERTEXSIZE;
else
v += VERTEXSIZE;
}
#ifdef WATER_NORMALS
@ -202,8 +199,8 @@ static void EmitWaterPolys( const cl_entity_t *ent, const msurface_t *warp, qboo
vertex_offset += p->numverts;
}
VK_RenderBufferUnlock( vertex_buffer );
VK_RenderBufferUnlock( index_buffer );
XVK_RenderBufferUnlock( vertex_buffer.buffer );
XVK_RenderBufferUnlock( index_buffer.buffer );
// Render
{
@ -212,12 +209,10 @@ static void EmitWaterPolys( const cl_entity_t *ent, const msurface_t *warp, qboo
.material = kXVkMaterialWater,
.vertex_count = num_vertices,
.vertex_buffer = vertex_buffer,
.vertex_offset = 0,
.vertex_offset = vertex_buffer.buffer.unit.offset,
.element_count = num_indices,
.index_offset = 0,
.index_buffer = index_buffer,
.index_offset = index_buffer.buffer.unit.offset,
};
VK_RenderModelDynamicAddGeometry( &geometry );
@ -432,25 +427,22 @@ static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) {
vk_brush_model_t *bmodel = mod->cache.data;
uint32_t vertex_offset = 0;
int num_geometries = 0;
vk_buffer_handle_t vertex_buffer, index_buffer;
vk_buffer_lock_t vertex_lock, index_lock;
xvk_render_buffer_allocation_t vertex_buffer, index_buffer;
vk_vertex_t *bvert = NULL;
uint16_t *bind = NULL;
uint32_t index_offset = 0;
vertex_buffer = VK_RenderBufferAlloc( sizeof(vk_vertex_t), sizes.num_vertices, LifetimeMap );
index_buffer = VK_RenderBufferAlloc( sizeof(uint16_t), sizes.num_indices, LifetimeMap );
if (vertex_buffer == InvalidHandle || index_buffer == InvalidHandle)
{
// TODO should we free one of the above if it still succeeded?
vertex_buffer = XVK_RenderBufferAllocAndLock( sizeof(vk_vertex_t), sizes.num_vertices );
index_buffer = XVK_RenderBufferAllocAndLock( sizeof(uint16_t), sizes.num_indices );
if (vertex_buffer.ptr == NULL || index_buffer.ptr == NULL) {
gEngine.Con_Printf(S_ERROR "Ran out of buffer space\n");
return false;
}
vertex_lock = VK_RenderBufferLock( vertex_buffer );
index_lock = VK_RenderBufferLock( index_buffer );
bvert = vertex_lock.ptr;
bind = index_lock.ptr;
bvert = vertex_buffer.ptr;
bind = index_buffer.ptr;
index_offset = index_buffer.buffer.unit.offset;
// Load sorted by gl_texturenum
for (int t = 0; t <= sizes.max_texture_id; ++t)
@ -484,11 +476,9 @@ static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) {
model_geometry->surf = surf;
model_geometry->texture = t;
model_geometry->vertex_buffer = vertex_buffer;
model_geometry->vertex_offset = vertex_offset;
model_geometry->vertex_offset = vertex_buffer.buffer.unit.offset + vertex_offset;
model_geometry->vertex_count = surf->numedges;
model_geometry->index_buffer = index_buffer;
model_geometry->index_offset = index_offset;
if( FBitSet( surf->flags, SURF_DRAWSKY )) {
@ -553,8 +543,8 @@ static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) {
}
}
VK_RenderBufferUnlock( index_buffer );
VK_RenderBufferUnlock( vertex_buffer );
XVK_RenderBufferUnlock( index_buffer.buffer );
XVK_RenderBufferUnlock( vertex_buffer.buffer );
ASSERT(sizes.num_surfaces == num_geometries);
bmodel->render_model.num_geometries = num_geometries;

View File

@ -425,7 +425,7 @@ void R_EndFrame( void )
// 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();
XVK_RenderBufferFrameClear();
g_frame.swapchain_image_index = -1;
}

View File

@ -179,8 +179,6 @@ vk_ray_model_t* VK_RayModelCreate( vk_ray_model_init_t args ) {
for (int i = 0; i < args.model->num_geometries; ++i) {
vk_render_geometry_t *mg = args.model->geometries + i;
const uint32_t prim_count = mg->element_count / 3;
const uint32_t vertex_offset = mg->vertex_offset + VK_RenderBufferGetOffsetInUnits(mg->vertex_buffer);
const uint32_t index_offset = mg->index_buffer == InvalidHandle ? UINT32_MAX : (mg->index_offset + VK_RenderBufferGetOffsetInUnits(mg->index_buffer));
max_prims += prim_count;
geom_max_prim_counts[i] = prim_count;
@ -192,7 +190,7 @@ vk_ray_model_t* VK_RayModelCreate( vk_ray_model_init_t args ) {
.geometry.triangles =
(VkAccelerationStructureGeometryTrianglesDataKHR){
.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR,
.indexType = mg->index_buffer == InvalidHandle ? VK_INDEX_TYPE_NONE_KHR : VK_INDEX_TYPE_UINT16,
.indexType = VK_INDEX_TYPE_UINT16,
.maxVertex = mg->vertex_count,
.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT,
.vertexStride = sizeof(vk_vertex_t),
@ -209,12 +207,12 @@ vk_ray_model_t* VK_RayModelCreate( vk_ray_model_init_t args ) {
geom_build_ranges[i] = (VkAccelerationStructureBuildRangeInfoKHR) {
.primitiveCount = prim_count,
.primitiveOffset = index_offset == UINT32_MAX ? 0 : index_offset * sizeof(uint16_t),
.firstVertex = vertex_offset,
.primitiveOffset = mg->index_offset * sizeof(uint16_t),
.firstVertex = mg->vertex_offset,
};
kusochki[i].vertex_offset = vertex_offset;
kusochki[i].index_offset = index_offset;
kusochki[i].vertex_offset = mg->vertex_offset;
kusochki[i].index_offset = mg->index_offset;
kusochki[i].triangles = prim_count;
kusochki[i].texture = mg->texture;

View File

@ -23,15 +23,6 @@ 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
@ -40,15 +31,11 @@ static struct {
VkPipeline pipelines[kRenderTransAdd + 1];
vk_buffer_t buffer;
vk_ring_buffer_t buffer_alloc;
vk_ring_buffer_t buffer_alloc_ring;
vk_buffer_t uniform_buffer;
uint32_t ubo_align;
vk_buffer_alloc_t allocs[MAX_ALLOCS];
int allocs_free[MAX_ALLOCS];
int num_free_allocs;
struct {
vec3_t origin, color;
} static_lights[32];
@ -233,14 +220,6 @@ 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;
}
}
typedef struct {
uint32_t num_lights, pad[3];
struct {
@ -251,8 +230,7 @@ typedef struct {
qboolean VK_RenderInit( void )
{
// TODO Better estimates
const uint32_t vertex_buffer_size = MAX_BUFFER_VERTICES * sizeof(float) * (3 + 3 + 2 + 2);
const uint32_t vertex_buffer_size = MAX_BUFFER_VERTICES * sizeof(vk_vertex_t);
const uint32_t index_buffer_size = MAX_BUFFER_INDICES * sizeof(uint16_t);
uint32_t uniform_unit_size;
@ -305,9 +283,7 @@ qboolean VK_RenderInit( void )
if (!createPipelines())
return false;
resetAllocFreeList();
g_render.buffer_alloc.size = g_render.buffer.size;
g_render.buffer_alloc_ring.size = g_render.buffer.size;
return true;
}
@ -322,123 +298,51 @@ void VK_RenderShutdown( void )
destroyBuffer( &g_render.uniform_buffer );
}
vk_buffer_handle_t VK_RenderBufferAlloc( uint32_t unit_size, uint32_t count, vk_lifetime_t lifetime )
{
xvk_render_buffer_allocation_t XVK_RenderBufferAllocAndLock( uint32_t unit_size, uint32_t count ) {
const uint32_t alloc_size = unit_size * count;
uint32_t offset;
vk_buffer_alloc_t *alloc;
vk_buffer_handle_t handle = InvalidHandle;
xvk_render_buffer_allocation_t retval = {0};
// FIXME long lifetimes are not supported yet
ASSERT(lifetime != LifetimeLong);
ASSERT(unit_size > 0);
if (!g_render.num_free_allocs) {
gEngine.Con_Printf(S_ERROR "Cannot allocate buffer, allocs count exhausted\n" );
return InvalidHandle;
}
offset = VK_RingBuffer_Alloc(&g_render.buffer_alloc, alloc_size, unit_size);
offset = VK_RingBuffer_Alloc(&g_render.buffer_alloc_ring, alloc_size, unit_size);
if (offset == AllocFailed) {
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_alloc.free);
return InvalidHandle;
alloc_size, unit_size, g_render.buffer_alloc_ring.free);
return retval;
}
// 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);
retval.buffer.unit.size = unit_size;
retval.buffer.unit.count = count;
retval.buffer.unit.offset = offset / unit_size;
retval.ptr = ((byte*)g_render.buffer.mapped) + offset;
alloc->buffer_offset_in_units = offset / unit_size;
alloc->unit_size = unit_size;
alloc->lifetime = lifetime;
alloc->count = count;
return handle;
return retval;
}
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;
void XVK_RenderBufferUnlock( xvk_render_buffer_t handle ) {
// TODO check whether we need to upload something from staging, etc
}
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;
void XVK_RenderBufferMapFreeze( void ) {
VK_RingBuffer_Fix(&g_render.buffer_alloc_ring);
}
void VK_RenderBufferUnlock( vk_buffer_handle_t handle )
{
vk_buffer_alloc_t *alloc = getBufferFromHandle( handle );
ASSERT(alloc->locked);
alloc->locked = false;
// TODO upload from staging to gpumem
}
uint32_t VK_RenderBufferGetOffsetInUnits( vk_buffer_handle_t handle )
{
const vk_buffer_alloc_t *alloc = getBufferFromHandle( handle );
return alloc->buffer_offset_in_units;
}
// Free all LifetimeSingleFrame resources
void VK_RenderBufferClearFrame( void )
{
VK_RingBuffer_ClearFrame(&g_render.buffer_alloc);
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 )
{
VK_RingBuffer_Clear(&g_render.buffer_alloc);
void XVK_RenderBufferMapClear( void ) {
VK_RingBuffer_Clear(&g_render.buffer_alloc_ring);
g_render.num_static_lights = 0;
resetAllocFreeList();
}
void VK_RenderMapLoadEnd( void )
{
VK_RingBuffer_Fix(&g_render.buffer_alloc);
void XVK_RenderBufferFrameClear( /*int frame_id*/void ) {
VK_RingBuffer_ClearFrame(&g_render.buffer_alloc_ring);
}
void VK_RenderBufferPrintStats( void )
{
void XVK_RenderBufferPrintStats( void ) {
// TODO get alignment holes size
gEngine.Con_Reportf("Buffer usage: %uKiB of (%uKiB)\n",
g_render.buffer_alloc.permanent_size / 1024,
g_render.buffer_alloc_ring.permanent_size / 1024,
g_render.buffer.size / 1024);
}
@ -450,7 +354,6 @@ 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;
/* TODO this should be a separate thing? */ struct { float r, g, b; } emissive;
} render_draw_t;
@ -564,7 +467,6 @@ static uint32_t allocUniform( uint32_t size, uint32_t alignment ) {
static void VK_RenderScheduleDraw( const render_draw_t *draw )
{
const vk_buffer_alloc_t *vertex_buffer = NULL, *index_buffer = NULL;
draw_command_t *draw_command;
ASSERT(draw->render_mode >= 0);
@ -572,20 +474,6 @@ static void VK_RenderScheduleDraw( const render_draw_t *draw )
ASSERT(draw->lightmap >= 0);
ASSERT(draw->texture >= 0);
{
vertex_buffer = getBufferFromHandle(draw->vertex_buffer);
ASSERT(vertex_buffer);
ASSERT(!vertex_buffer->locked);
}
// Index buffer is optional
if (draw->index_buffer != InvalidHandle)
{
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;
@ -711,9 +599,6 @@ 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)
{
@ -738,12 +623,9 @@ 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_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 {
vkCmdDraw(vk_core.cb, draw->draw.element_count, 1, vertex_offset, 0);
}
// Only indexed mode is supported
ASSERT(draw->draw.index_offset >= 0);
vkCmdDrawIndexed(vk_core.cb, draw->draw.element_count, 1, draw->draw.index_offset, draw->draw.vertex_offset, 0);
}
}
@ -847,32 +729,35 @@ void VK_RenderModelDestroy( vk_render_model_t* model ) {
void VK_RenderModelDraw( vk_render_model_t* model ) {
int current_texture = -1;
int index_count = 0;
int element_count = 0;
int index_offset = -1;
int vertex_offset = 0;
vk_buffer_handle_t vertex_buffer = InvalidHandle;
vk_buffer_handle_t index_buffer = InvalidHandle;
if (g_render_state.current_frame_is_ray_traced) {
VK_RayFrameAddModel(model->ray_model, model, (const matrix3x4*)g_render_state.model);
return;
}
for (int i = 0; i < model->num_geometries; ++i) {
const vk_render_geometry_t *geom = model->geometries + i;
const qboolean split = current_texture != geom->texture
|| vertex_offset != geom->vertex_offset
|| (index_offset + element_count) != geom->index_offset;
// We only support indexed geometry
ASSERT(geom->index_offset >= 0);
if (geom->texture < 0)
continue;
if (current_texture != geom->texture || vertex_buffer != geom->vertex_buffer || index_buffer != geom->index_buffer || vertex_offset != geom->vertex_offset)
{
if (index_count) {
if (split) {
if (element_count) {
const render_draw_t draw = {
.lightmap = tglob.lightmapTextures[0], // FIXME there can be more than one lightmap textures
.texture = current_texture,
.render_mode = model->render_mode,
.element_count = index_count,
.vertex_buffer = vertex_buffer,
.index_buffer = index_buffer,
.element_count = element_count,
.vertex_offset = vertex_offset,
.index_offset = index_offset,
};
@ -881,28 +766,22 @@ void VK_RenderModelDraw( vk_render_model_t* model ) {
}
current_texture = geom->texture;
vertex_buffer = geom->vertex_buffer;
index_buffer = geom->index_buffer;
index_count = 0;
index_offset = -1;
index_offset = geom->index_offset;
vertex_offset = geom->vertex_offset;
element_count = 0;
}
if (index_offset < 0)
index_offset = geom->index_offset;
// Make sure that all surfaces are concatenated in buffers
ASSERT(index_offset + index_count == geom->index_offset);
index_count += geom->element_count;
ASSERT(index_offset + element_count == geom->index_offset);
element_count += geom->element_count;
}
if (index_count) {
if (element_count) {
const render_draw_t draw = {
.lightmap = tglob.lightmapTextures[0],
.texture = current_texture,
.render_mode = model->render_mode,
.element_count = index_count,
.vertex_buffer = vertex_buffer,
.index_buffer = index_buffer,
.element_count = element_count,
.vertex_offset = vertex_offset,
.index_offset = index_offset,
};

View File

@ -6,40 +6,34 @@
qboolean VK_RenderInit( void );
void VK_RenderShutdown( void );
typedef int vk_buffer_handle_t; // -1 == invalid handle
enum { InvalidHandle = -1 };
// General buffer usage pattern
// 1. alloc (allocates buffer mem, stores allocation data)
// 2. (returns void* buf and handle) write to buf
// 3. upload and lock (ensures that all this data is in gpu mem, e.g. uploads from staging)
// 4. ... use it
// 5. free (frame/map end)
typedef struct {
struct {
uint32_t size; // single unit size in bytes
uint32_t offset; // offset in units from start of vulkan buffer
uint32_t count; // number of units in this allocation
} unit;
} xvk_render_buffer_t;
typedef struct {
void *ptr;
uint32_t unit_size, count;
} vk_buffer_lock_t;
xvk_render_buffer_t buffer;
} xvk_render_buffer_allocation_t;
typedef enum {
LifetimeLong,
LifetimeMap,
LifetimeSingleFrame,
} vk_lifetime_t;
xvk_render_buffer_allocation_t XVK_RenderBufferAllocAndLock( uint32_t unit_size, uint32_t count );
void XVK_RenderBufferUnlock( xvk_render_buffer_t handle );
void XVK_RenderBufferMapFreeze( void ); // Permanently freeze all allocations as map-permanent
void XVK_RenderBufferMapClear( void ); // Free the entire buffer for a new map
// TODO: allocation lifetime with contents validity lifetime?
void XVK_RenderBufferFrameClear( /*int frame_id*/void ); // mark data for frame with given id as free (essentially, just forward the ring buffer)
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 );
uint32_t VK_RenderBufferGetOffsetInUnits( 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 );
// Mark map as loaded
void VK_RenderMapLoadEnd( void );
// TODO uploading to GPU mem interface
void VK_RenderBufferPrintStats( void );
void XVK_RenderBufferPrintStats( 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
@ -75,8 +69,7 @@ typedef enum {
} XVkMaterialType;
typedef struct {
vk_buffer_handle_t index_buffer, vertex_buffer;
uint32_t index_offset, vertex_offset;
int index_offset, vertex_offset;
// TODO can be dynamic
int texture;

View File

@ -107,7 +107,7 @@ void R_NewMap( void )
// TODO should we do something like VK_BrushBeginLoad?
VK_BrushStatsClear();
VK_RenderBufferClearMap();
XVK_RenderBufferMapClear();
if (vk_core.rtx)
VK_RayNewMap();
@ -157,10 +157,10 @@ void R_NewMap( void )
// TODO should we do something like VK_BrushEndLoad?
VK_UploadLightmap();
VK_RenderMapLoadEnd();
XVK_RenderBufferMapFreeze();
XVK_RenderBufferPrintStats();
if (vk_core.rtx)
VK_RayMapLoadEnd();
VK_RenderBufferPrintStats();
}
qboolean R_AddEntity( struct cl_entity_s *clent, int type )

View File

@ -649,25 +649,22 @@ 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_handle_t vertex_buffer, index_buffer;
vk_buffer_lock_t vertex_lock, index_lock;
xvk_render_buffer_allocation_t vertex_buffer, index_buffer;
vk_vertex_t *dst_vtx;
uint16_t *dst_idx;
// Get buffer region for vertices and indices
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)
vertex_buffer = XVK_RenderBufferAllocAndLock( sizeof(vk_vertex_t), 4 );
index_buffer = XVK_RenderBufferAllocAndLock( sizeof(uint16_t), 6 );
if (!vertex_buffer.ptr || !index_buffer.ptr)
{
// TODO should we free one of the above if it still succeeded?
gEngine.Con_Printf(S_ERROR "Ran out of buffer space\n");
return;
}
vertex_lock = VK_RenderBufferLock( vertex_buffer );
index_lock = VK_RenderBufferLock( index_buffer );
dst_vtx = vertex_lock.ptr;
dst_idx = index_lock.ptr;
dst_vtx = vertex_buffer.ptr;
dst_idx = index_buffer.ptr;
// FIXME VK r_stats.c_sprite_polys++;
@ -702,8 +699,8 @@ 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 );
XVK_RenderBufferUnlock( index_buffer.buffer );
XVK_RenderBufferUnlock( vertex_buffer.buffer );
{
const vk_render_geometry_t geometry = {
@ -711,12 +708,10 @@ static void R_DrawSpriteQuad( mspriteframe_t *frame, vec3_t org, vec3_t v_right,
.material = kXVkMaterialDiffuse,
.vertex_count = 4,
.vertex_buffer = vertex_buffer,
.vertex_offset = 0,
.vertex_offset = vertex_buffer.buffer.unit.offset,
.element_count = 6,
.index_offset = 0,
.index_buffer = index_buffer,
.index_offset = index_buffer.buffer.unit.offset,
};
VK_RenderModelDynamicBegin( "sprite" /* TODO its name */, render_mode );

View File

@ -1902,8 +1902,7 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float
float *lv;
int i;
int num_vertices = 0, num_indices = 0;
vk_buffer_handle_t vertex_buffer, index_buffer;
vk_buffer_lock_t vertex_lock, index_lock;
xvk_render_buffer_allocation_t vertex_buffer, index_buffer;
vk_vertex_t *dst_vtx;
uint16_t *dst_idx;
uint32_t vertex_offset = 0, index_offset = 0;
@ -1924,19 +1923,17 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float
ASSERT(num_indices > 0);
// Get buffer region for vertices and indices
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)
vertex_buffer = XVK_RenderBufferAllocAndLock( sizeof(vk_vertex_t), num_vertices );
index_buffer = XVK_RenderBufferAllocAndLock( sizeof(uint16_t), num_indices );
if (vertex_buffer.ptr == NULL || index_buffer.ptr == NULL)
{
// TODO should we free one of the above if it still succeeded?
gEngine.Con_Printf(S_ERROR "Ran out of buffer space\n");
return;
}
vertex_lock = VK_RenderBufferLock( vertex_buffer );
index_lock = VK_RenderBufferLock( index_buffer );
dst_vtx = vertex_lock.ptr;
dst_idx = index_lock.ptr;
dst_vtx = vertex_buffer.ptr;
dst_idx = index_buffer.ptr;
// Restore ptricmds and upload vertices
ptricmds = ptricmds_initial;
@ -1948,7 +1945,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*)vertex_lock.ptr) + vertex_lock.count) > dst_vtx);
ASSERT((((vk_vertex_t*)vertex_buffer.ptr) + num_vertices) > dst_vtx);
VectorCopy(g_studio.verts[ptricmds[0]], dst_vtx->pos);
VectorCopy(g_studio.norms[ptricmds[0]], dst_vtx->normal);
@ -1997,8 +1994,8 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float
ASSERT(index_offset == num_indices);
ASSERT(vertex_offset == num_vertices);
VK_RenderBufferUnlock( index_buffer );
VK_RenderBufferUnlock( vertex_buffer );
XVK_RenderBufferUnlock( index_buffer.buffer );
XVK_RenderBufferUnlock( vertex_buffer.buffer );
// Render
{
@ -2007,12 +2004,10 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float
.texture = texture,
.material = kXVkMaterialDiffuse,
.vertex_buffer = vertex_buffer,
.vertex_offset = 0,
.vertex_offset = vertex_buffer.buffer.unit.offset,
.vertex_count = num_vertices,
.index_buffer = index_buffer,
.index_offset = 0,
.index_offset = index_buffer.buffer.unit.offset,
.element_count = num_indices,
};