JBR-7570 Implemented ring buffer. Added lazy implicit initialization for dynamic arrays. (#451)

(cherry picked from commit 7b89fe2311)
This commit is contained in:
Nikita Gubarkov
2024-08-28 15:11:20 +02:00
committed by jbrbot
parent ea90b7c6e1
commit 2eb758cba9
5 changed files with 216 additions and 59 deletions

View File

@@ -8,25 +8,48 @@ void* CARR_array_alloc(size_t elem_size, size_t capacity) {
if (pvec == NULL) {
return NULL;
}
pvec->elem_size = elem_size;
pvec->size = 0;
pvec->capacity = capacity;
return pvec->data;
}
void* CARR_array_realloc(CARR_array_t* vec, size_t new_capacity) {
void* CARR_array_realloc(CARR_array_t* vec, size_t elem_size, size_t new_capacity) {
if (vec->capacity == new_capacity) {
return vec->data;
}
CARR_array_t* new_vec =
(CARR_array_t*)((char*)CARR_array_alloc(vec->elem_size, new_capacity) - offsetof(CARR_array_t, data));
(CARR_array_t*)((char*)CARR_array_alloc(elem_size, new_capacity) - offsetof(CARR_array_t, data));
if (new_vec == NULL) {
return NULL;
return vec == NULL ? NULL : vec->data;
}
new_vec->capacity = new_capacity;
new_vec->size = MIN(vec->size, new_capacity);
new_vec->elem_size = vec->elem_size;
memcpy(new_vec->data, vec->data, new_vec->size*new_vec->elem_size);
memcpy(new_vec->data, vec->data, new_vec->size*elem_size);
free(vec);
return new_vec->data;
}
void* CARR_ring_buffer_realloc(CARR_ring_buffer_t* buf, size_t elem_size, size_t new_capacity) {
if (buf != NULL && buf->capacity == new_capacity) {
return buf->data;
}
CARR_ring_buffer_t* new_buf =
(CARR_ring_buffer_t*) malloc(elem_size * new_capacity + offsetof(CARR_ring_buffer_t, data));
if (new_buf == NULL) {
return NULL;
}
new_buf->head = new_buf->tail = 0;
new_buf->capacity = new_capacity;
if (buf != NULL) {
if (buf->tail > buf->head) {
new_buf->tail = buf->tail - buf->head;
memcpy(new_buf->data, buf->data + buf->head*elem_size, new_buf->tail*elem_size);
} else if (buf->tail < buf->head) {
new_buf->tail = buf->capacity + buf->tail - buf->head;
memcpy(new_buf->data, buf->data + buf->head*elem_size, (buf->capacity-buf->head)*elem_size);
memcpy(new_buf->data + (new_buf->tail-buf->tail)*elem_size, buf->data, buf->tail*elem_size);
}
free(buf);
}
return new_buf->data;
}

View File

@@ -1,48 +1,63 @@
#ifndef C_ARRAY_UTIL_H
#define C_ARRAY_UTIL_H
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#define ARRAY_CAPACITY_MULT 2
// C_ARRAY_UTIL_ALLOCATION_FAILED is called when allocation fails.
// Default implementation calls abort().
// Functions that can call C_ARRAY_UTIL_ALLOCATION_FAILED explicitly state
// this in the documentation. Functions with *_TRY_* return NULL on failure.
#ifndef C_ARRAY_UTIL_ALLOCATION_FAILED
#include <stdlib.h>
#define C_ARRAY_UTIL_ALLOCATION_FAILED() abort()
#endif
#define ARRAY_CAPACITY_GROW(C) (((C) * 3 + 1) / 2) // 1.5 multiplier
#define ARRAY_DEFAULT_CAPACITY 10
typedef struct {
size_t elem_size;
size_t size;
size_t capacity;
char data[];
} CARR_array_t;
void* CARR_array_alloc(size_t elem_size, size_t capacity);
void* CARR_array_realloc(CARR_array_t* vec, size_t new_capacity);
void* CARR_array_realloc(CARR_array_t* vec, size_t elem_size, size_t new_capacity);
typedef struct {
size_t head;
size_t tail;
size_t capacity;
char data[];
} CARR_ring_buffer_t;
void* CARR_ring_buffer_realloc(CARR_ring_buffer_t* buf, size_t elem_size, size_t new_capacity);
/**
* Allocate array
* Allocate array. Returns NULL on allocation failure.
* @param T type of elements
* @param CAPACITY capacity of the array
*/
#define ARRAY_ALLOC(T, CAPACITY) (T*)CARR_array_alloc(sizeof(T), CAPACITY)
#define ARRAY_T(P) (CARR_array_t *)((char*)P - offsetof(CARR_array_t, data))
#define ARRAY_T(P) ((CARR_array_t *)((P) == NULL ? NULL : (char*)(P) - offsetof(CARR_array_t, data)))
/**
* @param P pointer to the first data element of the array
* @return size of the array
*/
#define ARRAY_SIZE(P) (ARRAY_T(P))->size
#define ARRAY_SIZE(P) ((P) == NULL ? (size_t) 0 : (ARRAY_T(P))->size)
/**
* @param P pointer to the first data element of the array
* @return capacity of the array
*/
#define ARRAY_CAPACITY(P) (ARRAY_T(P))->capacity
#define ARRAY_CAPACITY(P) ((P) == NULL ? (size_t) 0 : (ARRAY_T(P))->capacity)
/**
* @param P pointer to the first data element of the array
* @return last element in the array
*/
#define ARRAY_LAST(P) (P[(ARRAY_T(P))->size - 1])
#define ARRAY_LAST(P) ((P)[ARRAY_SIZE(P) - 1])
/**
* Deallocate the vector
@@ -56,7 +71,7 @@ void* CARR_array_realloc(CARR_array_t* vec, size_t new_capacity);
* @param F function to apply
*/
#define ARRAY_APPLY(P, F) do { \
for (uint32_t _i = 0; _i < ARRAY_SIZE(P); _i++) F(&(P[_i])); \
for (size_t _i = 0; _i < ARRAY_SIZE(P); _i++) F(&((P)[_i])); \
} while(0)
/**
@@ -65,7 +80,7 @@ void* CARR_array_realloc(CARR_array_t* vec, size_t new_capacity);
* @param F function to apply
*/
#define ARRAY_APPLY_LEADING(P, F, ...) do { \
for (uint32_t _i = 0; _i < ARRAY_SIZE(P); _i++) F(&(P[_i]), __VA_ARGS__); \
for (size_t _i = 0; _i < ARRAY_SIZE(P); _i++) F(&((P)[_i]), __VA_ARGS__); \
} while(0)
/**
@@ -74,29 +89,148 @@ void* CARR_array_realloc(CARR_array_t* vec, size_t new_capacity);
* @param F function to apply
*/
#define ARRAY_APPLY_TRAILING(P, F, ...) do { \
for (uint32_t _i = 0; _i < ARRAY_SIZE(P); _i++) F(__VA_ARGS__, &(P[_i])); \
for (size_t _i = 0; _i < ARRAY_SIZE(P); _i++) F(__VA_ARGS__, &((P)[_i])); \
} while(0)
/**
* Shrink capacity of the array to its size
* @param PP pointer to the pointer to the first data element of the array
* Ensure array capacity. Implicitly initializes when array is NULL.
* On allocation failure, array is unchanged.
* @param P pointer to the first data element of the array
* @param CAPACITY required capacity of the array
*/
#define ARRAY_SHRINK_TO_FIT(PP) do { \
*PP = CARR_array_realloc(ARRAY_T(*PP), ARRAY_SIZE(*PP)); \
#define ARRAY_TRY_ENSURE_CAPACITY(P, CAPACITY) do { \
if ((P) == NULL) { \
if ((CAPACITY) > 0) (P) = CARR_array_alloc(sizeof((P)[0]), CAPACITY); \
} else if (ARRAY_CAPACITY(P) < (CAPACITY)) { \
(P) = CARR_array_realloc(ARRAY_T(P), sizeof((P)[0]), CAPACITY); \
} \
} while(0)
/**
* Add element to the end of the array
* @param PP pointer to the pointer to the first data element of the array
* Ensure array capacity. Implicitly initializes when array is NULL.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* @param P pointer to the first data element of the array
* @param CAPACITY required capacity of the array
*/
#define ARRAY_PUSH_BACK(PP, D) do { \
if (ARRAY_SIZE(*PP) >= ARRAY_CAPACITY(*PP)) { \
*PP = CARR_array_realloc(ARRAY_T(*PP), ARRAY_SIZE(*PP)*ARRAY_CAPACITY_MULT);\
} \
*(*PP + ARRAY_SIZE(*PP)) = (D); \
ARRAY_SIZE(*PP)++; \
#define ARRAY_ENSURE_CAPACITY(P, CAPACITY) do { \
ARRAY_TRY_ENSURE_CAPACITY(P, CAPACITY); \
if (ARRAY_CAPACITY(P) < (CAPACITY)) C_ARRAY_UTIL_ALLOCATION_FAILED(); \
} while(0)
#define SARRAY_COUNT_OF(STATIC_ARRAY) (sizeof(STATIC_ARRAY)/sizeof(STATIC_ARRAY[0]))
/**
* Shrink capacity of the array to its size.
* On allocation failure, array is unchanged.
* @param P pointer to the first data element of the array
*/
#define ARRAY_SHRINK_TO_FIT(P) do { \
if ((P) != NULL) { \
(P) = CARR_array_realloc(ARRAY_T(P), sizeof((P)[0]), ARRAY_SIZE(P)); \
} \
} while(0)
#define ARRAY_RESIZE_IMPL(P, SIZE, ...) do { \
if ((P) != NULL || (SIZE) > 0) { \
ARRAY_ENSURE_CAPACITY(P, SIZE); \
if ((P) != NULL && (ARRAY_T(P))->capacity >= (SIZE)) { \
(ARRAY_T(P))->size = (SIZE); \
} __VA_ARGS__ \
} \
} while(0)
/**
* Resize an array. Implicitly initializes when array is NULL.
* On allocation failure, array is unchanged.
* @param P pointer to the first data element of the array
* @param SIZE required size of the array
*/
#define ARRAY_TRY_RESIZE(P, SIZE) ARRAY_RESIZE_IMPL(P, SIZE, )
/**
* Resize an array. Implicitly initializes when array is NULL.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* @param P pointer to the first data element of the array
* @param SIZE required size of the array
*/
#define ARRAY_RESIZE(P, SIZE) ARRAY_RESIZE_IMPL(P, SIZE, else if ((SIZE) > 0) C_ARRAY_UTIL_ALLOCATION_FAILED();)
/**
* Add element to the end of the array. Implicitly initializes when array is NULL.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* @param P pointer to the first data element of the array
*/
#define ARRAY_PUSH_BACK(P, ...) do { \
if ((P) == NULL) { \
(P) = CARR_array_alloc(sizeof((P)[0]), ARRAY_DEFAULT_CAPACITY); \
} else if (ARRAY_SIZE(P) >= ARRAY_CAPACITY(P)) { \
(P) = CARR_array_realloc(ARRAY_T(P), sizeof((P)[0]), ARRAY_CAPACITY_GROW(ARRAY_SIZE(P))); \
} \
if (ARRAY_SIZE(P) >= ARRAY_CAPACITY(P)) C_ARRAY_UTIL_ALLOCATION_FAILED(); \
*((P) + ARRAY_SIZE(P)) = (__VA_ARGS__); \
(ARRAY_T(P))->size++; \
} while(0)
#define SARRAY_COUNT_OF(STATIC_ARRAY) (sizeof(STATIC_ARRAY)/sizeof((STATIC_ARRAY)[0]))
#define RING_BUFFER_T(P) ((CARR_ring_buffer_t *)((P) == NULL ? NULL : (char*)(P) - offsetof(CARR_ring_buffer_t, data)))
/**
* @param P pointer to the first data element of the ring buffer
* @return size of the ring buffer
*/
#define RING_BUFFER_SIZE(P) ((P) == NULL ? (size_t) 0 : \
(RING_BUFFER_T(P)->capacity + RING_BUFFER_T(P)->tail - RING_BUFFER_T(P)->head) % RING_BUFFER_T(P)->capacity)
/**
* @param P pointer to the first data element of the ring buffer
* @return capacity of the ring buffer
*/
#define RING_BUFFER_CAPACITY(P) ((P) == NULL ? (size_t) 0 : RING_BUFFER_T(P)->capacity)
/**
* Add element to the end of the ring buffer. Implicitly initializes when buffer is NULL.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* @param P pointer to the first data element of the buffer
*/
#define RING_BUFFER_PUSH(P, ...) RING_BUFFER_PUSH_CUSTOM(P, (P)[tail] = (__VA_ARGS__);)
#define RING_BUFFER_PUSH_CUSTOM(P, ...) do { \
size_t head, tail, new_tail; \
if ((P) == NULL) { \
(P) = CARR_ring_buffer_realloc(NULL, sizeof((P)[0]), ARRAY_DEFAULT_CAPACITY); \
if ((P) == NULL) C_ARRAY_UTIL_ALLOCATION_FAILED(); \
head = tail = 0; \
new_tail = 1; \
} else { \
head = RING_BUFFER_T(P)->head; \
tail = RING_BUFFER_T(P)->tail; \
new_tail = (tail + 1) % RING_BUFFER_T(P)->capacity; \
if (new_tail == head) { \
(P) = CARR_ring_buffer_realloc(RING_BUFFER_T(P), sizeof(P[0]), ARRAY_CAPACITY_GROW(RING_BUFFER_T(P)->capacity)); \
if ((P) == NULL) C_ARRAY_UTIL_ALLOCATION_FAILED(); \
head = 0; \
tail = RING_BUFFER_T(P)->tail; \
new_tail = RING_BUFFER_T(P)->tail + 1; \
} \
} \
__VA_ARGS__ \
RING_BUFFER_T(P)->tail = new_tail; \
} while(0)
/**
* Get pointer to the first element of the ring buffer.
* @param P pointer to the first data element of the buffer
*/
#define RING_BUFFER_PEEK(P) ((P) == NULL || RING_BUFFER_T(P)->head == RING_BUFFER_T(P)->tail ? NULL : &(P)[RING_BUFFER_T(P)->head])
/**
* Move beginning of the ring buffer forward (remove first element).
* @param P pointer to the first data element of the buffer
*/
#define RING_BUFFER_POP(P) RING_BUFFER_T(P)->head = (RING_BUFFER_T(P)->head + 1) % RING_BUFFER_T(P)->capacity
/**
* Deallocate the ring buffer
* @param P pointer to the first data element of the buffer
*/
#define RING_BUFFER_FREE(P) free(RING_BUFFER_T(P))
#endif // CARRAYUTILS_H

