vk: ring-based dynamic geometry buf alloc
- Clear split between static and dynamic geometry within the same buffer - Store previous frame geometry data longer - Use simpler ring buffer for allocation - Add waf unit tests for alolcator Known issues: - ray tracing still glitches a lot
This commit is contained in:
parent
68738fdd97
commit
cb427d0839
|
@ -1,3 +1,24 @@
|
|||
# Parallel frames
|
||||
- [ ] allocate for N frames:
|
||||
- [x] geometries
|
||||
- [ ] rt models
|
||||
- [ ] lights
|
||||
|
||||
// O. 1 buffer i bardak v nyom
|
||||
// - [SSSSAAS.SBAS....]
|
||||
// - can become extremely fragmented
|
||||
|
||||
// I. 2 buffer + bit indirection
|
||||
// - lives longer than 2 frames [SSS.SSS..SS.....]
|
||||
// - dynamic [AAAAA->.....<-BBBBBB]
|
||||
// - high bits in shader point to buffer
|
||||
|
||||
// II. 1 buffer bi-directional
|
||||
// - [SSS.SS..S...|AAAABBBB->...]
|
||||
// ^ - long-living "static" stuff
|
||||
// ^ - dynamic ring buffer
|
||||
// - [SSS.SS..S.|.....<-AAAABBBB] (dynamic split)
|
||||
|
||||
# Passes
|
||||
- [ ] better simple sampling
|
||||
- [x] all triangles
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
#include <stdlib.h> // malloc/free
|
||||
#include <string.h> // memcpy
|
||||
|
||||
//#include "vk_common.h"
|
||||
|
||||
#define MALLOC malloc
|
||||
#define FREE free
|
||||
|
||||
|
@ -244,7 +242,60 @@ void aloPoolFree(struct alo_pool_s *pool, int index) {
|
|||
}
|
||||
}
|
||||
|
||||
void aloRingInit(alo_ring_t* ring, uint32_t size) {
|
||||
ring->size = size;
|
||||
ring->head = 0;
|
||||
ring->tail = size;
|
||||
}
|
||||
|
||||
// Marks everything up-to-pos as free (expects up-to-pos to be valid)
|
||||
void aloRingFree(alo_ring_t* ring, uint32_t up_to_pos) {
|
||||
ASSERT(up_to_pos < ring->size);
|
||||
// FIXME assert that up_to_pos is valid and within allocated region
|
||||
if (up_to_pos == ring->head) {
|
||||
ring->head = 0;
|
||||
ring->tail = ring->size;
|
||||
} else
|
||||
ring->tail = up_to_pos;
|
||||
}
|
||||
|
||||
// Allocates a new aligned region and returns offset to it (AllocFailed if allocation failed)
|
||||
uint32_t aloRingAlloc(alo_ring_t* ring, uint32_t size, uint32_t alignment) {
|
||||
const uint32_t align = (alignment > 0) ? alignment : 1;
|
||||
const uint32_t pos = ALIGN_UP(ring->head, alignment);
|
||||
|
||||
ASSERT(size != 0);
|
||||
|
||||
// [XXX.....XXX]
|
||||
// h t
|
||||
if (ring->head <= ring->tail) {
|
||||
if (pos + size > ring->tail)
|
||||
return ALO_ALLOC_FAILED;
|
||||
|
||||
ring->head = pos + size;
|
||||
return pos;
|
||||
}
|
||||
|
||||
// [...XXXXXX...]
|
||||
// t h
|
||||
// 2 1
|
||||
|
||||
// 1. Check if we have enough space immediately in front of head
|
||||
if (pos + size <= ring->size) {
|
||||
ring->head = (pos + size) % ring->size;
|
||||
return pos;
|
||||
}
|
||||
|
||||
// 2. wrap around
|
||||
if (size > ring->tail)
|
||||
return ALO_ALLOC_FAILED;
|
||||
|
||||
ring->head = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(ALOLCATOR_TEST)
|
||||
#include <stdio.h>
|
||||
uint32_t rand_pcg32(uint32_t max) {
|
||||
if (!max) return 0;
|
||||
#define PCG32_INITIALIZER { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL }
|
||||
|
@ -426,11 +477,129 @@ int test(void) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define REQUIRE_EQUAL_UINT32(a, b) \
|
||||
do { \
|
||||
const uint32_t va = (a), vb = (b); \
|
||||
if (va != vb) { \
|
||||
fprintf(stderr, "%s:%d (%s == %s) FAILED: %u != %u\n", \
|
||||
__FILE__, __LINE__, \
|
||||
#a, #b, \
|
||||
va, vb); \
|
||||
} \
|
||||
ASSERT(va == vb); \
|
||||
} while(0)
|
||||
|
||||
static void dumpRing(int line, const alo_ring_t* ring) {
|
||||
fprintf(stderr, "%d ", line);
|
||||
if (ring->tail < ring->head) {
|
||||
fprintf(stderr, "t=%03d h=%03d [", ring->tail, ring->head);
|
||||
for (int i = 0; i < (int)ring->tail; ++i) fputc('.', stderr);
|
||||
fputc('T', stderr);
|
||||
for (int i = (int)ring->tail + 1; i < (int)ring->head; ++i) fputc('#', stderr);
|
||||
fputc('h', stderr);
|
||||
for (int i = (int)ring->head + 1; i < (int)ring->size; ++i) fputc('.', stderr);
|
||||
} else {
|
||||
fprintf(stderr, "h=%03d t=%03d [", ring->head, ring->tail);
|
||||
for (int i = 0; i < (int)ring->head; ++i) fputc('#', stderr);
|
||||
fputc('h', stderr);
|
||||
for (int i = (int)ring->head + 1; i < (int)ring->tail; ++i) fputc('.', stderr);
|
||||
fputc('T', stderr);
|
||||
for (int i = (int)ring->tail + 1; i < (int)ring->size; ++i) fputc('#', stderr);
|
||||
}
|
||||
fputs("]\n", stderr);
|
||||
}
|
||||
|
||||
#define TEST_ALLOC(name, size, expected, alignment) \
|
||||
const uint32_t name = aloRingAlloc(&ring, size, alignment); \
|
||||
dumpRing(__LINE__, &ring); \
|
||||
REQUIRE_EQUAL_UINT32(name, expected)
|
||||
|
||||
#define TEST_FREE(to) \
|
||||
aloRingFree(&ring, to); \
|
||||
dumpRing(__LINE__, &ring)
|
||||
|
||||
void testRing(void) {
|
||||
alo_ring_t ring;
|
||||
aloRingInit(&ring, 128);
|
||||
|
||||
fprintf(stderr, "%s\n", __FUNCTION__);
|
||||
|
||||
TEST_ALLOC(p0, 64, 0, 1);
|
||||
TEST_ALLOC(p1, 64, 64, 1);
|
||||
TEST_ALLOC(p2, 64, ALO_ALLOC_FAILED, 1);
|
||||
TEST_FREE(p1);
|
||||
TEST_ALLOC(p3, 32, 0, 1);
|
||||
TEST_FREE(p3);
|
||||
TEST_ALLOC(p4, 64, 32, 1);
|
||||
TEST_ALLOC(p5, 64, ALO_ALLOC_FAILED, 1);
|
||||
TEST_ALLOC(p6, 16, 96, 1);
|
||||
TEST_ALLOC(p7, 32, ALO_ALLOC_FAILED, 1);
|
||||
TEST_FREE(p4);
|
||||
TEST_ALLOC(p8, 32, 0, 1);
|
||||
}
|
||||
|
||||
void stressTestRing(void) {
|
||||
#define BUFSIZE 128
|
||||
#define NUM_ALLOCS 16
|
||||
const int rounds = 10000;
|
||||
struct { uint32_t pos, size, val; } allocs[NUM_ALLOCS];
|
||||
int count = 0, wr = 0, rd = 0;
|
||||
uint32_t buf[BUFSIZE];
|
||||
|
||||
alo_ring_t ring;
|
||||
aloRingInit(&ring, BUFSIZE);
|
||||
|
||||
for (int i = 0; i < NUM_ALLOCS; ++i)
|
||||
allocs[i].pos = ALO_ALLOC_FAILED;
|
||||
|
||||
fprintf(stderr, "%s\n", __FUNCTION__);
|
||||
|
||||
for (int i = 0; i < rounds; ++i) {
|
||||
if (count < NUM_ALLOCS) {
|
||||
const uint32_t align = 1 << rand_pcg32(5);
|
||||
const uint32_t size = 1 + rand_pcg32(BUFSIZE / 5);
|
||||
const uint32_t pos = aloRingAlloc(&ring, size, align);
|
||||
fprintf(stderr, "ALLOC(%d;%d) size=%d align=%d => pos=%d\n", wr, count, size, align, pos);
|
||||
|
||||
if (pos != ALO_ALLOC_FAILED) {
|
||||
dumpRing(__LINE__, &ring);
|
||||
allocs[wr].pos = pos;
|
||||
allocs[wr].size = size;
|
||||
allocs[wr].val = rand_pcg32(0xFFFFFFFFul);
|
||||
for (int i = 0; i < (int)size; ++i)
|
||||
buf[pos + i] = allocs[wr].val;
|
||||
wr = (wr + 1) % NUM_ALLOCS;
|
||||
count++;
|
||||
} else {
|
||||
ASSERT(count);
|
||||
}
|
||||
}
|
||||
|
||||
if (rand_pcg32(5) == 0) {
|
||||
int to_remove = rand_pcg32(5) + 1;
|
||||
while (to_remove-- > 0 && count > 0) {
|
||||
ASSERT(allocs[rd].pos != ALO_ALLOC_FAILED);
|
||||
fprintf(stderr, "FREE(%d;%d) pos=%d(%d) count=%d to_remove=%d\n", rd, count, allocs[rd].pos, allocs[rd].size, count, to_remove);
|
||||
for (int i = 0; i < (int)allocs[rd].size; ++i)
|
||||
REQUIRE_EQUAL_UINT32(buf[allocs[rd].pos + i], allocs[rd].val);
|
||||
aloRingFree(&ring, allocs[rd].pos);
|
||||
dumpRing(__LINE__, &ring);
|
||||
allocs[rd].pos = ALO_ALLOC_FAILED;
|
||||
rd = (rd + 1) % NUM_ALLOCS;
|
||||
--count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test();
|
||||
|
||||
ASSERT(1000 == testRandom(1000, 1000000, 1, 0));
|
||||
testRandom(1000, 1000000, 32, 999);
|
||||
|
||||
testRing();
|
||||
stressTestRing();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -17,3 +17,21 @@ typedef struct {
|
|||
|
||||
alo_block_t aloPoolAllocate(struct alo_pool_s*, alo_size_t size, alo_size_t alignment);
|
||||
void aloPoolFree(struct alo_pool_s *pool, int index);
|
||||
|
||||
// <- size ->
|
||||
// [.....|AAAAAAAAAAAAAAA|......]
|
||||
// ^ -- tail ^ -- head
|
||||
typedef struct {
|
||||
uint32_t size, head, tail;
|
||||
} alo_ring_t;
|
||||
|
||||
#define ALO_ALLOC_FAILED 0xffffffffu
|
||||
|
||||
// Marks the entire buffer as free
|
||||
void aloRingInit(alo_ring_t* ring, uint32_t size);
|
||||
|
||||
// Allocates a new aligned region and returns offset to it (AllocFailed if allocation failed)
|
||||
uint32_t aloRingAlloc(alo_ring_t* ring, uint32_t size, uint32_t alignment);
|
||||
|
||||
// Marks everything up-to-pos as free (expects up-to-pos to be valid)
|
||||
void aloRingFree(alo_ring_t* ring, uint32_t up_to_pos);
|
||||
|
|
|
@ -223,7 +223,7 @@ 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 );
|
||||
|
||||
if (!R_GeometryBufferAllocAndLock( &buffer, total_vertices, total_indices )) {
|
||||
if (!R_GeometryBufferAllocAndLock( &buffer, total_vertices, total_indices, LifetimeSingleFrame )) {
|
||||
gEngine.Con_Printf(S_ERROR "Cannot allocate geometry for beam\n");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ static void EmitWaterPolys( const cl_entity_t *ent, const msurface_t *warp, qboo
|
|||
num_indices += triangles * 3;
|
||||
}
|
||||
|
||||
if (!R_GeometryBufferAllocAndLock( &buffer, num_vertices, num_indices )) {
|
||||
if (!R_GeometryBufferAllocAndLock( &buffer, num_vertices, num_indices, LifetimeSingleFrame )) {
|
||||
gEngine.Con_Printf(S_ERROR "Cannot allocate geometry for %s\n", ent->model->name );
|
||||
return;
|
||||
}
|
||||
|
@ -485,7 +485,7 @@ static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) {
|
|||
uint32_t index_offset = 0;
|
||||
r_geometry_buffer_lock_t buffer;
|
||||
|
||||
if (!R_GeometryBufferAllocAndLock( &buffer, sizes.num_vertices, sizes.num_indices )) {
|
||||
if (!R_GeometryBufferAllocAndLock( &buffer, sizes.num_vertices, sizes.num_indices, LifetimeLong )) {
|
||||
gEngine.Con_Printf(S_ERROR "Cannot allocate geometry for %s\n", mod->name );
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -5,10 +5,6 @@
|
|||
|
||||
#define MAX_TEXTURES 4096
|
||||
|
||||
// TODO count these properly
|
||||
#define MAX_BUFFER_VERTICES (512 * 1024)
|
||||
#define MAX_BUFFER_INDICES (MAX_BUFFER_VERTICES * 3)
|
||||
|
||||
// indexed by uint8_t
|
||||
#define MAX_SURFACE_LIGHTS 256
|
||||
// indexed by uint8_t
|
||||
|
|
|
@ -328,12 +328,6 @@ static void submit( VkCommandBuffer cmdbuf, qboolean wait ) {
|
|||
}
|
||||
g_frame.current.phase = Phase_Idle;
|
||||
}
|
||||
|
||||
// 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
|
||||
XVK_RenderBufferFrameClear();
|
||||
}
|
||||
|
||||
inline static VkCommandBuffer currentCommandBuffer( void ) {
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
#include "vk_math.h"
|
||||
#include "vk_rtx.h"
|
||||
#include "vk_descriptor.h"
|
||||
#include "vk_framectl.h" // FIXME
|
||||
#include "vk_framectl.h" // FIXME needed for dynamic models cmdbuf
|
||||
#include "alolcator.h"
|
||||
|
||||
#include "eiface.h"
|
||||
#include "xash3d_mathlib.h"
|
||||
|
@ -20,27 +21,40 @@
|
|||
|
||||
#define MAX_UNIFORM_SLOTS (MAX_SCENE_ENTITIES * 2 /* solid + trans */ + 1)
|
||||
|
||||
#define MAX_BUFFER_VERTICES_STATIC (128 * 1024)
|
||||
#define MAX_BUFFER_INDICES_STATIC (MAX_BUFFER_VERTICES_STATIC * 3)
|
||||
#define GEOMETRY_BUFFER_STATIC_SIZE ALIGN_UP(MAX_BUFFER_VERTICES_STATIC * sizeof(vk_vertex_t) + MAX_BUFFER_INDICES_STATIC * sizeof(uint16_t), sizeof(vk_vertex_t))
|
||||
|
||||
#define MAX_BUFFER_VERTICES_DYNAMIC (128 * 1024)
|
||||
#define MAX_BUFFER_INDICES_DYNAMIC (MAX_BUFFER_VERTICES_DYNAMIC * 3)
|
||||
#define GEOMETRY_BUFFER_DYNAMIC_SIZE ALIGN_UP(MAX_BUFFER_VERTICES_DYNAMIC * sizeof(vk_vertex_t) + MAX_BUFFER_INDICES_DYNAMIC * sizeof(uint16_t), sizeof(vk_vertex_t))
|
||||
|
||||
#define GEOMETRY_BUFFER_SIZE (GEOMETRY_BUFFER_STATIC_SIZE + GEOMETRY_BUFFER_DYNAMIC_SIZE)
|
||||
|
||||
typedef struct {
|
||||
matrix4x4 mvp;
|
||||
vec4_t color;
|
||||
} uniform_data_t;
|
||||
|
||||
// TODO estimate
|
||||
#define MAX_ALLOCS 1024
|
||||
|
||||
static struct {
|
||||
VkPipelineLayout pipeline_layout;
|
||||
VkPipeline pipelines[kRenderTransAdd + 1];
|
||||
|
||||
vk_buffer_t buffer;
|
||||
vk_ring_buffer_t buffer_alloc_ring;
|
||||
|
||||
vk_buffer_t uniform_buffer;
|
||||
uint32_t ubo_align;
|
||||
|
||||
float fov_angle_y;
|
||||
} g_render;
|
||||
|
||||
struct {
|
||||
vk_buffer_t buffer;
|
||||
alo_ring_t static_ring;
|
||||
alo_ring_t dynamic_ring;
|
||||
|
||||
int frame_index;
|
||||
uint32_t dynamic_offsets[MAX_CONCURRENT_FRAMES];
|
||||
} g_geom;
|
||||
|
||||
static qboolean createPipelines( void )
|
||||
{
|
||||
/* VkPushConstantRange push_const = { */
|
||||
|
@ -225,10 +239,7 @@ typedef struct {
|
|||
} light[MAX_DLIGHTS];
|
||||
} vk_ubo_lights_t;
|
||||
|
||||
qboolean VK_RenderInit( void )
|
||||
{
|
||||
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);
|
||||
qboolean VK_RenderInit( void ) {
|
||||
uint32_t uniform_unit_size;
|
||||
|
||||
g_render.ubo_align = Q_max(4, vk_core.physical_device.properties.limits.minUniformBufferOffsetAlignment);
|
||||
|
@ -236,7 +247,7 @@ qboolean VK_RenderInit( void )
|
|||
|
||||
// TODO device memory and friends (e.g. handle mobile memory ...)
|
||||
|
||||
if (!VK_BufferCreate("geometry buffer", &g_render.buffer, vertex_buffer_size + index_buffer_size,
|
||||
if (!VK_BufferCreate("geometry buffer", &g_geom.buffer, GEOMETRY_BUFFER_SIZE,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | (vk_core.rtx ? VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR : 0),
|
||||
(vk_core.rtx ? VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : 0))) // TODO staging buffer?
|
||||
return false;
|
||||
|
@ -280,7 +291,7 @@ qboolean VK_RenderInit( void )
|
|||
if (!createPipelines())
|
||||
return false;
|
||||
|
||||
g_render.buffer_alloc_ring.size = g_render.buffer.size;
|
||||
XVK_RenderBufferMapClear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -291,22 +302,31 @@ void VK_RenderShutdown( void )
|
|||
vkDestroyPipeline(vk_core.device, g_render.pipelines[i], NULL);
|
||||
vkDestroyPipelineLayout( vk_core.device, g_render.pipeline_layout, NULL );
|
||||
|
||||
VK_BufferDestroy( &g_render.buffer );
|
||||
VK_BufferDestroy( &g_geom.buffer );
|
||||
VK_BufferDestroy( &g_render.uniform_buffer );
|
||||
}
|
||||
|
||||
qboolean R_GeometryBufferAllocAndLock( r_geometry_buffer_lock_t *lock, int vertex_count, int index_count ) {
|
||||
qboolean R_GeometryBufferAllocAndLock( r_geometry_buffer_lock_t *lock, int vertex_count, int index_count, r_geometry_lifetime_t lifetime ) {
|
||||
const uint32_t vertices_size = vertex_count * sizeof(vk_vertex_t);
|
||||
const uint32_t indices_size = index_count * sizeof(uint16_t);
|
||||
const uint32_t total_size = vertices_size + indices_size;
|
||||
alo_ring_t * const ring = (lifetime != LifetimeSingleFrame) ? &g_geom.static_ring : &g_geom.dynamic_ring;
|
||||
|
||||
const uint32_t offset = VK_RingBuffer_Alloc(&g_render.buffer_alloc_ring, total_size, sizeof(vk_vertex_t));
|
||||
if (offset == AllocFailed) {
|
||||
gEngine.Con_Printf(S_ERROR "Cannot allocate geometry buffer for %d vertices (%d bytes) and %d indices (%d bytes), only %u bytes left",
|
||||
vertex_count, vertices_size, index_count, indices_size, g_render.buffer_alloc_ring.free);
|
||||
const uint32_t alloc_offset = aloRingAlloc(ring, total_size, sizeof(vk_vertex_t));
|
||||
const uint32_t offset = alloc_offset + ((lifetime == LifetimeSingleFrame) ? GEOMETRY_BUFFER_STATIC_SIZE : 0);
|
||||
if (alloc_offset == ALO_ALLOC_FAILED) {
|
||||
gEngine.Con_Printf(S_ERROR "Cannot allocate %s geometry buffer for %d vertices (%d bytes) and %d indices (%d bytes)\n",
|
||||
lifetime == LifetimeSingleFrame ? "dynamic" : "static",
|
||||
vertex_count, vertices_size, index_count, indices_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store first dynamic allocation this frame
|
||||
if (lifetime == LifetimeSingleFrame && g_geom.dynamic_offsets[g_geom.frame_index] == ALO_ALLOC_FAILED) {
|
||||
gEngine.Con_Reportf("FRAME=%d FIRST_OFFSET=%d\n", g_geom.frame_index, alloc_offset);
|
||||
g_geom.dynamic_offsets[g_geom.frame_index] = alloc_offset;
|
||||
}
|
||||
|
||||
{
|
||||
const uint32_t vertices_offset = offset / sizeof(vk_vertex_t);
|
||||
const uint32_t indices_offset = (offset + vertices_size) / sizeof(uint16_t);
|
||||
|
@ -339,26 +359,22 @@ qboolean R_GeometryBufferAllocAndLock( r_geometry_buffer_lock_t *lock, int verte
|
|||
}
|
||||
|
||||
void R_GeometryBufferUnlock( const r_geometry_buffer_lock_t *lock ) {
|
||||
R_VkStagingUnlockToBuffer(lock->impl_.staging_handle, g_render.buffer.buffer, lock->impl_.offset);
|
||||
}
|
||||
|
||||
void XVK_RenderBufferMapFreeze( void ) {
|
||||
VK_RingBuffer_Fix(&g_render.buffer_alloc_ring);
|
||||
R_VkStagingUnlockToBuffer(lock->impl_.staging_handle, g_geom.buffer.buffer, lock->impl_.offset);
|
||||
}
|
||||
|
||||
void XVK_RenderBufferMapClear( void ) {
|
||||
VK_RingBuffer_Clear(&g_render.buffer_alloc_ring);
|
||||
}
|
||||
|
||||
void XVK_RenderBufferFrameClear( /*int frame_id*/void ) {
|
||||
VK_RingBuffer_ClearFrame(&g_render.buffer_alloc_ring);
|
||||
aloRingInit(&g_geom.static_ring, GEOMETRY_BUFFER_STATIC_SIZE);
|
||||
aloRingInit(&g_geom.dynamic_ring, GEOMETRY_BUFFER_DYNAMIC_SIZE);
|
||||
for (int i = 0; i < COUNTOF(g_geom.dynamic_offsets); ++i) {
|
||||
g_geom.dynamic_offsets[i] = ALO_ALLOC_FAILED;
|
||||
}
|
||||
g_geom.frame_index = 0;
|
||||
}
|
||||
|
||||
void XVK_RenderBufferPrintStats( void ) {
|
||||
// TODO get alignment holes size
|
||||
gEngine.Con_Reportf("Buffer usage: %uKiB of (%uKiB)\n",
|
||||
g_render.buffer_alloc_ring.permanent_size / 1024,
|
||||
g_render.buffer.size / 1024);
|
||||
g_geom.static_ring.head / 1024, g_geom.static_ring.size / 1024);
|
||||
}
|
||||
|
||||
#define MAX_DRAW_COMMANDS 8192 // TODO estimate
|
||||
|
@ -417,7 +433,7 @@ enum {
|
|||
};
|
||||
|
||||
void VK_RenderBegin( qboolean ray_tracing ) {
|
||||
g_render_state.uniform_free_offset = 0;
|
||||
g_render_state.uniform_free_offset = 0; // FIXME multiple frames in flight
|
||||
g_render_state.uniform_data_set_mask = UNIFORM_UNSET;
|
||||
g_render_state.current_ubo_offset = UINT32_MAX;
|
||||
|
||||
|
@ -427,6 +443,16 @@ void VK_RenderBegin( qboolean ray_tracing ) {
|
|||
g_render_state.num_draw_commands = 0;
|
||||
g_render_state.current_frame_is_ray_traced = ray_tracing;
|
||||
|
||||
{
|
||||
const int new_frame = (g_geom.frame_index + 1) % COUNTOF(g_geom.dynamic_offsets);
|
||||
if (g_geom.dynamic_offsets[new_frame] != ALO_ALLOC_FAILED) {
|
||||
gEngine.Con_Reportf("FRAME=%d FREE_OFFSET=%d\n", g_geom.frame_index, g_geom.dynamic_offsets[new_frame]);
|
||||
aloRingFree(&g_geom.dynamic_ring, g_geom.dynamic_offsets[new_frame]);
|
||||
g_geom.dynamic_offsets[new_frame] = ALO_ALLOC_FAILED;
|
||||
}
|
||||
g_geom.frame_index = new_frame;
|
||||
}
|
||||
|
||||
if (ray_tracing)
|
||||
VK_RayFrameBegin();
|
||||
}
|
||||
|
@ -609,8 +635,8 @@ void VK_RenderEnd( VkCommandBuffer cmdbuf )
|
|||
|
||||
{
|
||||
const VkDeviceSize offset = 0;
|
||||
vkCmdBindVertexBuffers(cmdbuf, 0, 1, &g_render.buffer.buffer, &offset);
|
||||
vkCmdBindIndexBuffer(cmdbuf, g_render.buffer.buffer, 0, VK_INDEX_TYPE_UINT16);
|
||||
vkCmdBindVertexBuffers(cmdbuf, 0, 1, &g_geom.buffer.buffer, &offset);
|
||||
vkCmdBindIndexBuffer(cmdbuf, g_geom.buffer.buffer, 0, VK_INDEX_TYPE_UINT16);
|
||||
}
|
||||
|
||||
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, g_render.pipeline_layout, 3, 1, vk_desc.ubo_sets + 1, 1, &dlights_ubo_offset);
|
||||
|
@ -690,7 +716,7 @@ void VK_RenderEndRTX( VkCommandBuffer cmdbuf, VkImageView img_dst_view, VkImage
|
|||
.view = &g_render_state.view,
|
||||
|
||||
.geometry_data = {
|
||||
.buffer = g_render.buffer.buffer,
|
||||
.buffer = g_geom.buffer.buffer,
|
||||
.size = VK_WHOLE_SIZE,
|
||||
},
|
||||
|
||||
|
@ -705,7 +731,7 @@ qboolean VK_RenderModelInit( VkCommandBuffer cmdbuf, vk_render_model_t *model )
|
|||
if (vk_core.rtx && (g_render_state.current_frame_is_ray_traced || !model->dynamic)) {
|
||||
// TODO runtime rtx switch: ???
|
||||
const vk_ray_model_init_t args = {
|
||||
.buffer = g_render.buffer.buffer,
|
||||
.buffer = g_geom.buffer.buffer,
|
||||
.model = model,
|
||||
};
|
||||
R_VkStagingCommit(cmdbuf);
|
||||
|
@ -713,14 +739,16 @@ qboolean VK_RenderModelInit( VkCommandBuffer cmdbuf, vk_render_model_t *model )
|
|||
const VkBufferMemoryBarrier bmb[] = { {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR, // FIXME
|
||||
.buffer = g_render.buffer.buffer,
|
||||
//.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR, // FIXME
|
||||
.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT, // FIXME
|
||||
.buffer = g_geom.buffer.buffer,
|
||||
.offset = 0, // FIXME
|
||||
.size = VK_WHOLE_SIZE, // FIXME
|
||||
} };
|
||||
vkCmdPipelineBarrier(cmdbuf,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
|
||||
//VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR,
|
||||
VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR,
|
||||
0, 0, NULL, ARRAYSIZE(bmb), bmb, 0, NULL);
|
||||
}
|
||||
model->ray_model = VK_RayModelCreate(cmdbuf, args);
|
||||
|
|
|
@ -45,15 +45,17 @@ typedef struct {
|
|||
} impl_;
|
||||
} r_geometry_buffer_lock_t;
|
||||
|
||||
qboolean R_GeometryBufferAllocAndLock( r_geometry_buffer_lock_t *lock, int vertex_count, int index_count );
|
||||
typedef enum {
|
||||
LifetimeLong,
|
||||
LifetimeSingleFrame
|
||||
} r_geometry_lifetime_t;
|
||||
|
||||
qboolean R_GeometryBufferAllocAndLock( r_geometry_buffer_lock_t *lock, int vertex_count, int index_count, r_geometry_lifetime_t lifetime );
|
||||
void R_GeometryBufferUnlock( const r_geometry_buffer_lock_t *lock );
|
||||
//void R_VkGeometryBufferFree( int handle );
|
||||
|
||||
void XVK_RenderBufferMapFreeze( void ); // Permanently freeze all allocations as map-permanent
|
||||
void XVK_RenderBufferMapClear( void ); // Free the entire buffer for a new map
|
||||
|
||||
void XVK_RenderBufferFrameClear( /*int frame_id*/void ); // mark data for frame with given id as free (essentially, just forward the ring buffer)
|
||||
|
||||
void XVK_RenderBufferPrintStats( void );
|
||||
|
||||
// Set UBO state for next VK_RenderScheduleDraw calls
|
||||
|
@ -138,15 +140,10 @@ qboolean VK_RenderModelInit( VkCommandBuffer cmdbuf, vk_render_model_t* model );
|
|||
void VK_RenderModelDestroy( vk_render_model_t* model );
|
||||
void VK_RenderModelDraw( const cl_entity_t *ent, vk_render_model_t* model );
|
||||
|
||||
void VK_RenderFrameBegin( void );
|
||||
|
||||
void VK_RenderModelDynamicBegin( int render_mode, const char *debug_name_fmt, ... );
|
||||
void VK_RenderModelDynamicAddGeometry( const vk_render_geometry_t *geom );
|
||||
void VK_RenderModelDynamicCommit( void );
|
||||
|
||||
void VK_RenderFrameEnd( VkCommandBuffer cmdbuf );
|
||||
void VK_RenderFrameEndRTX( VkCommandBuffer cmdbuf, VkImageView img_dst_view, VkImage img_dst, uint32_t w, uint32_t h );
|
||||
|
||||
void VK_RenderDebugLabelBegin( const char *label );
|
||||
void VK_RenderDebugLabelEnd( void );
|
||||
|
||||
|
|
|
@ -234,7 +234,6 @@ void R_NewMap( void ) {
|
|||
|
||||
// TODO should we do something like VK_BrushEndLoad?
|
||||
VK_UploadLightmap();
|
||||
XVK_RenderBufferMapFreeze();
|
||||
XVK_RenderBufferPrintStats();
|
||||
if (vk_core.rtx)
|
||||
VK_RayMapLoadEnd();
|
||||
|
|
|
@ -652,7 +652,7 @@ static void R_DrawSpriteQuad( const char *debug_name, mspriteframe_t *frame, vec
|
|||
|
||||
// Get buffer region for vertices and indices
|
||||
r_geometry_buffer_lock_t buffer;
|
||||
if (!R_GeometryBufferAllocAndLock( &buffer, 4, 6 )) {
|
||||
if (!R_GeometryBufferAllocAndLock( &buffer, 4, 6, LifetimeSingleFrame )) {
|
||||
gEngine.Con_Printf(S_ERROR "Cannot allocate geometry for sprite quad\n");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1923,7 +1923,7 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float
|
|||
ASSERT(num_indices > 0);
|
||||
|
||||
// Get buffer region for vertices and indices
|
||||
if (!R_GeometryBufferAllocAndLock( &buffer, num_vertices, num_indices )) {
|
||||
if (!R_GeometryBufferAllocAndLock( &buffer, num_vertices, num_indices, LifetimeSingleFrame )) {
|
||||
gEngine.Con_Printf(S_ERROR "Cannot allocate geometry for studio model\n");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -65,6 +65,17 @@ def configure(conf):
|
|||
if '-Werror=declaration-after-statement' in conf.env.CFLAGS:
|
||||
conf.env.CFLAGS.remove('-Werror=declaration-after-statement')
|
||||
|
||||
def printTestSummary(bld):
|
||||
results = getattr(bld, 'utest_results', [])
|
||||
for (f, code, out, err) in results:
|
||||
if code == 0:
|
||||
Logs.pprint('GREEN', '%s: ok' % f)
|
||||
continue
|
||||
|
||||
Logs.pprint('RED', '%s: test failed' % f)
|
||||
Logs.pprint('CYAN', out.decode('utf-8'))
|
||||
Logs.pprint('CYAN', err.decode('utf-8'))
|
||||
|
||||
def build(bld):
|
||||
if not bld.env.VK:
|
||||
return
|
||||
|
@ -126,4 +137,10 @@ def build(bld):
|
|||
bld.install_files(bld.env.LIBDIR,
|
||||
bld.path.ant_glob('data/**'),
|
||||
cwd=bld.path.find_dir('data/'),
|
||||
relative_trick=True)
|
||||
relative_trick=True)
|
||||
|
||||
bld.program(features='test', defines=['ALOLCATOR_TEST'],source='alolcator.c', target='alolcator')
|
||||
bld.add_post_fun(printTestSummary)
|
||||
|
||||
#from waflib.Tools import waf_unit_test
|
||||
#bld.add_post_fun(waf_unit_test.summary)
|
||||
|
|
Loading…
Reference in New Issue