From fd2475974265f1ac01711229b5d730dcddb583f3 Mon Sep 17 00:00:00 2001 From: Nikita Gubarkov Date: Wed, 28 Aug 2024 15:11:20 +0200 Subject: [PATCH] JBR-7570 Implemented ring buffer. Added lazy implicit initialization for dynamic arrays. (#451) --- .../native/common/java2d/vulkan/CArrayUtil.c | 35 +++- .../native/common/java2d/vulkan/CArrayUtil.h | 188 +++++++++++++++--- .../native/common/java2d/vulkan/VKBase.c | 26 +-- .../native/common/java2d/vulkan/VKImage.c | 2 +- .../native/common/java2d/vulkan/VKRenderer.c | 24 +-- 5 files changed, 216 insertions(+), 59 deletions(-) diff --git a/src/java.desktop/share/native/common/java2d/vulkan/CArrayUtil.c b/src/java.desktop/share/native/common/java2d/vulkan/CArrayUtil.c index c3645d2125f7..5ac9f7405eec 100644 --- a/src/java.desktop/share/native/common/java2d/vulkan/CArrayUtil.c +++ b/src/java.desktop/share/native/common/java2d/vulkan/CArrayUtil.c @@ -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; +} diff --git a/src/java.desktop/share/native/common/java2d/vulkan/CArrayUtil.h b/src/java.desktop/share/native/common/java2d/vulkan/CArrayUtil.h index ae0cb0aff2a5..526f3b2c2357 100644 --- a/src/java.desktop/share/native/common/java2d/vulkan/CArrayUtil.h +++ b/src/java.desktop/share/native/common/java2d/vulkan/CArrayUtil.h @@ -1,48 +1,63 @@ #ifndef C_ARRAY_UTIL_H #define C_ARRAY_UTIL_H -#include #include -#include -#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 +#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) + +/** + * 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 diff --git a/src/java.desktop/share/native/common/java2d/vulkan/VKBase.c b/src/java.desktop/share/native/common/java2d/vulkan/VKBase.c index 898fed82cb10..37358471e3b5 100644 --- a/src/java.desktop/share/native/common/java2d/vulkan/VKBase.c +++ b/src/java.desktop/share/native/common/java2d/vulkan/VKBase.c @@ -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") diff --git a/src/java.desktop/share/native/common/java2d/vulkan/VKImage.c b/src/java.desktop/share/native/common/java2d/vulkan/VKImage.c index 5596f5ef6b85..cc5ecb5891ab 100644 --- a/src/java.desktop/share/native/common/java2d/vulkan/VKImage.c +++ b/src/java.desktop/share/native/common/java2d/vulkan/VKImage.c @@ -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, diff --git a/src/java.desktop/share/native/common/java2d/vulkan/VKRenderer.c b/src/java.desktop/share/native/common/java2d/vulkan/VKRenderer.c index a0f01d5b1017..7b071f5d789a 100644 --- a/src/java.desktop/share/native/common/java2d/vulkan/VKRenderer.c +++ b/src/java.desktop/share/native/common/java2d/vulkan/VKRenderer.c @@ -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);