#include "command_list.h" #include "backend_api.h" #include #include #include #include RT_CVAR_SZ(r_PerFrameCommandListMemory, "Amount of memory to allocate for single frame command list management." " The total amount of memory will be this times the maximum number of frames in flight. (Default: 16 MB)", RT_MB(16)); static rt_arena _list_arenas[3]; static rt_arena *_current_arena = &_list_arenas[0]; static unsigned int _current_arena_idx = 0; static rt_mutex *_mutex; rt_result InitCommandLists(void) { _mutex = rtCreateMutex(); if (!_mutex) return RT_UNKNOWN_ERROR; for (unsigned int i = 0; i < RT_ARRAY_COUNT(_list_arenas); ++i) { rt_create_arena_result arena_res = rtCreateArena(NULL, r_PerFrameCommandListMemory.sz); if (!arena_res.ok) { rtDestroyMutex(_mutex); return RT_OUT_OF_MEMORY; } _list_arenas[i] = arena_res.arena; } return RT_SUCCESS; } void ShutdownCommandLists(void) { for (unsigned int i = 0; i < RT_ARRAY_COUNT(_list_arenas); ++i) { rtReleaseArena(&_list_arenas[i]); } rtDestroyMutex(_mutex); } void CommandListsOnBeginFrame(void) { _current_arena_idx = (_current_arena_idx + 1) % RT_ARRAY_COUNT(_list_arenas); _current_arena = &_list_arenas[_current_arena_idx]; rtArenaClear(_current_arena); } #define COMMAND_LIST_CAPACITY RT_KB(512) #define AVERAGE_COMMAND_DATA_SIZE sizeof(rt_draw_indirect_data) #define COMMAND_LIST_MAX_LENGTH (COMMAND_LIST_CAPACITY / AVERAGE_COMMAND_DATA_SIZE) /* Get a new render command list. */ RT_DLLEXPORT rt_begin_render_command_list_result rtBeginRenderCommandList(void) { size_t mem_required = COMMAND_LIST_MAX_LENGTH * sizeof(rt_render_command_header) + COMMAND_LIST_CAPACITY; rtLockMutex(_mutex); void *mem = rtArenaPush(_current_arena, mem_required); rtUnlockMutex(_mutex); if (!mem) { rtReportError("RENDERER", "Ran out of memory for command lists."); return (rt_begin_render_command_list_result){.result = RT_OUT_OF_MEMORY}; } rt_render_command_list list = { .headers = mem, .data = (void *)((rt_render_command_header *)mem + COMMAND_LIST_MAX_LENGTH), .length = 0u, .data_capacity = COMMAND_LIST_CAPACITY, .data_end = 0u }; return (rt_begin_render_command_list_result){ .result = RT_SUCCESS, .list = list }; } /* Lowlevel function that writes the data to the queue. */ RT_DLLEXPORT rt_result rtEncodeRenderCommand(rt_render_command_list *list, rt_render_command_type type, rt_render_queue queue, const void *data) { size_t data_size = 0u; switch (type) { case RT_RENDER_COMMAND_DRAW_INDIRECT: data_size = sizeof(rt_draw_indirect_data); break; default: rtReportError("RENDERER", "Invalid render command type %u", type); return RT_INVALID_VALUE; } if (list->length == COMMAND_LIST_MAX_LENGTH || (list->data_end + data_size) > list->data_capacity) { rtReportError("RENDERER", "Reached maximum lenght or capacity of command list."); return RT_OUT_OF_MEMORY; } list->headers[list->length].type = type; list->headers[list->length].target_queue = queue; ++list->length; char *dest = (char *)list->data + list->data_end; memcpy(dest, data, data_size); list->data_end += data_size; return RT_SUCCESS; } /* Submit a finished command list to the graphics device. The list will not be usable after this function is done. */ RT_DLLEXPORT rt_result rtSubmitCommandList(rt_render_command_list *list) { return g_device_i.SubmitCommandList(g_device_i.o, list); }