move buffer allocation to vk_render.c from vk_bruh.c

This commit is contained in:
Ivan Avdeev 2021-02-10 10:33:44 -08:00
parent ad5d515200
commit 5ebe36c54c
9 changed files with 211 additions and 102 deletions

View File

@ -1,10 +1,10 @@
## 2021-02-08
- [ ] move entity rendering-enumeration into vk_scene
- [ ] refactor brush into brushes and separate rendering/buffer management
## 2021-02-10
- [x] refactor brush into brushes and separate rendering/buffer management
# Next
- [ ] draw studio models as bounding boxes
- [ ] studio models
- [ ] animated textures (accept PR)
# Planned
- [ ] move all consts to vk_const
@ -16,11 +16,12 @@
- [ ] issue: transparent brushes are too transparent (train ride)
- [ ] render skybox
- [ ] mipmaps
- [ ] animated textures
- [ ] lightmap dynamic styles
- [ ] flashlight
- [ ] screenshot
- [ ] fog
- [ ] RTX
- [ ] studio models survive NewMap; need to compactify buffers after removing all brushes
# Someday
- [ ] (helps with RTX?) unified rendering (brush/studio models/...), each model is instance, instance data is read from storage buffers, gives info about vertex format, texture bindings, etc; which are read from another set of storage buffers, ..
@ -42,3 +43,6 @@
- [x] swapchain getting stale
- [x] HUD sprites
- [x] issue: lightmap sometimes gets corrupted on map load
## 2021-02-08
- [x] move entity rendering-enumeration into vk_scene

View File

