|  |  |  | @ -16,25 +16,6 @@ typedef struct ClientContextStruct { | 
		
	
		
			
				|  |  |  |  |   UIContext ui; | 
		
	
		
			
				|  |  |  |  | } ClientContext; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | void record_ui_compute(VkCommandBuffer command_buffer, UIContext* ui, uint32_t frame) { | 
		
	
		
			
				|  |  |  |  |   UIPushConstant push = { | 
		
	
		
			
				|  |  |  |  |     .time = 0.0, | 
		
	
		
			
				|  |  |  |  |     .layer = 0, | 
		
	
		
			
				|  |  |  |  |   }; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, ui->string_pipeline.pipeline); | 
		
	
		
			
				|  |  |  |  |   for(uint32_t i = 0; i < ui->max_containers; i++) { | 
		
	
		
			
				|  |  |  |  |     if(ui->containers[i].id != 0x00000000) { | 
		
	
		
			
				|  |  |  |  |       for(uint32_t j = 0; j < ui->containers[i].layer_count; j++) { | 
		
	
		
			
				|  |  |  |  |         push.layer = ui->containers[i].layers[j].address[frame]; | 
		
	
		
			
				|  |  |  |  |         command_copy_buffer(command_buffer, ui->containers[i].layers[j].layer[frame], ui->containers[i].layers[j].layer[frame], offsetof(GPULayer, num_drawables), offsetof(GPULayer, draw) + offsetof(DrawCommand, instance_count), sizeof(uint32_t)); | 
		
	
		
			
				|  |  |  |  |         vkCmdPushConstants(command_buffer, ui->string_pipeline.layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, 16, &push); | 
		
	
		
			
				|  |  |  |  |         vkCmdDispatchIndirect(command_buffer, ui->containers[i].layers[j].layer[frame], offsetof(GPULayer, dispatch_strings)); | 
		
	
		
			
				|  |  |  |  |       } | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | VkResult test_ui(RenderContext* gpu, UIContext* ui) { | 
		
	
		
			
				|  |  |  |  |   VkResult result; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -173,39 +154,6 @@ VkResult test_ui(RenderContext* gpu, UIContext* ui) { | 
		
	
		
			
				|  |  |  |  |   return VK_SUCCESS; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | // Threads:
 | 
		
	
		
			
				|  |  |  |  | // 1. render
 | 
		
	
		
			
				|  |  |  |  | //  - Submits the draw buffer to the GPU as soon as it can
 | 
		
	
		
			
				|  |  |  |  | // 2. network
 | 
		
	
		
			
				|  |  |  |  | //  - Handles packets to/from the network to/from the main thread
 | 
		
	
		
			
				|  |  |  |  | // 3. main
 | 
		
	
		
			
				|  |  |  |  | //  - updates the data in the GPU that's being drawn from
 | 
		
	
		
			
				|  |  |  |  | //  - updates the data in the GPU from network requests
 | 
		
	
		
			
				|  |  |  |  | //
 | 
		
	
		
			
				|  |  |  |  | //  Data:
 | 
		
	
		
			
				|  |  |  |  | //  Render thread reads Render and UI context 
 | 
		
	
		
			
				|  |  |  |  | //  Main thread reads and writes UI context
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | void* render_thread(void* data) { | 
		
	
		
			
				|  |  |  |  |   ClientContext* context = (ClientContext*)data; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   double last_frame_time = glfwGetTime(); | 
		
	
		
			
				|  |  |  |  |   while(glfwWindowShouldClose(context->window) == 0) { | 
		
	
		
			
				|  |  |  |  |     double frame_time = glfwGetTime(); | 
		
	
		
			
				|  |  |  |  |     double delta_time = frame_time - last_frame_time; | 
		
	
		
			
				|  |  |  |  |     (void)delta_time; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     VkResult result = draw_frame(&context->render, &context->ui, frame_time); | 
		
	
		
			
				|  |  |  |  |     if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |       fprintf(stderr, "draw_frame error: %s\n", string_VkResult(result)); | 
		
	
		
			
				|  |  |  |  |       glfwDestroyWindow(context->window); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     last_frame_time = frame_time; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return NULL; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | void* network_thread(void* data) { | 
		
	
		
			
				|  |  |  |  |   ClientContext* context = (ClientContext*)data; | 
		
	
		
			
				|  |  |  |  |   (void)context; | 
		
	
	
		
			
				
					|  |  |  | @ -213,46 +161,82 @@ void* network_thread(void* data) { | 
		
	
		
			
				|  |  |  |  |   return NULL; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | int main_thread(void* data) { | 
		
	
		
			
				|  |  |  |  |   ClientContext* context = (ClientContext*)data; | 
		
	
		
			
				|  |  |  |  | int main_thread(ClientContext* context) { | 
		
	
		
			
				|  |  |  |  |   GPUString fps_string = { | 
		
	
		
			
				|  |  |  |  |     .pos = {0, 32}, | 
		
	
		
			
				|  |  |  |  |     .size = 32, | 
		
	
		
			
				|  |  |  |  |     .color = {1.0, 1.0, 1.0, 1.0}, | 
		
	
		
			
				|  |  |  |  |     .offset = 0, | 
		
	
		
			
				|  |  |  |  |     .length = 4, | 
		
	
		
			
				|  |  |  |  |     .font = 0, | 
		
	
		
			
				|  |  |  |  |   }; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   LayerInput fps_layer = { | 
		
	
		
			
				|  |  |  |  |     .num_strings = 1, | 
		
	
		
			
				|  |  |  |  |     .strings = &fps_string, | 
		
	
		
			
				|  |  |  |  |     .max_codes = 10, | 
		
	
		
			
				|  |  |  |  |   }; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   ContainerInput fps_container = { | 
		
	
		
			
				|  |  |  |  |     .id = 1, | 
		
	
		
			
				|  |  |  |  |     .size = {200, 200}, | 
		
	
		
			
				|  |  |  |  |     .layer_count = 1, | 
		
	
		
			
				|  |  |  |  |     .layers = &fps_layer, | 
		
	
		
			
				|  |  |  |  |   }; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   create_container(&fps_container, &context->render, &context->ui); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   int x = 0; | 
		
	
		
			
				|  |  |  |  |   double last_draw = -1; | 
		
	
		
			
				|  |  |  |  |   double draw_interval = 1; | 
		
	
		
			
				|  |  |  |  |   double frame_count = 0; | 
		
	
		
			
				|  |  |  |  |   while(glfwWindowShouldClose(context->window) == 0) { | 
		
	
		
			
				|  |  |  |  |     glfwPollEvents(); | 
		
	
		
			
				|  |  |  |  |     if(x == 0 && glfwGetTime() > 0.0) { | 
		
	
		
			
				|  |  |  |  |       x = 1; | 
		
	
		
			
				|  |  |  |  |       test_ui(&context->render, &context->ui); | 
		
	
		
			
				|  |  |  |  |     double frame_time = glfwGetTime(); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     //
 | 
		
	
		
			
				|  |  |  |  |     if(frame_time - last_draw > draw_interval) { | 
		
	
		
			
				|  |  |  |  |       context->render.frame[context->render.current_frame].transfer_count = 2; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |       TransferInfo* transfer_infos = context->render.frame[context->render.current_frame].transfer_infos; | 
		
	
		
			
				|  |  |  |  |       transfer_infos[0].size = 10*sizeof(uint32_t); | 
		
	
		
			
				|  |  |  |  |       transfer_infos[0].dst_offset = 0; | 
		
	
		
			
				|  |  |  |  |       transfer_infos[0].buffers[0] = context->ui.containers[0].layers[0].codes[0]; | 
		
	
		
			
				|  |  |  |  |       transfer_infos[0].buffers[1] = context->ui.containers[0].layers[0].codes[1]; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |       transfer_infos[1].size = sizeof(GPUString); | 
		
	
		
			
				|  |  |  |  |       transfer_infos[1].dst_offset = 0; | 
		
	
		
			
				|  |  |  |  |       transfer_infos[1].buffers[0] = context->ui.containers[0].layers[0].strings[0]; | 
		
	
		
			
				|  |  |  |  |       transfer_infos[1].buffers[1] = context->ui.containers[0].layers[0].strings[1]; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |       void* mapped = context->render.frame[context->render.current_frame].transfer_mapped; | 
		
	
		
			
				|  |  |  |  |       uint32_t* mapped_codes = (uint32_t*)mapped; | 
		
	
		
			
				|  |  |  |  |       GPUString* mapped_string = (GPUString*)(mapped + 10*sizeof(uint32_t)); | 
		
	
		
			
				|  |  |  |  |       char str[11]; | 
		
	
		
			
				|  |  |  |  |       snprintf(str, 11, "%3.2f", frame_count/(frame_time-last_draw)); | 
		
	
		
			
				|  |  |  |  |       map_string(str, mapped_codes, 0, 0, &context->ui); | 
		
	
		
			
				|  |  |  |  |       mapped_string->size = 32; | 
		
	
		
			
				|  |  |  |  |       mapped_string->pos[0] = 0; | 
		
	
		
			
				|  |  |  |  |       mapped_string->pos[1] = 32; | 
		
	
		
			
				|  |  |  |  |       mapped_string->color[0] = 1.0; | 
		
	
		
			
				|  |  |  |  |       mapped_string->color[1] = 1.0; | 
		
	
		
			
				|  |  |  |  |       mapped_string->color[2] = 1.0; | 
		
	
		
			
				|  |  |  |  |       mapped_string->color[3] = 1.0; | 
		
	
		
			
				|  |  |  |  |       mapped_string->font = 0; | 
		
	
		
			
				|  |  |  |  |       mapped_string->offset = 0; | 
		
	
		
			
				|  |  |  |  |       mapped_string->length = strlen(str); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |       last_draw = frame_time; | 
		
	
		
			
				|  |  |  |  |       frame_count = 0; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |     //
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     for(uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { | 
		
	
		
			
				|  |  |  |  |       VkCommandBuffer command_buffer = command_begin_single(context->render.device, context->render.transfer_pool); | 
		
	
		
			
				|  |  |  |  |       record_ui_compute(command_buffer, &context->ui, i); | 
		
	
		
			
				|  |  |  |  |       vkEndCommandBuffer(command_buffer); | 
		
	
		
			
				|  |  |  |  |       FrameSync id = increment_transfer(&context->render.frame[i].id); | 
		
	
		
			
				|  |  |  |  |       VkSemaphore wait_semaphores[] = {context->render.frame[i].transfer, context->render.frame[i].frame}; | 
		
	
		
			
				|  |  |  |  |       uint64_t wait_values[] = {id.transfer, id.frame}; | 
		
	
		
			
				|  |  |  |  |       uint64_t signal_values[] = {id.transfer+1}; | 
		
	
		
			
				|  |  |  |  |       VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT}; | 
		
	
		
			
				|  |  |  |  |       VkTimelineSemaphoreSubmitInfo timeline_info = { | 
		
	
		
			
				|  |  |  |  |         .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, | 
		
	
		
			
				|  |  |  |  |         .pSignalSemaphoreValues = signal_values, | 
		
	
		
			
				|  |  |  |  |         .signalSemaphoreValueCount = 1, | 
		
	
		
			
				|  |  |  |  |         .pWaitSemaphoreValues = wait_values, | 
		
	
		
			
				|  |  |  |  |         .waitSemaphoreValueCount = 2, | 
		
	
		
			
				|  |  |  |  |       }; | 
		
	
		
			
				|  |  |  |  |       VkSubmitInfo submit_info = { | 
		
	
		
			
				|  |  |  |  |         .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, | 
		
	
		
			
				|  |  |  |  |         .commandBufferCount = 1, | 
		
	
		
			
				|  |  |  |  |         .pCommandBuffers = &command_buffer, | 
		
	
		
			
				|  |  |  |  |         .signalSemaphoreCount = 1, | 
		
	
		
			
				|  |  |  |  |         .pSignalSemaphores = &context->render.frame[i].transfer, | 
		
	
		
			
				|  |  |  |  |         .waitSemaphoreCount = 2, | 
		
	
		
			
				|  |  |  |  |         .pWaitSemaphores = wait_semaphores, | 
		
	
		
			
				|  |  |  |  |         .pWaitDstStageMask = wait_stages, | 
		
	
		
			
				|  |  |  |  |         .pNext = &timeline_info, | 
		
	
		
			
				|  |  |  |  |       }; | 
		
	
		
			
				|  |  |  |  |       vkQueueSubmit(context->render.transfer_queue.handle, 1, &submit_info, VK_NULL_HANDLE); | 
		
	
		
			
				|  |  |  |  |     VkResult result = draw_frame(&context->render, &context->ui, frame_time); | 
		
	
		
			
				|  |  |  |  |     if(result != VK_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |       fprintf(stderr, "draw_frame error: %s\n", string_VkResult(result)); | 
		
	
		
			
				|  |  |  |  |       glfwDestroyWindow(context->window); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     frame_count += 1; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return 0; | 
		
	
	
		
			
				
					|  |  |  | @ -327,15 +311,8 @@ int main() { | 
		
	
		
			
				|  |  |  |  |   // TODO: make # of fonts/textures/containers scaling, recreate GPU buffers as necessary
 | 
		
	
		
			
				|  |  |  |  |   VK_RESULT(create_ui_context(10, 10, 10, &context.render, &context.ui)); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   // Start threads
 | 
		
	
		
			
				|  |  |  |  |   pthread_t render_thread_handle; | 
		
	
		
			
				|  |  |  |  |   pthread_t network_thread_handle; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   error = pthread_create(&render_thread_handle, NULL, &render_thread, &context); | 
		
	
		
			
				|  |  |  |  |   if(error != 0) { | 
		
	
		
			
				|  |  |  |  |     return error; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   error = pthread_create(&network_thread_handle, NULL, &network_thread, &context); | 
		
	
		
			
				|  |  |  |  |   if(error != 0) { | 
		
	
		
			
				|  |  |  |  |     return error; | 
		
	
	
		
			
				
					|  |  |  | @ -346,11 +323,6 @@ int main() { | 
		
	
		
			
				|  |  |  |  |     return error; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   error = pthread_join(render_thread_handle, NULL); | 
		
	
		
			
				|  |  |  |  |   if(error != 0) { | 
		
	
		
			
				|  |  |  |  |     return error; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   error = pthread_join(network_thread_handle, NULL); | 
		
	
		
			
				|  |  |  |  |   if(error != 0) { | 
		
	
		
			
				|  |  |  |  |     return error; | 
		
	
	
		
			
				
					|  |  |  | 
 |