diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f84f506..16a168c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ on: jobs: generate-config: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: write-all outputs: BIRTH_GITHUB_TARGETS: ${{ steps.generate-config.outputs.BIRTH_GITHUB_TARGETS }} diff --git a/bootstrap/bloat-buster/bb_core.c b/bootstrap/bloat-buster/bb_core.c index 279561e..3de57fa 100644 --- a/bootstrap/bloat-buster/bb_core.c +++ b/bootstrap/bloat-buster/bb_core.c @@ -168,8 +168,14 @@ void run_app() window_rect_texture_update_end(renderer, render_window); + auto frame_start = os_timestamp(); + while (!os_window_should_close(os_window)) { + auto frame_end = os_timestamp(); + auto frame_ms = os_resolve_timestamps(frame_start, frame_end, TIME_UNIT_MILLISECONDS); + frame_start = frame_end; + os_poll_events(); auto mouse_position = os_window_cursor_position_get(os_window); @@ -177,6 +183,14 @@ void run_app() renderer_window_frame_begin(renderer, render_window); + u8 format_buffer[256]; + auto buffer_len = format_float((String)array_to_slice(format_buffer), frame_ms); + format_buffer[buffer_len + 0] = ' '; + format_buffer[buffer_len + 1] = 'm'; + format_buffer[buffer_len + 2] = 's'; + auto ms = (String) { .pointer = format_buffer, .length = buffer_len + 3 }; + draw_string(render_window, Color4(0, 1, 1, 1), ms, monospace_font, RECT_TEXTURE_SLOT_MONOSPACE_FONT, 500, 500); + u32 box_width = 100; u32 box_height = 100; auto box_color = Color4(1, 1, 1, 1); diff --git a/bootstrap/include/std/os.h b/bootstrap/include/std/os.h index c754d9c..b98b787 100644 --- a/bootstrap/include/std/os.h +++ b/bootstrap/include/std/os.h @@ -2,6 +2,20 @@ #include +typedef enum TimeUnit +{ + TIME_UNIT_NANOSECONDS, + TIME_UNIT_MICROSECONDS, + TIME_UNIT_MILLISECONDS, + TIME_UNIT_SECONDS, +} TimeUnit; + + +STRUCT(Timestamp) +{ + u128 value; +}; + STRUCT(OSFileOpenFlags) { u32 truncate:1; @@ -95,6 +109,9 @@ EXPORT void calibrate_cpu_timer(); EXPORT void print_string(String string); +EXPORT Timestamp os_timestamp(); +EXPORT f64 os_resolve_timestamps(Timestamp start, Timestamp end, TimeUnit time_unit); + #if _WIN32 typedef void* HANDLE; EXPORT HANDLE os_windows_get_module_handle(); diff --git a/bootstrap/std/os.c b/bootstrap/std/os.c index 89d4547..070e649 100644 --- a/bootstrap/std/os.c +++ b/bootstrap/std/os.c @@ -20,38 +20,6 @@ #include #endif -fn -#if _WIN32 -u64 -#else -#if LINK_LIBC -struct timespec -#else -u64 -#endif -#endif -timestamp() -{ -#ifdef _WIN32 - LARGE_INTEGER li; - QueryPerformanceCounter(&li); - return (u64)li.QuadPart; -#else -#if LINK_LIBC - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return ts; -#else -#if defined(__x86_64__) - return __rdtsc(); -#else - return 0; -#endif -#endif -#endif -} - - #if _WIN32 global_variable u64 cpu_frequency; #else @@ -62,6 +30,78 @@ global_variable u64 cpu_frequency; #endif #endif +Timestamp os_timestamp() +{ + Timestamp result; + +#if _WIN32 + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + result.value = li.QuadPart; +#else + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + result.value = ((u128)ts.tv_sec << 64) | ts.tv_nsec; +#endif + + return result; +} + +f64 os_resolve_timestamps(Timestamp start, Timestamp end, TimeUnit time_unit) +{ + f64 result; +#if _WIN32 + auto start_tick = (s64)start.value; + auto end_tick = (s64)end.value; + + auto seconds = (f64)(end_tick - start_tick) / cpu_frequency; + + switch (time_unit) + { + case TIME_UNIT_NANOSECONDS: + result = seconds * 1000000000.0; + break; + case TIME_UNIT_MICROSECONDS: + result = seconds * 1000000.0; + break; + case TIME_UNIT_MILLISECONDS: + result = seconds * 1000.0; + break; + case TIME_UNIT_SECONDS: + result = seconds; + break; + } +#else + auto segmented_nanoseconds = (s64)end.value - (s64)start.value; + auto segmented_seconds = (s64)(end.value >> 64) - (s64)(start.value >> 64); + if (segmented_nanoseconds < 0) + { + segmented_seconds -= 1; + segmented_nanoseconds += 1000000000; + } + + auto total_ns = segmented_seconds * 1000000000 + segmented_nanoseconds; + + switch (time_unit) + { + case TIME_UNIT_NANOSECONDS: + result = total_ns; + break; + case TIME_UNIT_MICROSECONDS: + result = total_ns / 1000.0; + break; + case TIME_UNIT_MILLISECONDS: + result = total_ns / 1000000.0; + break; + case TIME_UNIT_SECONDS: + result = total_ns / 1000000000.0; + break; + } +#endif + + return result; +} + FileDescriptor os_stdout_get() { #if _WIN32 @@ -73,95 +113,6 @@ FileDescriptor os_stdout_get() #endif } -typedef enum TimeUnit -{ - TIME_UNIT_NANOSECONDS, - TIME_UNIT_MICROSECONDS, - TIME_UNIT_MILLISECONDS, - TIME_UNIT_SECONDS, -} TimeUnit; - -may_be_unused fn f64 resolve_timestamp( -#if _WIN32 - u64 start, u64 end, -#else -#if LINK_LIBC - struct timespec start, struct timespec end, -#else - u64 start, u64 end, -#endif -#endif - TimeUnit time_unit) -{ -#if _WIN32 - auto s = (f64)(end - start) / (f64)cpu_frequency; - switch (time_unit) - { - case TIME_UNIT_NANOSECONDS: - return s * 1000000000.0; - case TIME_UNIT_MICROSECONDS: - return s * 1000000.0; - case TIME_UNIT_MILLISECONDS: - return s * 1000.0; - case TIME_UNIT_SECONDS: - return s; - } -#else -#if LINK_LIBC - assert(end.tv_sec >= start.tv_sec); - struct timespec result = { - .tv_sec = end.tv_sec - start.tv_sec, - .tv_nsec = end.tv_nsec - start.tv_nsec, - }; - - auto ns_in_a_sec = 1000000000; - if (result.tv_nsec < 0) - { - result.tv_sec -= 1; - result.tv_nsec += ns_in_a_sec; - } - - auto ns = result.tv_sec * ns_in_a_sec + result.tv_nsec; - switch (time_unit) - { - case TIME_UNIT_NANOSECONDS: - return (f64)ns; - case TIME_UNIT_MICROSECONDS: - return (f64)ns / 1000.0; - case TIME_UNIT_MILLISECONDS: - return (f64)ns / 1000000.0; - case TIME_UNIT_SECONDS: - return (f64)ns / 1000000000.0; - } -#else - assert(end >= start); - auto ticks = end - start; - f64 s = (f64)(end - start); - if (cpu_frequency) - { - s = s / (f64)cpu_frequency; - - switch (time_unit) - { - case TIME_UNIT_NANOSECONDS: - return s * 1000000000.0; - case TIME_UNIT_MICROSECONDS: - return s * 1000000.0; - case TIME_UNIT_MILLISECONDS: - return s * 1000.0; - case TIME_UNIT_SECONDS: - return s; - } - } - else - { - // warning: rdtsc frequency not queried (returning ticks as are) - return s; - } -#endif -#endif -} - String path_dir(String string) { String result = {}; @@ -964,7 +915,7 @@ void calibrate_cpu_timer() clock_getres(CLOCK_MONOTONIC, &cpu_resolution); #else u64 miliseconds_to_wait = 100; - u64 cpu_start = timestamp(); + u64 cpu_start = os_timestamp(); u64 os_frequency = os_timer_freq(); u64 os_elapsed = 0; u64 os_start = os_timer_get(); @@ -976,7 +927,7 @@ void calibrate_cpu_timer() os_elapsed = os_end - os_start; } - u64 cpu_end = timestamp(); + u64 cpu_end = os_timestamp(); u64 cpu_elapsed = cpu_end - cpu_start; cpu_frequency = os_frequency * cpu_elapsed / os_elapsed; #endif @@ -1326,7 +1277,6 @@ void print(const char* format, ...) #endif } - static_assert(sizeof(Arena) == 64); Arena* arena_init(u64 reserved_size, u64 granularity, u64 initial_size) @@ -1480,7 +1430,7 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[]) print("\n"); #if _WIN32 - auto start_timestamp = timestamp(); + auto start_timestamp = os_timestamp(); u32 length = 0; for (u32 i = 0; i < arguments.length; i += 1) @@ -1508,7 +1458,7 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[]) } } bytes[byte_i - 1] = 0; - auto end_timestamp = timestamp(); + auto end_timestamp = os_timestamp(); PROCESS_INFORMATION process_information = {}; STARTUPINFOA startup_info = {}; @@ -1518,12 +1468,12 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[]) startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); auto handle_inheritance = 1; - auto start = timestamp(); + auto start = os_timestamp(); if (CreateProcessA(0, bytes, 0, 0, handle_inheritance, 0, 0, 0, &startup_info, &process_information)) { WaitForSingleObject(process_information.hProcess, INFINITE); - auto end = timestamp(); - auto ms = resolve_timestamp(start, end, TIME_UNIT_MILLISECONDS); + auto end = os_timestamp(); + auto ms = os_resolve_timestamps(start, end, TIME_UNIT_MILLISECONDS); print("Process ran in {f64} ms\n", ms); DWORD exit_code; @@ -1572,7 +1522,7 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[]) trap(); } - auto start_timestamp = timestamp(); + auto start_timestamp = os_timestamp(); if (pid == 0) { @@ -1592,7 +1542,7 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[]) int status = 0; int options = 0; pid_t result = syscall_waitpid(pid, &status, options); - auto end_timestamp = timestamp(); + auto end_timestamp = os_timestamp(); int success = 0; if (result == pid) { @@ -1629,7 +1579,7 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[]) print("Program failed to run!\n"); failed_execution(); } - auto ms = resolve_timestamp(start_timestamp, end_timestamp, TIME_UNIT_MILLISECONDS); + auto ms = os_resolve_timestamps(start_timestamp, end_timestamp, TIME_UNIT_MILLISECONDS); auto ticks = #if LINK_LIBC 0 diff --git a/bootstrap/std/render.c b/bootstrap/std/render.c index 17c8cbf..909ce1e 100644 --- a/bootstrap/std/render.c +++ b/bootstrap/std/render.c @@ -16,7 +16,6 @@ if (unlikely(result != VK_SUCCESS)) wrong_vulkan_result(result, strlit(#call), strlit(__FILE__), __LINE__); \ } while(0) - #define MAX_SWAPCHAIN_IMAGE_COUNT (16) #define MAX_FRAME_COUNT (2) #define MAX_DESCRIPTOR_SET_COUNT (16) @@ -29,8 +28,6 @@ #define MAX_DESCRIPTOR_SET_UPDATE_COUNT (16) #define MAX_LOCAL_BUFFER_COPY_COUNT (16) -#define DEFAULT_PIPELINE BB_PIPELINE_RECT - STRUCT(VulkanImageCreate) { u32 width; @@ -137,6 +134,20 @@ STRUCT(Renderer) TextureAtlas fonts[RENDER_FONT_TYPE_COUNT]; }; +STRUCT(PipelineInstantiation) +{ + VkWriteDescriptorSet descriptor_set_update; + VkDescriptorSet descriptor_sets[MAX_DESCRIPTOR_SET_COUNT]; + VkDescriptorImageInfo texture_descriptors[MAX_TEXTURE_UPDATE_COUNT]; +}; + +STRUCT(FramePipelineInstantiation) +{ + VertexBuffer vertex_buffer; + IndexBuffer index_buffer; + VulkanBuffer transient_buffer; +}; + STRUCT(WindowFrame) { VkCommandPool command_pool; @@ -147,16 +158,7 @@ STRUCT(WindowFrame) BBPipeline bound_pipeline; VkBuffer index_buffer; GPUDrawPushConstants push_constants; -}; - -STRUCT(PipelineInstantiation) -{ - VertexBuffer vertex_buffer; - IndexBuffer index_buffer; - VulkanBuffer transient_buffer; - VkWriteDescriptorSet descriptor_set_update; - VkDescriptorSet descriptor_sets[MAX_DESCRIPTOR_SET_COUNT]; - VkDescriptorImageInfo texture_descriptors[MAX_TEXTURE_UPDATE_COUNT]; + FramePipelineInstantiation pipeline_instantiations[BB_PIPELINE_COUNT]; }; STRUCT(RenderWindow) @@ -1558,14 +1560,21 @@ RenderWindow* renderer_window_initialize(Renderer* renderer, OSWindow window) VkSurfaceCapabilitiesKHR surface_capabilities; vkok(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(renderer->physical_device, result->surface, &surface_capabilities)); swapchain_recreate(renderer, result, surface_capabilities); - + + for (u64 frame_index = 0; frame_index < MAX_FRAME_COUNT; frame_index += 1) + { + for (u64 pipeline_index = 0; pipeline_index < BB_PIPELINE_COUNT; pipeline_index += 1) + { + result->frames[frame_index].pipeline_instantiations[pipeline_index].vertex_buffer.gpu.type = BUFFER_TYPE_VERTEX; + result->frames[frame_index].pipeline_instantiations[pipeline_index].index_buffer.gpu.type = BUFFER_TYPE_INDEX; + result->frames[frame_index].pipeline_instantiations[pipeline_index].transient_buffer.type = BUFFER_TYPE_STAGING; + } + } + for (u64 pipeline_index = 0; pipeline_index < BB_PIPELINE_COUNT; pipeline_index += 1) { auto* pipeline_descriptor = &renderer->pipelines[pipeline_index]; auto* pipeline_instantiation = &result->pipeline_instantiations[pipeline_index]; - pipeline_instantiation->vertex_buffer.gpu.type = BUFFER_TYPE_VERTEX; - pipeline_instantiation->index_buffer.gpu.type = BUFFER_TYPE_INDEX; - pipeline_instantiation->transient_buffer.type = BUFFER_TYPE_STAGING; u16 descriptor_type_counter[DESCRIPTOR_TYPE_COUNT] = {}; @@ -1696,7 +1705,7 @@ void renderer_window_frame_begin(Renderer* renderer, RenderWindow* window) // Reset frame data for (u32 i = 0; i < array_length(window->pipeline_instantiations); i += 1) { - auto* pipeline_instantiation = &window->pipeline_instantiations[i]; + auto* pipeline_instantiation = &frame->pipeline_instantiations[i]; pipeline_instantiation->vertex_buffer.cpu.length = 0; pipeline_instantiation->vertex_buffer.count = 0; pipeline_instantiation->index_buffer.cpu.length = 0; @@ -1742,29 +1751,29 @@ void renderer_window_frame_end(Renderer* renderer, RenderWindow* window) for (u32 i = 0; i < BB_PIPELINE_COUNT; i += 1) { - auto* pipeline_instantiation = &window->pipeline_instantiations[i]; + auto* frame_pipeline_instantiation = &frame->pipeline_instantiations[i]; - if (likely(pipeline_instantiation->vertex_buffer.cpu.length)) + if (likely(frame_pipeline_instantiation->vertex_buffer.cpu.length)) { - auto new_vertex_buffer_size = pipeline_instantiation->vertex_buffer.cpu.length * sizeof(*pipeline_instantiation->vertex_buffer.cpu.pointer); - auto new_index_buffer_size = pipeline_instantiation->index_buffer.cpu.length * sizeof(*pipeline_instantiation->index_buffer.cpu.pointer); + auto new_vertex_buffer_size = frame_pipeline_instantiation->vertex_buffer.cpu.length * sizeof(*frame_pipeline_instantiation->vertex_buffer.cpu.pointer); + auto new_index_buffer_size = frame_pipeline_instantiation->index_buffer.cpu.length * sizeof(*frame_pipeline_instantiation->index_buffer.cpu.pointer); auto new_transient_buffer_size = new_vertex_buffer_size + new_index_buffer_size; - buffer_ensure_capacity(renderer, &pipeline_instantiation->transient_buffer, new_transient_buffer_size); - buffer_ensure_capacity(renderer, &pipeline_instantiation->vertex_buffer.gpu, new_vertex_buffer_size); - buffer_ensure_capacity(renderer, &pipeline_instantiation->index_buffer.gpu, new_index_buffer_size); + buffer_ensure_capacity(renderer, &frame_pipeline_instantiation->transient_buffer, new_transient_buffer_size); + buffer_ensure_capacity(renderer, &frame_pipeline_instantiation->vertex_buffer.gpu, new_vertex_buffer_size); + buffer_ensure_capacity(renderer, &frame_pipeline_instantiation->index_buffer.gpu, new_index_buffer_size); - buffer_copy_to_host(pipeline_instantiation->transient_buffer, (Slice(HostBufferCopy)) array_to_slice(((HostBufferCopy[]) { + buffer_copy_to_host(frame_pipeline_instantiation->transient_buffer, (Slice(HostBufferCopy)) array_to_slice(((HostBufferCopy[]) { (HostBufferCopy) { .source = (String) { - .pointer = (u8*)pipeline_instantiation->vertex_buffer.cpu.pointer, + .pointer = (u8*)frame_pipeline_instantiation->vertex_buffer.cpu.pointer, .length = new_vertex_buffer_size, }, .destination_offset = 0, }, (HostBufferCopy) { .source = (String) { - .pointer = (u8*)pipeline_instantiation->index_buffer.cpu.pointer, + .pointer = (u8*)frame_pipeline_instantiation->index_buffer.cpu.pointer, .length = new_index_buffer_size, }, .destination_offset = new_vertex_buffer_size, @@ -1773,8 +1782,8 @@ void renderer_window_frame_end(Renderer* renderer, RenderWindow* window) buffer_copy_to_local_command(frame->command_buffer, (Slice(LocalBufferCopy)) array_to_slice(((LocalBufferCopy[]) { { - .destination = pipeline_instantiation->vertex_buffer.gpu, - .source = pipeline_instantiation->transient_buffer, + .destination = frame_pipeline_instantiation->vertex_buffer.gpu, + .source = frame_pipeline_instantiation->transient_buffer, .regions = array_to_slice(((LocalBufferCopyRegion[]) { { .source_offset = 0, @@ -1784,8 +1793,8 @@ void renderer_window_frame_end(Renderer* renderer, RenderWindow* window) })), }, { - .destination = pipeline_instantiation->index_buffer.gpu, - .source = pipeline_instantiation->transient_buffer, + .destination = frame_pipeline_instantiation->index_buffer.gpu, + .source = frame_pipeline_instantiation->transient_buffer, .regions = array_to_slice(((LocalBufferCopyRegion[]) { { .source_offset = new_vertex_buffer_size, @@ -1860,8 +1869,9 @@ void renderer_window_frame_end(Renderer* renderer, RenderWindow* window) { auto* pipeline = &renderer->pipelines[i]; auto* pipeline_instantiation = &window->pipeline_instantiations[i]; + auto* frame_pipeline_instantiation = &frame->pipeline_instantiations[i]; - if (likely(pipeline_instantiation->vertex_buffer.cpu.length)) + if (likely(frame_pipeline_instantiation->vertex_buffer.cpu.length)) { // Bind pipeline and descriptor sets { @@ -1877,15 +1887,15 @@ void renderer_window_frame_end(Renderer* renderer, RenderWindow* window) // Bind index buffer { - vkCmdBindIndexBuffer(frame->command_buffer, pipeline_instantiation->index_buffer.gpu.handle, 0, VK_INDEX_TYPE_UINT32); - frame->index_buffer = pipeline_instantiation->index_buffer.gpu.handle; + vkCmdBindIndexBuffer(frame->command_buffer, frame_pipeline_instantiation->index_buffer.gpu.handle, 0, VK_INDEX_TYPE_UINT32); + frame->index_buffer = frame_pipeline_instantiation->index_buffer.gpu.handle; // print("Binding descriptor sets: 0x{u64}\n", frame->index_buffer); } // Send vertex buffer and screen dimensions to the shader auto push_constants = (GPUDrawPushConstants) { - .vertex_buffer = pipeline_instantiation->vertex_buffer.gpu.address, + .vertex_buffer = frame_pipeline_instantiation->vertex_buffer.gpu.address, .width = window->width, .height = window->height, }; @@ -1896,7 +1906,7 @@ void renderer_window_frame_end(Renderer* renderer, RenderWindow* window) frame->push_constants = push_constants; } - vkCmdDrawIndexed(frame->command_buffer, pipeline_instantiation->index_buffer.cpu.length, 1, 0, 0, 0); + vkCmdDrawIndexed(frame->command_buffer, frame_pipeline_instantiation->index_buffer.cpu.length, 1, 0, 0, 0); } } @@ -2095,49 +2105,6 @@ TextureIndex renderer_texture_create(Renderer* renderer, TextureMemory texture_m return (TextureIndex) { .value = texture_index }; } - -// void window_bind_index_buffer(RenderWindow* window, BufferIndex index_buffer, u64 offset, IndexType index_type) -// { -// auto* frame = window_frame(window); -// auto* buffer = &buffers[index_buffer.value]; -// VkIndexType vk_index_type; -// switch (index_type) -// { -// case INDEX_TYPE_U16: -// vk_index_type = VK_INDEX_TYPE_UINT16; -// break; -// case INDEX_TYPE_U32: -// vk_index_type = VK_INDEX_TYPE_UINT32; -// break; -// } -// vkCmdBindIndexBuffer(frame->command_buffer, buffer->handle, offset, vk_index_type); -// } - -// void window_push_constants(RenderWindow* window, PipelineIndex pipeline_index, SliceP(void) memories) -// { -// auto* pipeline = &pipelines[pipeline_index.value]; -// auto* pipeline_layout = &pipeline_layouts[pipeline->layout.value]; -// auto* frame = window_frame(window); -// -// if (memories.length != pipeline_layout->push_constant_range_count) -// { -// failed_execution(); -// } -// -// for (u64 i = 0; i < memories.length; i += 1) -// { -// auto* memory = memories.pointer[i]; -// auto push_constant_range = pipeline_layout->push_constant_ranges[i]; -// vkCmdPushConstants(frame->command_buffer, pipeline_layout->handle, push_constant_range.stageFlags, push_constant_range.offset, push_constant_range.size, memory); -// } -// } - -// u64 buffer_address(BufferIndex buffer_index) -// { -// auto* buffer = &buffers[buffer_index.value]; -// return buffer->address; -// } - void window_draw_indexed(RenderWindow* window, u32 index_count, u32 instance_count, u32 first_index, s32 vertex_offset, u32 first_instance) { auto* frame = window_frame(window); @@ -2208,7 +2175,8 @@ void window_rect_texture_update_end(Renderer* renderer, RenderWindow* window) u32 window_pipeline_add_vertices(RenderWindow* window, BBPipeline pipeline_index, String vertex_memory, u32 vertex_count) { - auto* vertex_buffer = &window->pipeline_instantiations[pipeline_index].vertex_buffer; + auto* frame = window_frame(window); + auto* vertex_buffer = &frame->pipeline_instantiations[pipeline_index].vertex_buffer; vb_copy_string(&vertex_buffer->cpu, vertex_memory); auto vertex_offset = vertex_buffer->count; vertex_buffer->count = vertex_offset + vertex_count; @@ -2217,6 +2185,7 @@ u32 window_pipeline_add_vertices(RenderWindow* window, BBPipeline pipeline_index void window_pipeline_add_indices(RenderWindow* window, BBPipeline pipeline_index, Slice(u32) indices) { - auto* index_pointer = vb_add(&window->pipeline_instantiations[pipeline_index].index_buffer.cpu, indices.length); + auto* frame = window_frame(window); + auto* index_pointer = vb_add(&frame->pipeline_instantiations[pipeline_index].index_buffer.cpu, indices.length); memcpy(index_pointer, indices.pointer, indices.length * sizeof(*indices.pointer)); }