@ -17,10 +17,6 @@
#include <math.h>
#include <memory.h>
// TODO count these properly
#define MAX_BUFFER_VERTICES (1 * 1024 * 1024)
#define MAX_BUFFER_INDICES (MAX_BUFFER_VERTICES * 3)
typedef struct brush_vertex_s
{
vec3_t pos;
@ -47,34 +43,13 @@ typedef struct vk_brush_model_s {
} vk_brush_model_t;
static struct {
// TODO merge these into a single buffer
vk_buffer_t vertex_buffer;
uint32_t num_vertices;
vk_buffer_t index_buffer;
uint32_t num_indices;
vk_buffer_t uniform_buffer;
uint32_t uniform_unit_size;
struct {
int num_vertices, num_indices;
} stat;
VkPipelineLayout pipeline_layout;
VkPipeline pipelines[kRenderTransAdd + 1];
} g_brush;
/* static brush_vertex_t *allocVertices(int num_vertices) { */
/* if (num_vertices + g_brush.num_vertices > MAX_BUFFER_VERTICES) */
/* { */
/* gEngine.Con_Printf(S_ERROR "Ran out of buffer vertex space\n"); */
/* return NULL; */
/* } */
/* } */
uniform_data_t *getUniformSlot(int index)
{
ASSERT(index >= 0);
ASSERT(index < MAX_UNIFORM_SLOTS);
return (uniform_data_t*)(((uint8_t*)g_brush.uniform_buffer.mapped) + (g_brush.uniform_unit_size * index));
}
static qboolean createPipelines( void )
{
@ -237,44 +212,9 @@ static qboolean createPipelines( void )
qboolean VK_BrushInit( void )
{
const uint32_t vertex_buffer_size = MAX_BUFFER_VERTICES * sizeof(brush_vertex_t);
const uint32_t index_buffer_size = MAX_BUFFER_INDICES * sizeof(uint16_t);
const uint32_t ubo_align = Q_max(4, vk_core.physical_device.properties.limits.minUniformBufferOffsetAlignment);
g_brush.uniform_unit_size = ((sizeof(uniform_data_t) + ubo_align - 1) / ubo_align) * ubo_align;
// TODO device memory and friends (e.g. handle mobile memory ...)
if (!createBuffer(&g_brush.vertex_buffer, vertex_buffer_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
return false;
if (!createBuffer(&g_brush.index_buffer, index_buffer_size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
return false;
if (!createBuffer(&g_brush.uniform_buffer, g_brush.uniform_unit_size * MAX_UNIFORM_SLOTS, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
return false;
if (!createPipelines())
return false;
{
VkDescriptorBufferInfo dbi = {
.buffer = g_brush.uniform_buffer.buffer,
.offset = 0,
.range = sizeof(uniform_data_t),
};
VkWriteDescriptorSet wds[] = { {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
.pBufferInfo = &dbi,
.dstSet = vk_core.descriptor_pool.ubo_sets[0], // FIXME
}};
vkUpdateDescriptorSets(vk_core.device, ARRAYSIZE(wds), wds, 0, NULL);
}
return true;
}
@ -285,17 +225,12 @@ void VK_BrushShutdown( void )
for (int i = 0; i < ARRAYSIZE(g_brush.pipelines); ++i)
vkDestroyPipeline(vk_core.device, g_brush.pipelines[i], NULL);
vkDestroyPipelineLayout( vk_core.device, g_brush.pipeline_layout, NULL );
destroyBuffer( &g_brush.vertex_buffer );
destroyBuffer( &g_brush.index_buffer );
destroyBuffer( &g_brush.uniform_buffer );
}
void VK_BrushDrawModel( const model_t *mod, int render_mode, int ubo_index )
{
// Expect all buffers to be bound
const vk_brush_model_t *bmodel = mod->cache.data;
const uint32_t dynamic_offset[] = { g_brush.uniform_unit_size * ubo_index };
int current_texture = -1;
int index_count = 0;
int index_offset = -1;
@ -322,7 +257,7 @@ void VK_BrushDrawModel( const model_t *mod, int render_mode, int ubo_index )
vkCmdBindPipeline(vk_core.cb, VK_PIPELINE_BIND_POINT_GRAPHICS, g_brush.pipelines[fixme_current_pipeline_index]);
}
vkCmdBindDescriptorSets(vk_core.cb, VK_PIPELINE_BIND_POINT_GRAPHICS, g_brush.pipeline_layout, 0, 1, vk_core.descriptor_pool.ubo_sets, ARRAYSIZE(dynamic_offset), dynamic_offset);
VK_RenderBindUniformBufferWithIndex( g_brush.pipeline_layout, ubo_index);
for (int i = 0; i < bmodel->num_surfaces; ++i) {
const vk_brush_model_surface_t *bsurf = bmodel->surfaces + i;
@ -358,10 +293,7 @@ void VK_BrushDrawModel( const model_t *mod, int render_mode, int ubo_index )
qboolean VK_BrushRenderBegin( void )
{
const VkDeviceSize offset = 0;
vkCmdBindVertexBuffers(vk_core.cb, 0, 1, &g_brush.vertex_buffer.buffer, &offset);
vkCmdBindIndexBuffer(vk_core.cb, g_brush.index_buffer.buffer, 0, VK_INDEX_TYPE_UINT16);
VK_RenderBindBuffers(); // TODO in scene?
if (!tglob.lightmapTextures[0])
{
gEngine.Con_Printf( S_ERROR "Don't have a lightmap texture\n");
@ -376,10 +308,13 @@ qboolean VK_BrushRenderBegin( void )
}
static int loadBrushSurfaces( const model_t *mod, vk_brush_model_surface_t *out_surfaces) {
brush_vertex_t *bvert = g_brush.vertex_buffer.mapped;
uint16_t *bind = g_brush.index_buffer.mapped;
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;
brush_vertex_t *bvert = NULL;
uint16_t *bind = NULL;
uint32_t index_offset = 0;
int num_indices = 0, num_vertices = 0, max_texture_id = 0;
for( int i = 0; i < mod->nummodelsurfaces; ++i)
@ -403,6 +338,27 @@ 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(brush_vertex_t), num_vertices );
bvert = vertex_alloc.ptr;
bmodel->vertex_offset = vertex_alloc.buffer_offset_in_units;
if (!bvert)
{
gEngine.Con_Printf(S_ERROR "Ran out of buffer vertex 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;
if (!bind)
{
gEngine.Con_Printf(S_ERROR "Ran out of buffer index space\n");
return -1;
}
g_brush.stat.num_indices += num_indices;
g_brush.stat.num_vertices += num_vertices;
// Load sorted by gl_texturenum
for (int t = 0; t <= max_texture_id; ++t)
{
@ -426,17 +382,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 (surf->numedges + g_brush.num_vertices > MAX_BUFFER_VERTICES)
{
gEngine.Con_Printf(S_ERROR "Ran out of buffer vertex space\n");
return -1;
}
if ((surf->numedges-1) * 3 + g_brush.num_indices > MAX_BUFFER_INDICES)
{
gEngine.Con_Printf(S_ERROR "Ran out of buffer index space\n");
return -1;
}
if (vertex_offset + surf->numedges >= UINT16_MAX)
{
@ -445,7 +390,7 @@ static int loadBrushSurfaces( const model_t *mod, vk_brush_model_surface_t *out_
}
bsurf->texture_num = surf->texinfo->texture->gl_texturenum;
bsurf->index_offset = g_brush.num_indices;
bsurf->index_offset = index_offset;
bsurf->index_count = 0;
VK_CreateSurfaceLightmap( surf, mod );
@ -486,14 +431,15 @@ static int loadBrushSurfaces( const model_t *mod, vk_brush_model_surface_t *out_
//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[g_brush.num_vertices++] = vertex;
*(bvert++) = vertex;
// TODO contemplate triangle_strip (or fan?) + primitive restart
if (k > 1) {
bind[g_brush.num_indices++] = (uint16_t)(vertex_offset + 0);
bind[g_brush.num_indices++] = (uint16_t)(vertex_offset + k - 1);
bind[g_brush.num_indices++] = (uint16_t)(vertex_offset + k);
*(bind++) = (uint16_t)(vertex_offset + 0);
*(bind++) = (uint16_t)(vertex_offset + k - 1);
*(bind++) = (uint16_t)(vertex_offset + k);
bsurf->index_count += 3;
index_offset += 3;
}
}
@ -519,7 +465,6 @@ qboolean VK_LoadBrushModel( model_t *mod, const byte *buffer )
bmodel = Mem_Malloc(vk_core.pool, sizeof(vk_brush_model_t) + sizeof(vk_brush_model_surface_t) * mod->nummodelsurfaces);
mod->cache.data = bmodel;
bmodel->vertex_offset = g_brush.num_vertices;
bmodel->num_surfaces = loadBrushSurfaces( mod, bmodel->surfaces );
if (bmodel->num_surfaces < 0) {
gEngine.Con_Reportf( S_ERROR "Model %s was not loaded\n", mod->name );
@ -535,13 +480,13 @@ qboolean VK_LoadBrushModel( model_t *mod, const byte *buffer )
/* 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.num_vertices, g_brush.num_indices);
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;
}
void VK_BrushClear( void )
{
// Free previous map data
g_brush.num_vertices = 0;
g_brush.num_indices = 0;
g_brush.stat.num_vertices = 0;
g_brush.stat.num_indices = 0;
}

View File

@ -22,6 +22,8 @@ qboolean createBuffer(vk_buffer_t *buf, uint32_t size, VkBufferUsageFlags usage,
// FIXME when there are many allocation per VkDeviceMemory, fix this
XVK_CHECK(vkMapMemory(vk_core.device, buf->device_memory.device_memory, 0, bci.size, 0, &buf->mapped));
buf->size = size;
return true;
}

View File

@ -30,5 +30,7 @@
++called; \
} while(0)
#define ALIGN_UP(ptr, align) ((((ptr) + (align) - 1) / (align)) * (align))
extern ref_api_t gEngine;
extern ref_globals_t *gpGlobals;

View File

@ -2,3 +2,7 @@
#define MAX_SCENE_STACK 2
#define MAX_SCENE_ENTITIES 2048
// TODO count these properly
#define MAX_BUFFER_VERTICES (1 * 1024 * 1024)
#define MAX_BUFFER_INDICES (MAX_BUFFER_VERTICES * 3)

View File

@ -9,6 +9,7 @@
#include "vk_scene.h"
#include "vk_cvar.h"
#include "vk_pipeline.h"
#include "vk_render.h"
#include "xash3d_types.h"
#include "cvardef.h"
@ -579,6 +580,9 @@ qboolean R_VkInit( void )
VK_LoadCvars();
if (!VK_RenderInit())
return false;
VK_SceneInit();
initTextures();
@ -606,6 +610,8 @@ void R_VkShutdown( void )
destroyTextures();
VK_RenderShutdown();
VK_PipelineShutdown();
vkDestroyDescriptorPool(vk_core.device, vk_core.descriptor_pool.pool, NULL);

123
ref_vk/vk_render.c Normal file
View File

@ -0,0 +1,123 @@
#include "vk_render.h"
#include "vk_core.h"
#include "vk_buffer.h"
#include "vk_const.h"
#include "vk_common.h"
#include "eiface.h"
#include "xash3d_mathlib.h"
static struct {
vk_buffer_t buffer;
uint32_t buffer_free_offset;
vk_buffer_t uniform_buffer;
uint32_t uniform_unit_size;
struct {
int align_holes_size;
} stat;
} g_render;
static struct {
VkPipeline current_pipeline;
} g_render_state;
uniform_data_t *VK_RenderGetUniformSlot(int index)
{
ASSERT(index >= 0);
ASSERT(index < MAX_UNIFORM_SLOTS);
return (uniform_data_t*)(((uint8_t*)g_render.uniform_buffer.mapped) + (g_render.uniform_unit_size * index));
}
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 index_buffer_size = MAX_BUFFER_INDICES * sizeof(uint16_t);
const uint32_t ubo_align = Q_max(4, vk_core.physical_device.properties.limits.minUniformBufferOffsetAlignment);
g_render.uniform_unit_size = ((sizeof(uniform_data_t) + ubo_align - 1) / ubo_align) * ubo_align;
// TODO device memory and friends (e.g. handle mobile memory ...)
if (!createBuffer(&g_render.buffer, vertex_buffer_size + index_buffer_size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
return false;
if (!createBuffer(&g_render.uniform_buffer, g_render.uniform_unit_size * MAX_UNIFORM_SLOTS, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
return false;
{
VkDescriptorBufferInfo dbi = {
.buffer = g_render.uniform_buffer.buffer,
.offset = 0,
.range = sizeof(uniform_data_t),
};
VkWriteDescriptorSet wds[] = { {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
.pBufferInfo = &dbi,
.dstSet = vk_core.descriptor_pool.ubo_sets[0], // FIXME
}};
vkUpdateDescriptorSets(vk_core.device, ARRAYSIZE(wds), wds, 0, NULL);
}
return true;
}
void VK_RenderShutdown( void )
{
destroyBuffer( &g_render.buffer );
destroyBuffer( &g_render.uniform_buffer );
}
void VK_RenderBindBuffers( void )
{
const VkDeviceSize offset = 0;
vkCmdBindVertexBuffers(vk_core.cb, 0, 1, &g_render.buffer.buffer, &offset);
vkCmdBindIndexBuffer(vk_core.cb, g_render.buffer.buffer, 0, VK_INDEX_TYPE_UINT16);
}
void VK_RenderBindUniformBufferWithIndex( VkPipelineLayout pipeline_layout, int index )
{
const uint32_t dynamic_offset[] = { g_render.uniform_unit_size * index };
vkCmdBindDescriptorSets(vk_core.cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, vk_core.descriptor_pool.ubo_sets, ARRAYSIZE(dynamic_offset), dynamic_offset);
}
vk_buffer_alloc_t VK_RenderBufferAlloc( uint32_t unit_size, uint32_t count )
{
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};
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};
}
ret.buffer_offset_in_units = offset / unit_size;
ret.ptr = g_render.buffer.mapped + offset;
g_render.stat.align_holes_size += offset - g_render.buffer_free_offset;
g_render.buffer_free_offset = offset + alloc_size;
return ret;
}
void VK_RenderBufferClearAll( void )
{
g_render.buffer_free_offset = 0;
g_render.stat.align_holes_size = 0;
}
void VK_RenderBufferPrintStats( void )
{
gEngine.Con_Reportf("Buffer usage: %uKiB of (%uKiB); holes: %u bytes\n",
g_render.buffer_free_offset / 1024,
g_render.buffer.size / 1024,
g_render.stat.align_holes_size);
}

View File

@ -1,6 +1,7 @@
#pragma once
#include "vk_common.h"
#include "vk_const.h"
#include "vk_core.h"
typedef struct {
matrix4x4 mvp;
@ -9,4 +10,23 @@ typedef struct {
#define MAX_UNIFORM_SLOTS (MAX_SCENE_ENTITIES * 2 /* solid + trans */ + 1)
uniform_data_t *getUniformSlot(int index);
uniform_data_t *VK_RenderGetUniformSlot(int index);
typedef struct vk_buffer_alloc_s {
uint32_t buffer_offset_in_units;
void *ptr;
} vk_buffer_alloc_t;
// TODO uploading to GPU mem interface
vk_buffer_alloc_t VK_RenderBufferAlloc( uint32_t unit_size, uint32_t count );
void VK_RenderBufferClearAll( void );
qboolean VK_RenderInit( void );
void VK_RenderShutdown( void );
// TODO should this not be global?
void VK_RenderBindBuffers( void );
void VK_RenderBindUniformBufferWithIndex( VkPipelineLayout pipeline_layout, int index );
void VK_RenderBufferPrintStats( void );

View File

@ -78,6 +78,7 @@ void R_NewMap( void )
// TODO should we do something like VK_BrushBeginLoad?
VK_BrushClear();
VK_RenderBufferClearAll();
// Load all models at once
gEngine.Con_Reportf( "Num models: %d:\n", num_models );
@ -100,6 +101,8 @@ void R_NewMap( void )
// TODO should we do something like VK_BrushEndLoad?
VK_UploadLightmap();
VK_RenderBufferPrintStats();
}
qboolean R_AddEntity( struct cl_entity_s *clent, int type )
@ -490,7 +493,7 @@ static int drawEntity( cl_entity_t *ent, int render_mode, int ubo_index, const m
{
const model_t *mod = ent->model;
matrix4x4 model, ent_mvp;
uniform_data_t *ubo = getUniformSlot(ubo_index);
uniform_data_t *ubo = VK_RenderGetUniformSlot(ubo_index);
float alpha;
if (!mod)
@ -557,7 +560,7 @@ void VK_SceneRender( void )
const model_t *world = gEngine.pfnGetModelByIndex( 1 );
if (world)
{
uniform_data_t *ubo = getUniformSlot(ubo_index);
uniform_data_t *ubo = VK_RenderGetUniformSlot(ubo_index);
Matrix4x4_ToArrayFloatGL( mvp, (float*)ubo->mvp );
Vector4Set(ubo->color, 1.f, 1.f, 1.f, 1.f);
VK_BrushDrawModel( world, kRenderNormal, ubo_index );