View File

@@ -241,13 +241,13 @@ static jboolean VK_InitGraphicsEnvironment(PFN_vkGetInstanceProcAddr vkGetInstan
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " %s", (char *) extensions[i].extensionName)
}
pchar* enabledLayers = ARRAY_ALLOC(pchar, MAX_ENABLED_LAYERS);
pchar* enabledExtensions = ARRAY_ALLOC(pchar, MAX_ENABLED_EXTENSIONS);
pchar* enabledLayers = NULL;
pchar* enabledExtensions = NULL;
void *pNext = NULL;
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
ARRAY_PUSH_BACK(&enabledExtensions, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
ARRAY_PUSH_BACK(enabledExtensions, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
#endif
ARRAY_PUSH_BACK(&enabledExtensions, VK_KHR_SURFACE_EXTENSION_NAME);
ARRAY_PUSH_BACK(enabledExtensions, VK_KHR_SURFACE_EXTENSION_NAME);
// Check required layers & extensions.
for (uint32_t i = 0; i < ARRAY_SIZE(enabledExtensions); i++) {
@@ -300,8 +300,8 @@ static jboolean VK_InitGraphicsEnvironment(PFN_vkGetInstanceProcAddr vkGetInstan
}
if (foundDebugLayer && foundDebugExt) {
ARRAY_PUSH_BACK(&enabledLayers, VALIDATION_LAYER_NAME);
ARRAY_PUSH_BACK(&enabledExtensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
ARRAY_PUSH_BACK(enabledLayers, VALIDATION_LAYER_NAME);
ARRAY_PUSH_BACK(enabledExtensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
pNext = &features;
} else {
J2dRlsTraceLn2(J2D_TRACE_WARNING, "Vulkan: %s and %s are not supported",
@@ -561,7 +561,7 @@ static jboolean VK_FindDevices() {
return JNI_FALSE;
}
ARRAY_PUSH_BACK(&deviceEnabledExtensions, VK_KHR_SWAPCHAIN_EXTENSION_NAME);
ARRAY_PUSH_BACK(deviceEnabledExtensions, VK_KHR_SWAPCHAIN_EXTENSION_NAME);
// Validation layer
#ifdef DEBUG
@@ -569,7 +569,7 @@ static jboolean VK_FindDevices() {
for (uint32_t j = 0; j < layerCount; j++) {
if (strcmp(VALIDATION_LAYER_NAME, layers[j].layerName) == 0) {
validationLayerNotSupported = 0;
ARRAY_PUSH_BACK(&deviceEnabledLayers, VALIDATION_LAYER_NAME);
ARRAY_PUSH_BACK(deviceEnabledLayers, VALIDATION_LAYER_NAME);
break;
}
}
@@ -584,7 +584,7 @@ static jboolean VK_FindDevices() {
return JNI_FALSE;
}
ARRAY_PUSH_BACK(&geInstance->devices,
ARRAY_PUSH_BACK(geInstance->devices,
((VKDevice) {
.name = deviceName,
.handle = VK_NULL_HANDLE,
@@ -795,10 +795,10 @@ static jboolean VK_InitDevice(VKDevice* device) {
}
VKTxVertex* vertices = ARRAY_ALLOC(VKTxVertex, 4);
ARRAY_PUSH_BACK(&vertices, ((VKTxVertex){-1.0f, -1.0f, 0.0f, 0.0f}));
ARRAY_PUSH_BACK(&vertices, ((VKTxVertex){1.0f, -1.0f, 1.0f, 0.0f}));
ARRAY_PUSH_BACK(&vertices, ((VKTxVertex){-1.0f, 1.0f, 0.0f, 1.0f}));
ARRAY_PUSH_BACK(&vertices, ((VKTxVertex){1.0f, 1.0f, 1.0f, 1.0f}));
ARRAY_PUSH_BACK(vertices, ((VKTxVertex){-1.0f, -1.0f, 0.0f, 0.0f}));
ARRAY_PUSH_BACK(vertices, ((VKTxVertex){1.0f, -1.0f, 1.0f, 0.0f}));
ARRAY_PUSH_BACK(vertices, ((VKTxVertex){-1.0f, 1.0f, 0.0f, 1.0f}));
ARRAY_PUSH_BACK(vertices, ((VKTxVertex){1.0f, 1.0f, 1.0f, 1.0f}));
device->blitVertexBuffer = ARRAY_TO_VERTEX_BUF(device, vertices);
if (!device->blitVertexBuffer) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Cannot create vertex buffer")

View File

@@ -175,7 +175,7 @@ VKImage* VKImage_CreateImageArrayFromSwapChain(VKDevice* device,
VKImage* images = ARRAY_ALLOC(VKImage, swapChainImagesCount);
for (uint32_t i = 0; i < swapChainImagesCount; i++) {
ARRAY_PUSH_BACK(&images, ((VKImage){
ARRAY_PUSH_BACK(images, ((VKImage){
.image = swapChainImages[i],
.memory = VK_NULL_HANDLE,
.format = format,

View File

@@ -831,16 +831,16 @@ void VKRenderer_RenderParallelogram(VKDevice* device,
float p4x = -1.0f + (x11 + dx12) / width;
float p4y = -1.0f + (y11 + dy12) / height;
ARRAY_PUSH_BACK(&vertices, ((VKVertex) {p1x, p1y}));
ARRAY_PUSH_BACK(&vertices, ((VKVertex) {p2x, p2y}));
ARRAY_PUSH_BACK(&vertices, ((VKVertex) {p3x, p3y}));
ARRAY_PUSH_BACK(vertices, ((VKVertex) {p1x, p1y}));
ARRAY_PUSH_BACK(vertices, ((VKVertex) {p2x, p2y}));
ARRAY_PUSH_BACK(vertices, ((VKVertex) {p3x, p3y}));
if (renderer->primitiveTopology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) {
ARRAY_PUSH_BACK(&vertices, ((VKVertex) {p3x, p3y}));
ARRAY_PUSH_BACK(vertices, ((VKVertex) {p3x, p3y}));
}
ARRAY_PUSH_BACK(&vertices, ((VKVertex) {p4x, p4y}));
ARRAY_PUSH_BACK(&vertices, ((VKVertex) {p1x, p1y}));
ARRAY_PUSH_BACK(vertices, ((VKVertex) {p4x, p4y}));
ARRAY_PUSH_BACK(vertices, ((VKVertex) {p1x, p1y}));
int vertexNum = ARRAY_SIZE(vertices);
@@ -888,17 +888,17 @@ void VKRenderer_FillSpans(VKDevice* device, jint color, VKSDOps *dstOps, jint sp
float p4x = p1x;
float p4y = p3y;
ARRAY_PUSH_BACK(&vertices, ((VKVertex){p1x,p1y}));
ARRAY_PUSH_BACK(vertices, ((VKVertex){p1x,p1y}));
ARRAY_PUSH_BACK(&vertices, ((VKVertex){p2x,p2y}));
ARRAY_PUSH_BACK(vertices, ((VKVertex){p2x,p2y}));
ARRAY_PUSH_BACK(&vertices, ((VKVertex){p3x,p3y}));
ARRAY_PUSH_BACK(vertices, ((VKVertex){p3x,p3y}));
ARRAY_PUSH_BACK(&vertices, ((VKVertex){p3x,p3y}));
ARRAY_PUSH_BACK(vertices, ((VKVertex){p3x,p3y}));
ARRAY_PUSH_BACK(&vertices, ((VKVertex){p4x,p4y}));
ARRAY_PUSH_BACK(vertices, ((VKVertex){p4x,p4y}));
ARRAY_PUSH_BACK(&vertices, ((VKVertex){p1x,p1y}));
ARRAY_PUSH_BACK(vertices, ((VKVertex){p1x,p1y}));
}
VKBuffer *fillVertexBuffer = ARRAY_TO_VERTEX_BUF(device, vertices);