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:
Ivan 'provod' Avdeev 2022-05-14 13:36:33 -07:00 committed by Ivan Avdeev
parent 68738fdd97
commit cb427d0839
13 changed files with 306 additions and 67 deletions

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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 ) {

View File

@ -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);

View File

@ -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 );

View File

@ -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();

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)