Double buffered GPUHexContext

main
noah metz 2024-11-06 17:50:42 -07:00
parent 32963fce07
commit f984f245aa
5 changed files with 191 additions and 136 deletions

@ -13,6 +13,11 @@
#define REGION_WIDTH (HEX_X*REGION_DIAMETER) #define REGION_WIDTH (HEX_X*REGION_DIAMETER)
#define REGION_HEIGHT (HEX_Z*REGION_DIAMETER) #define REGION_HEIGHT (HEX_Z*REGION_DIAMETER)
extern vec3 hex_vertices[];
extern vec3 hex_starts[];
extern vec3 hex_directions[];
extern int hex_indices[];
typedef struct GPUHexStruct { typedef struct GPUHexStruct {
float height[6]; float height[6];
uint32_t color[7]; uint32_t color[7];
@ -44,23 +49,24 @@ typedef struct GPUHexContextStruct {
vec4 hover_end; vec4 hover_end;
uint32_t current_map; uint32_t current_map;
uint32_t clicked_region; uint32_t clicked_region;
int32_t clicked_hex; uint32_t clicked_hex;
uint32_t hovered_region; uint32_t hovered_region;
int32_t hovered_hex; uint32_t hovered_hex;
VkDeviceAddress regions[MAX_LOADED_REGIONS]; VkDeviceAddress regions[MAX_LOADED_REGIONS];
} GPUHexContext; } GPUHexContext;
typedef struct HexContextStruct { typedef struct HexContextStruct {
VkDeviceAddress address; VkDeviceAddress address[MAX_FRAMES_IN_FLIGHT];
VkBuffer context; VkBuffer context[MAX_FRAMES_IN_FLIGHT];
VmaAllocation context_memory; VmaAllocation context_memory[MAX_FRAMES_IN_FLIGHT];
GraphicsPipeline graphics; GraphicsPipeline graphics;
GraphicsPipeline ray_pipeline;
GraphicsPipeline highlight_pipeline; GraphicsPipeline highlight_pipeline;
#ifdef DRAW_HEX_RAYS
GraphicsPipeline ray_pipeline;
#endif
HexRegion* regions[MAX_LOADED_REGIONS]; HexRegion* regions[MAX_LOADED_REGIONS];
GPUHexContext data; GPUHexContext data;
} HexContext; } HexContext;

@ -5,8 +5,6 @@
void record_ui_draw(VkCommandBuffer command_buffer, UIContext* ui_context, double time, uint32_t frame) { void record_ui_draw(VkCommandBuffer command_buffer, UIContext* ui_context, double time, uint32_t frame) {
UIPushConstant push = { UIPushConstant push = {
.time = (float)time, .time = (float)time,
.layer = 0,
.pad = frame,
}; };
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->pipeline.pipeline); vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ui_context->pipeline.pipeline);
@ -25,20 +23,22 @@ void record_ui_draw(VkCommandBuffer command_buffer, UIContext* ui_context, doubl
void record_hex_draw(VkCommandBuffer command_buffer, HexContext* hex, double time, uint32_t frame) { void record_hex_draw(VkCommandBuffer command_buffer, HexContext* hex, double time, uint32_t frame) {
HexPushConstant push = { HexPushConstant push = {
.context = hex->address, .context = hex->address[frame],
.time = (float)time, .time = (float)time,
}; };
vkCmdPushConstants(command_buffer, hex->graphics.layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(HexPushConstant), &push); vkCmdPushConstants(command_buffer, hex->graphics.layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(HexPushConstant), &push);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, hex->graphics.pipeline); vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, hex->graphics.pipeline);
vkCmdDraw(command_buffer, 18, REGION_HEX_COUNT*MAX_LOADED_REGIONS, 0, 0); vkCmdDraw(command_buffer, 18, REGION_HEX_COUNT*MAX_LOADED_REGIONS, 0, 0);
// Draw rays
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, hex->ray_pipeline.pipeline);
vkCmdDraw(command_buffer, 2, 2, 0, 0);
// Draw Highlights
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, hex->highlight_pipeline.pipeline); vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, hex->highlight_pipeline.pipeline);
vkCmdDraw(command_buffer, 18, 2, 0, 0); vkCmdDraw(command_buffer, 18, 2, 0, 0);
#ifdef DRAW_HEX_RAYS
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, hex->ray_pipeline.pipeline);
vkCmdDraw(command_buffer, 2, 2, 0, 0);
#endif
} }
void record_ui_compute(VkCommandBuffer command_buffer, UIContext* ui, uint32_t frame) { void record_ui_compute(VkCommandBuffer command_buffer, UIContext* ui, uint32_t frame) {
@ -144,7 +144,7 @@ VkResult draw_frame(
VK_RESULT(vkEndCommandBuffer(compute_commands)); VK_RESULT(vkEndCommandBuffer(compute_commands));
frame->compute_index += 1; frame->compute_index += 1;
VkPipelineStageFlags compute_wait_stages[] = {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT}; VkPipelineStageFlags compute_wait_stages[] = {VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT};
VkTimelineSemaphoreSubmitInfo compute_timeline = { VkTimelineSemaphoreSubmitInfo compute_timeline = {
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
.waitSemaphoreValueCount = 1, .waitSemaphoreValueCount = 1,

@ -952,6 +952,7 @@ VkResult create_depth_image(
VmaAllocationCreateInfo allocation_info = { VmaAllocationCreateInfo allocation_info = {
.usage = VMA_MEMORY_USAGE_AUTO, .usage = VMA_MEMORY_USAGE_AUTO,
.preferredFlags = VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT,
}; };
VkResult result; VkResult result;
@ -1294,17 +1295,6 @@ VkResult add_transfer(
VkDeviceSize src_offset = 0; VkDeviceSize src_offset = 0;
for(uint32_t i = 0; i < transfer->count; i++) { for(uint32_t i = 0; i < transfer->count; i++) {
/*
Adding size 32 write to 0x4b000000004b at offset 0
size = 32, offset = 0, info->offset = 0
Adding size 8 write to 0x4b000000004b at offset 44
size = 8, offset = 44, info->offset = 0
Transferring 32 bytes: 90605F3D228D5A448B925AC4C8FA45429E195A42E0EE36C4E0CA214400B07F3F to 0x4b000000004b
*/
VkDeviceSize diff = offset - transfer->infos[i].offset; VkDeviceSize diff = offset - transfer->infos[i].offset;
if(transfer->infos[i].buffer == buffer if(transfer->infos[i].buffer == buffer
&& transfer->infos[i].offset <= offset && transfer->infos[i].offset <= offset

@ -4,6 +4,43 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
// cos(I*PI/3)/2, sin(I*PI/3)/2
vec3 hex_vertices[] = {
{ 1.0/2, 0, 0},
{ 1.0/4, 0, SQRT3/4},
{-1.0/4, 0, SQRT3/4},
{-1.0/2, 0, 0},
{-1.0/4, 0, -SQRT3/4},
{ 1.0/4, 0, -SQRT3/4},
};
vec3 hex_starts[] = {
{ 0, 0, HEX_Z},
{-HEX_X, 0, HEX_Z/2},
{-HEX_X, 0, -HEX_Z/2},
{ 0, 0, -HEX_Z},
{ HEX_X, 0, -HEX_Z/2},
{ HEX_X, 0, HEX_Z/2},
};
vec3 hex_directions[] = {
{-HEX_X, 0, -HEX_Z/2},
{ 0, 0, -HEX_Z},
{ HEX_X, 0, -HEX_Z/2},
{ HEX_X, 0, HEX_Z/2},
{ 0, 0, HEX_Z},
{-HEX_X, 0, HEX_Z/2},
};
int hex_indices[] = {
0, 2, 1,
0, 3, 2,
0, 4, 3,
0, 5, 4,
0, 6, 5,
0, 1, 6,
};
VkResult create_ray_pipeline( VkResult create_ray_pipeline(
RenderContext* gpu, RenderContext* gpu,
GraphicsPipeline* pipeline) { GraphicsPipeline* pipeline) {
@ -475,20 +512,24 @@ VkResult create_hex_context(
VkResult result; VkResult result;
VK_RESULT(create_hex_pipeline(gpu, &context->graphics)); VK_RESULT(create_hex_pipeline(gpu, &context->graphics));
#ifdef DRAW_HEX_RAYS
VK_RESULT(create_ray_pipeline(gpu, &context->ray_pipeline)); VK_RESULT(create_ray_pipeline(gpu, &context->ray_pipeline));
#endif
VK_RESULT(create_hex_highlight_pipeline(gpu, &context->highlight_pipeline)); VK_RESULT(create_hex_highlight_pipeline(gpu, &context->highlight_pipeline));
memset(&context->data, 0, sizeof(GPUHexContext)); memset(&context->data, 0, sizeof(GPUHexContext));
glm_perspective(PERSPECTIVE_FOVY, (float)gpu->swapchain_extent.width/(float)gpu->swapchain_extent.height, PERSPECTIVE_NEARZ, PERSPECTIVE_FARZ, context->data.proj); glm_perspective(PERSPECTIVE_FOVY, (float)gpu->swapchain_extent.width/(float)gpu->swapchain_extent.height, PERSPECTIVE_NEARZ, PERSPECTIVE_FARZ, context->data.proj);
glm_mat4_identity(context->data.view); glm_mat4_identity(context->data.view);
VK_RESULT(create_storage_buffer( for(uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
gpu->allocator, VK_RESULT(create_storage_buffer(
0, gpu->allocator,
sizeof(GPUHexContext), 0,
&context->context, sizeof(GPUHexContext),
&context->context_memory)); &context->context[i],
context->address = buffer_address(gpu->device, context->context); &context->context_memory[i]));
VK_RESULT(add_transfer(&context->data, context->context, 0, sizeof(GPUHexContext), gpu->current_frame, gpu)); context->address[i] = buffer_address(gpu->device, context->context[i]);
}
VK_RESULT(add_transfers(&context->data, context->context, 0, sizeof(GPUHexContext), gpu));
return VK_SUCCESS; return VK_SUCCESS;
} }
@ -522,8 +563,8 @@ VkResult set_hex_region(int32_t q, int32_t r, int32_t y, uint32_t map, HexRegion
VK_RESULT(add_transfer(&(*region)->data.q, (*region)->region, offsetof(GPUHexRegion, q), sizeof(int32_t)*3 + sizeof(uint32_t), gpu->current_frame, gpu)); VK_RESULT(add_transfer(&(*region)->data.q, (*region)->region, offsetof(GPUHexRegion, q), sizeof(int32_t)*3 + sizeof(uint32_t), gpu->current_frame, gpu));
(*region)->address = buffer_address(gpu->device, (*region)->region); (*region)->address = buffer_address(gpu->device, (*region)->region);
// TODO: load hex data before setting address so that data is synchronized
VK_RESULT(add_transfer(&(*region)->address, hex->context, offsetof(GPUHexContext, regions) + sizeof(VkDeviceAddress)*i, sizeof(VkDeviceAddress), gpu->current_frame, gpu)); VK_RESULT(add_transfers(&(*region)->address, hex->context, offsetof(GPUHexContext, regions) + sizeof(VkDeviceAddress)*i, sizeof(VkDeviceAddress), gpu));
return VK_SUCCESS; return VK_SUCCESS;
} }

@ -56,76 +56,14 @@ void* network_thread(void* data) {
return NULL; return NULL;
} }
// cos(I*PI/3)/2, sin(I*PI/3)/2 bool ray_hex_intersect(float* distance, vec3 start, vec3 dir, vec3 region_offset, uint32_t hex_index, HexRegion* region) {
vec3 hex_vertices[] = {
{ 1.0/2, 0, 0},
{ 1.0/4, 0, SQRT3/4},
{-1.0/4, 0, SQRT3/4},
{-1.0/2, 0, 0},
{-1.0/4, 0, -SQRT3/4},
{ 1.0/4, 0, -SQRT3/4},
};
vec3 hex_starts[] = {
{ 0, 0, HEX_Z},
{-HEX_X, 0, HEX_Z/2},
{-HEX_X, 0, -HEX_Z/2},
{ 0, 0, -HEX_Z},
{ HEX_X, 0, -HEX_Z/2},
{ HEX_X, 0, HEX_Z/2},
};
vec3 hex_directions[] = {
{-HEX_X, 0, -HEX_Z/2},
{ 0, 0, -HEX_Z},
{ HEX_X, 0, -HEX_Z/2},
{ HEX_X, 0, HEX_Z/2},
{ 0, 0, HEX_Z},
{-HEX_X, 0, HEX_Z/2},
};
int hex_indices[] = {
0, 2, 1,
0, 3, 2,
0, 4, 3,
0, 5, 4,
0, 6, 5,
0, 1, 6,
};
bool ray_hex_intersect(float* distance, vec4 ray_start, vec4 ray_end, uint32_t region_index, uint32_t hex_index, HexContext* context) {
/* /*
Ray-hexagon intersection Ray-hexagon intersection
1. For each triangle in the hexagon, check for intersection 1. For each triangle in the hexagon, check for intersection
2. If intersections, return the closest along the ray 2. If intersections, return the closest along the ray
*/ */
GPUHexRegion* region = &context->regions[region_index]->data;
GPUHex* hex = &region->hexes[hex_index];
vec3 start; GPUHex* hex = &region->data.hexes[hex_index];
start[0] = ray_start[0]/ray_start[3];
start[1] = ray_start[1]/ray_start[3];
start[2] = ray_start[2]/ray_start[3];
vec3 end;
end[0] = ray_end[0]/ray_end[3];
end[1] = ray_end[1]/ray_end[3];
end[2] = ray_end[2]/ray_end[3];
vec3 dir;
dir[0] = end[0] - start[0];
dir[1] = end[1] - start[1];
dir[2] = end[2] - start[2];
float dir_len = glm_vec3_norm(dir);
glm_vec3_divs(dir, dir_len, dir);
vec3 region_offset = {
((float)region->q + (float)region->r/2)*REGION_WIDTH - region->r*HEX_X/2,
0,
0.75*region->r*REGION_HEIGHT + 0.25*region->r*HEX_Z + 0.5*region->q*HEX_Z,
};
float center_height = (hex->height[0] float center_height = (hex->height[0]
+ hex->height[1] + hex->height[1]
@ -207,6 +145,74 @@ bool ray_hex_intersect(float* distance, vec4 ray_start, vec4 ray_end, uint32_t r
return intersect; return intersect;
} }
bool ray_region_intersect(float* distance, uint32_t* hid, vec3 start, vec3 dir, HexRegion* region) {
bool intersect = false;
float temp_distance;
*distance = INFINITY;
vec3 region_offset = {
((float)region->data.q + (float)region->data.r/2)*REGION_WIDTH - region->data.r*HEX_X/2,
0,
0.75*region->data.r*REGION_HEIGHT + 0.25*region->data.r*HEX_Z + 0.5*region->data.q*HEX_Z,
};
for(uint32_t temp_hid = 0; temp_hid < REGION_HEX_COUNT; temp_hid++) {
if(ray_hex_intersect(&temp_distance, start, dir, region_offset, temp_hid, region)) {
if(temp_distance < *distance) {
intersect = true;
*hid = temp_hid;
*distance = temp_distance;
}
}
}
return intersect;
}
bool ray_world_intersect(float* distance, uint32_t* rid, uint32_t* hid, vec4 ray_start, vec4 ray_end, HexContext* context) {
vec3 start;
start[0] = ray_start[0]/ray_start[3];
start[1] = ray_start[1]/ray_start[3];
start[2] = ray_start[2]/ray_start[3];
vec3 end;
end[0] = ray_end[0]/ray_end[3];
end[1] = ray_end[1]/ray_end[3];
end[2] = ray_end[2]/ray_end[3];
vec3 dir;
dir[0] = end[0] - start[0];
dir[1] = end[1] - start[1];
dir[2] = end[2] - start[2];
float mdir = glm_vec3_norm(dir);
glm_vec3_divs(dir, mdir, dir);
bool intersect = false;
float temp_distance;
uint32_t temp_hid;
*distance = INFINITY;
for(uint32_t temp_rid = 0; temp_rid < MAX_LOADED_REGIONS; temp_rid++) {
HexRegion* region = context->regions[temp_rid];
if(region == NULL) {
continue;
} else if(region->data.map != context->data.current_map) {
continue;
}
if(ray_region_intersect(&temp_distance, &temp_hid, start, dir, region)) {
if(temp_distance < *distance) {
intersect = true;
*hid = temp_hid;
*rid = temp_rid;
*distance = temp_distance;
}
}
}
return intersect;
}
vec3 up = {0, 1, 0}; vec3 up = {0, 1, 0};
VkResult main_thread(ClientContext* context) { VkResult main_thread(ClientContext* context) {
@ -251,7 +257,7 @@ VkResult main_thread(ClientContext* context) {
}; };
context->hex->data.current_map = 0x01; context->hex->data.current_map = 0x01;
add_transfer(&context->hex->data.current_map, context->hex->context, offsetof(GPUHexContext, current_map), sizeof(uint32_t), context->render->current_frame, context->render); add_transfers(&context->hex->data.current_map, context->hex->context, offsetof(GPUHexContext, current_map), sizeof(uint32_t), context->render);
uint32_t region = 0; uint32_t region = 0;
for(int32_t q = -5; q < 5; q++) { for(int32_t q = -5; q < 5; q++) {
@ -264,7 +270,7 @@ VkResult main_thread(ClientContext* context) {
} }
regions[region]->data.hexes[i].color[6] = colors[(q+r+50) % (sizeof(colors)/sizeof(uint32_t))]; regions[region]->data.hexes[i].color[6] = colors[(q+r+50) % (sizeof(colors)/sizeof(uint32_t))];
} }
VK_RESULT(add_transfer(&regions[region]->data.hexes, regions[region]->region, offsetof(GPUHexRegion, hexes), sizeof(GPUHex)*REGION_HEX_COUNT, 0, context->render)); VK_RESULT(add_transfer(&regions[region]->data.hexes, regions[region]->region, offsetof(GPUHexRegion, hexes), sizeof(GPUHex)*REGION_HEX_COUNT, context->render->current_frame, context->render));
region++; region++;
} }
} }
@ -302,12 +308,11 @@ VkResult main_thread(ClientContext* context) {
PERSPECTIVE_NEARZ, PERSPECTIVE_NEARZ,
PERSPECTIVE_FARZ, PERSPECTIVE_FARZ,
context->hex->data.proj); context->hex->data.proj);
VK_RESULT(add_transfer( VK_RESULT(add_transfers(
&context->hex->data.proj, &context->hex->data.proj,
context->hex->context, context->hex->context,
offsetof(GPUHexContext, proj), offsetof(GPUHexContext, proj),
sizeof(mat4), sizeof(mat4),
context->render->current_frame,
context->render)); context->render));
} }
@ -343,7 +348,7 @@ VkResult main_thread(ClientContext* context) {
glm_mat4_mul(context->hex->data.proj, context->hex->data.view, regular); glm_mat4_mul(context->hex->data.proj, context->hex->data.view, regular);
glm_mat4_inv(regular, context->inverse); glm_mat4_inv(regular, context->inverse);
add_transfer(&context->hex->data, context->hex->context, 0, 2*sizeof(mat4), context->render->current_frame, context->render); add_transfers(&context->hex->data, context->hex->context, 0, 2*sizeof(mat4), context->render);
} }
if(context->clicked_hex[0] != 0 || context->clicked_hex[1] != 0) { if(context->clicked_hex[0] != 0 || context->clicked_hex[1] != 0) {
@ -471,24 +476,32 @@ void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
break; break;
case GLFW_MOUSE_BUTTON_LEFT: case GLFW_MOUSE_BUTTON_LEFT:
if(action == GLFW_PRESS) { if(action == GLFW_PRESS) {
cursor_to_world_ray(context, cursor, context->hex->data.click_start, context->hex->data.click_end); cursor_to_world_ray(
add_transfer(&context->hex->data.click_start, context->hex->context, offsetof(GPUHexContext, click_start), sizeof(vec4)*2, context->render->current_frame, context->render); context,
cursor,
context->hex->data.click_start, context->hex->data.click_end);
add_transfers(
&context->hex->data.click_start,
context->hex->context,
offsetof(GPUHexContext, click_start),
sizeof(vec4)*2,
context->render);
// Hex intersections // Hex intersections
float distance; float distance;
for(uint32_t r = 0; r < MAX_LOADED_REGIONS; r++) { if(ray_world_intersect(
if(context->hex->regions[r] == NULL) { &distance,
continue; &context->hex->data.clicked_region,
} else if(context->hex->regions[r]->data.map != context->hex->data.current_map) { &context->hex->data.clicked_hex,
continue; context->hex->data.click_start,
} context->hex->data.click_end,
for(uint32_t h = 0; h < REGION_HEX_COUNT; h++) { context->hex)) {
if(ray_hex_intersect(&distance, context->hex->data.click_start, context->hex->data.click_end, r, h, context->hex)) { add_transfers(
context->hex->data.clicked_region = r; &context->hex->data.clicked_region,
context->hex->data.clicked_hex = h; context->hex->context,
add_transfer(&context->hex->data.clicked_region, context->hex->context, offsetof(GPUHexContext, clicked_region), sizeof(uint32_t)*2, context->render->current_frame, context->render); offsetof(GPUHexContext, clicked_region),
} sizeof(uint32_t)*2,
} context->render);
} }
// UI intersections // UI intersections
@ -533,23 +546,28 @@ void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos) {
context->cursor[0] = xpos; context->cursor[0] = xpos;
context->cursor[1] = ypos; context->cursor[1] = ypos;
cursor_to_world_ray(context, context->cursor, context->hex->data.hover_start, context->hex->data.hover_end); cursor_to_world_ray(context, context->cursor, context->hex->data.hover_start, context->hex->data.hover_end);
add_transfer(&context->hex->data.hover_start, context->hex->context, offsetof(GPUHexContext, hover_start), sizeof(vec4)*2, context->render->current_frame, context->render); add_transfers(
&context->hex->data.hover_start,
context->hex->context,
offsetof(GPUHexContext, hover_start),
sizeof(vec4)*2,
context->render);
// Hex intersections // Hex intersections
float distance; float distance;
for(uint32_t r = 0; r < MAX_LOADED_REGIONS; r++) { if(ray_world_intersect(
if(context->hex->regions[r] == NULL) { &distance,
continue; &context->hex->data.hovered_region,
} else if(context->hex->regions[r]->data.map != context->hex->data.current_map) { &context->hex->data.hovered_hex,
continue; context->hex->data.hover_start,
} context->hex->data.hover_end,
for(uint32_t h = 0; h < REGION_HEX_COUNT; h++) { context->hex)) {
if(ray_hex_intersect(&distance, context->hex->data.hover_start, context->hex->data.hover_end, r, h, context->hex)) { add_transfers(
context->hex->data.hovered_region = r; &context->hex->data.hovered_region,
context->hex->data.hovered_hex = h; context->hex->context,
add_transfer(&context->hex->data.hovered_region, context->hex->context, offsetof(GPUHexContext, hovered_region), sizeof(uint32_t)*2, context->render->current_frame, context->render); offsetof(GPUHexContext, hovered_region),
} sizeof(uint32_t)*2,
} context->render);
} }
} }