Skip to content

Commit

Permalink
JBR-7570 Implemented ring buffer. Added lazy implicit initialization …
Browse files Browse the repository at this point in the history
…for dynamic arrays. (#451)

(cherry picked from commit 7b89fe2)
  • Loading branch information
YaaZ authored and jbrbot committed Dec 15, 2024
1 parent 25146a8 commit 8f5507f
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 59 deletions.
35 changes: 29 additions & 6 deletions src/java.desktop/share/native/common/java2d/vulkan/CArrayUtil.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
188 changes: 161 additions & 27 deletions src/java.desktop/share/native/common/java2d/vulkan/CArrayUtil.h
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)

/**
Expand All @@ -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)

/**
Expand All @@ -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)

/**
* 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_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)

/**
* 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_ENSURE_CAPACITY(P, CAPACITY) do { \
ARRAY_TRY_ENSURE_CAPACITY(P, CAPACITY); \
if (ARRAY_CAPACITY(P) < (CAPACITY)) C_ARRAY_UTIL_ALLOCATION_FAILED(); \
} while(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();)

/**
* Shrink capacity of the array to its size
* @param PP pointer to the pointer to the first data element of the array
* 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_SHRINK_TO_FIT(PP) do { \
*PP = CARR_array_realloc(ARRAY_T(*PP), ARRAY_SIZE(*PP)); \
#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 array
* @param PP pointer to the pointer to the first data element of the array
* 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 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 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)

#define SARRAY_COUNT_OF(STATIC_ARRAY) (sizeof(STATIC_ARRAY)/sizeof(STATIC_ARRAY[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
26 changes: 13 additions & 13 deletions src/java.desktop/share/native/common/java2d/vulkan/VKBase.c
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -561,15 +561,15 @@ 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
int validationLayerNotSupported = 1;
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;
}
}
Expand All @@ -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,
Expand Down Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit 8f5507f

Please sign in to comment.