From a7339ffd537314b5e92686c3d7e64e1847010dd8 Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Tue, 6 Feb 2024 17:57:45 +0100 Subject: [PATCH] Add a semaphore type --- src/runtime/framegraph_processor.c | 611 +++++++++++++++++++++++++++++ src/runtime/threading_semaphore.c | 39 ++ 2 files changed, 650 insertions(+) create mode 100644 src/runtime/framegraph_processor.c create mode 100644 src/runtime/threading_semaphore.c diff --git a/src/runtime/framegraph_processor.c b/src/runtime/framegraph_processor.c new file mode 100644 index 0000000..bf25153 --- /dev/null +++ b/src/runtime/framegraph_processor.c @@ -0,0 +1,611 @@ +#include "asset_compiler.h" +#include "buffer_manager.h" +#include "description_parser.h" +#include "gfx.h" + +#include +#include + +typedef struct { + rt_render_target_info *render_targets; + rt_render_pass_info *render_passes; + rt_render_target_read *reads; + rt_render_target_write *writes; + uint32_t render_target_count; + uint32_t render_pass_count; + uint32_t read_capacity; + uint32_t write_capacity; + uint32_t read_count; + uint32_t write_count; +} rt_parsed_framegraph; + +static int RenderTargetExists(const rt_framegraph_info *framegraph, rt_render_target_id id) { + const rt_render_target_info *render_targets = rtResolveConstRelptr(&framegraph->render_targets); + for (uint32_t i = 0; i < framegraph->render_target_count; ++i) { + if (render_targets[i].id == id) + return 1; + } + return 0; +} + +static rt_result ParseFramegraph(const char *text, + size_t size, + const char *file_path, + rt_framegraph_info **p_framegraph, + rt_arena *arena) { + rt_parse_state state; + unsigned int root_list; + rt_result result = rtParseDescription(text, size, file_path, &root_list, &state, arena); + if (result != RT_SUCCESS) + return result; + + /* Push on the arena to ensure a continous memory layout */ + rt_framegraph_info *framegraph = RT_ARENA_PUSH_STRUCT_ZERO(arena, rt_framegraph_info); + if (!framegraph) + return RT_OUT_OF_MEMORY; + +#pragma region Render Targets + const rt_parsed_stmt *rt_list_stmt = rtFindStatement(&state, root_list, "render_targets"); + if (!rt_list_stmt) { + rtLog("AC", + "Framegraph %s does not contain a list of render targets. (\"render_targets\")", + file_path); + return RT_INVALID_VALUE; + } + if (rt_list_stmt->form != RT_STMT_FORM_LIST) { + rtLog("AC", "Expected a list as the value of \"render_targets\" in %s", file_path); + return RT_INVALID_VALUE; + } + const rt_parsed_stmt_list *rt_list = &state.statement_lists[rt_list_stmt->list_index]; + framegraph->render_target_count = rt_list->count; + + rt_render_target_info *render_targets = + RT_ARENA_PUSH_ARRAY_ZERO(arena, rt_render_target_info, framegraph->render_target_count); + if (!render_targets) + return RT_OUT_OF_MEMORY; + rtSetRelptr(&framegraph->render_targets, render_targets); + const rt_parsed_stmt *rendertarget_stmt = &state.statements[rt_list->first]; + for (uint32_t i = 0; i < rt_list->count; + ++i, rendertarget_stmt = &state.statements[rendertarget_stmt->next]) { + if (rendertarget_stmt->form != RT_STMT_FORM_LIST) { + rtLog("AC", + "Expected a list as the value of \"render_targets.%.*s\" in %s", + rendertarget_stmt->attribute.length, + rendertarget_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + + rt_render_target_info rt = {0}; + rt.id = rtCalculateRenderTargetID(rendertarget_stmt->attribute.start, + rendertarget_stmt->attribute.length); + + const rt_parsed_stmt *width_stmt = + rtFindStatement(&state, rendertarget_stmt->list_index, "width"); + if (!width_stmt) { + rtLog("AC", + "Could not find \"render_targets.%.*s.width\" in %s", + rendertarget_stmt->attribute.length, + rendertarget_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + if (width_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected a simple value for \"render_targets.%.*s.width\" in %s", + rendertarget_stmt->attribute.length, + rendertarget_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + if (rtCompareSpanToString(width_stmt->value, "SWAPCHAIN") == 0) + rt.width = RT_RENDER_TARGET_SIZE_SWAPCHAIN; + else + sscanf(width_stmt->value.start, "%u", &rt.width); + + const rt_parsed_stmt *height_stmt = + rtFindStatement(&state, rendertarget_stmt->list_index, "height"); + if (!height_stmt) { + rtLog("AC", + "Could not find \"render_targets.%.*s.height\" in %s", + rendertarget_stmt->attribute.length, + rendertarget_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + if (height_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected a simple value for \"render_targets.%.*s.height\" in %s", + rendertarget_stmt->attribute.length, + rendertarget_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + if (rtCompareSpanToString(height_stmt->value, "SWAPCHAIN") == 0) + rt.height = RT_RENDER_TARGET_SIZE_SWAPCHAIN; + else + sscanf(height_stmt->value.start, "%u", &rt.height); + + const rt_parsed_stmt *samples_stmt = + rtFindStatement(&state, rendertarget_stmt->list_index, "sample_count"); + if (!samples_stmt) { + rtLog("AC", + "Could not find \"render_targets.%.*s.sample_count\" in %s", + rendertarget_stmt->attribute.length, + rendertarget_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + if (samples_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected a simple value for \"render_targets.%.*s.sample_count\" in %s", + rendertarget_stmt->attribute.length, + rendertarget_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + sscanf(samples_stmt->value.start, "%u", &rt.sample_count); + + const rt_parsed_stmt *format_stmt = + rtFindStatement(&state, rendertarget_stmt->list_index, "format"); + if (!format_stmt) { + rtLog("AC", + "Could not find \"render_targets.%.*s.format\" in %s", + rendertarget_stmt->attribute.length, + rendertarget_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + if (format_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected a simple value for \"render_targets.%.*s.format\" in %s", + rendertarget_stmt->attribute.length, + rendertarget_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + if (rtCompareSpanToString(format_stmt->value, "R8G8B8A8_UNORM") == 0) + rt.format = RT_PIXEL_FORMAT_R8G8B8A8_UNORM; + else if (rtCompareSpanToString(format_stmt->value, "B8G8R8A8_UNORM") == 0) + rt.format = RT_PIXEL_FORMAT_B8G8R8A8_UNORM; + else if (rtCompareSpanToString(format_stmt->value, "R8G8B8A8_SRGB") == 0) + rt.format = RT_PIXEL_FORMAT_R8G8B8A8_SRGB; + else if (rtCompareSpanToString(format_stmt->value, "B8G8R8A8_SRGB") == 0) + rt.format = RT_PIXEL_FORMAT_B8G8R8A8_SRGB; + else if (rtCompareSpanToString(format_stmt->value, "R8G8B8_UNORM") == 0) + rt.format = RT_PIXEL_FORMAT_R8G8B8_UNORM; + else if (rtCompareSpanToString(format_stmt->value, "B8G8R8_UNORM") == 0) + rt.format = RT_PIXEL_FORMAT_B8G8R8_UNORM; + else if (rtCompareSpanToString(format_stmt->value, "R8G8B8_SRGB") == 0) + rt.format = RT_PIXEL_FORMAT_R8G8B8_SRGB; + else if (rtCompareSpanToString(format_stmt->value, "B8G8R8_SRGB") == 0) + rt.format = RT_PIXEL_FORMAT_B8G8R8_SRGB; + else if (rtCompareSpanToString(format_stmt->value, "DEPTH24_STENCIL8") == 0) + rt.format = RT_PIXEL_FORMAT_DEPTH24_STENCIL8; + else if (rtCompareSpanToString(format_stmt->value, "DEPTH32") == 0) + rt.format = RT_PIXEL_FORMAT_DEPTH32; + else if (rtCompareSpanToString(format_stmt->value, "SWAPCHAIN") == 0) + rt.format = RT_PIXEL_FORMAT_SWAPCHAIN; + else { + rt.format = RT_PIXEL_FORMAT_INVALID; + rtLog("AC", + "Invalid format value \"%.*s\" in \"render_targets.%.*s.format\" in %s", + format_stmt->value.length, + format_stmt->value.start, + rendertarget_stmt->attribute.length, + rendertarget_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + + render_targets[i] = rt; + } +#pragma endregion Render Targets + +#pragma region Render Passes + /* First count the total number of reads and writes */ + uint32_t total_reads = 0, total_writes = 0; + const rt_parsed_stmt *pass_list_stmt = rtFindStatement(&state, root_list, "passes"); + if (!pass_list_stmt) { + rtLog("Framegraph %s does not contain a list of passes (\"passes\")", file_path); + return RT_INVALID_VALUE; + } + if (pass_list_stmt->form != RT_STMT_FORM_LIST) { + rtLog("AC", "Expected a list as the value of \"passes\" in %s", file_path); + return RT_INVALID_VALUE; + } + const rt_parsed_stmt_list *pass_list = &state.statement_lists[pass_list_stmt->list_index]; + rt_render_pass_info *passes = + RT_ARENA_PUSH_ARRAY_ZERO(arena, rt_render_pass_info, pass_list->count); + if (!passes) + return RT_OUT_OF_MEMORY; + rtSetRelptr(&framegraph->render_passes, passes); + framegraph->render_pass_count = pass_list->count; + const rt_parsed_stmt *pass_stmt = &state.statements[pass_list->first]; + for (uint32_t i = 0; i < pass_list->count; + ++i, pass_stmt = &state.statements[pass_stmt->next]) { + if (pass_stmt->form != RT_STMT_FORM_LIST) { + rtLog("AC", + "Expected a list as the value of \"passes.%.*s\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + + passes[i].id = + rtCalculateRenderPassID(pass_stmt->attribute.start, pass_stmt->attribute.length); + + const rt_parsed_stmt *write_list_stmt = + rtFindStatement(&state, pass_stmt->list_index, "writes"); + if (write_list_stmt) { + if (write_list_stmt->form != RT_STMT_FORM_LIST) { + rtLog("AC", + "Expected a list as the value of \"passes.%.*s.writes\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + const rt_parsed_stmt_list *write_list = + &state.statement_lists[write_list_stmt->list_index]; + passes[i].write_render_target_count = write_list->count; + total_writes += write_list->count; + } else { + passes[i].write_render_target_count = 0; + rtSetRelptr(&passes[i].write_render_targets, NULL); + } + + const rt_parsed_stmt *read_list_stmt = + rtFindStatement(&state, pass_stmt->list_index, "reads"); + if (read_list_stmt) { + if (read_list_stmt->form != RT_STMT_FORM_LIST) { + rtLog("AC", + "Expected a list as the value of \"passes.%.*s.reads\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + const rt_parsed_stmt_list *read_list = + &state.statement_lists[read_list_stmt->list_index]; + passes[i].read_render_target_count = read_list->count; + total_reads += read_list->count; + } else { + passes[i].read_render_target_count = 0; + rtSetRelptr(&passes[i].read_render_targets, NULL); + } + } + /* Now fill the read & write lists */ + rt_render_target_read *reads = + RT_ARENA_PUSH_ARRAY_ZERO(arena, rt_render_target_read, total_reads); + rt_render_target_write *writes = + RT_ARENA_PUSH_ARRAY_ZERO(arena, rt_render_target_write, total_writes); + if (!reads && total_reads != 0) + return RT_OUT_OF_MEMORY; + if (!writes && total_writes != 0) + return RT_OUT_OF_MEMORY; + uint32_t reads_at = 0; + uint32_t writes_at = 0; + + pass_stmt = &state.statements[pass_list->first]; + for (uint32_t i = 0; i < pass_list->count; + ++i, pass_stmt = &state.statements[pass_stmt->next]) { + RT_ASSERT(pass_stmt->form == RT_STMT_FORM_LIST, ""); + const rt_parsed_stmt *write_list_stmt = + rtFindStatement(&state, pass_stmt->list_index, "writes"); + if (write_list_stmt) { + RT_ASSERT(write_list_stmt->form == RT_STMT_FORM_LIST, ""); + const rt_parsed_stmt_list *write_list = + &state.statement_lists[write_list_stmt->list_index]; + + rtSetRelptr(&passes[i].write_render_targets, &writes[writes_at]); + const rt_parsed_stmt *write_stmt = &state.statements[write_list->first]; + for (uint32_t j = 0; j < write_list->count; + ++j, write_stmt = &state.statements[write_stmt->next]) { + + if (write_stmt->form != RT_STMT_FORM_LIST) { + rtLog("AC", + "Expected a list as the value of \"passes.%.*s.writes.%.*s\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + write_stmt->attribute.length, + write_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + + rt_render_target_write *write = &writes[writes_at++]; + memset(write, 0, sizeof(*write)); + write->render_target = rtCalculateRenderTargetID(write_stmt->attribute.start, + write_stmt->attribute.length); + if (!RenderTargetExists(framegraph, write->render_target)) { + rtLog("AC", + "Referenced unknown render target \"%.s\" in \"passes.%.*s.writes.%.*s\" " + "in %s", + write_stmt->attribute.length, + write_stmt->attribute.start, + pass_stmt->attribute.length, + pass_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + + const rt_parsed_stmt *clear_value_stmt = + rtFindStatement(&state, write_stmt->list_index, "clear_value"); + if (clear_value_stmt) { + if (clear_value_stmt->form != RT_STMT_FORM_LIST) { + rtLog("AC", + "Expected a list as the value of " + "\"passes.%.*s.writes.%.*s.clear_value\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + write_stmt->attribute.length, + write_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + const rt_parsed_stmt *r_stmt = + rtFindStatement(&state, clear_value_stmt->list_index, "r"); + if (r_stmt) { + if (r_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected a float as the value of " + "\"passes.%.*s.writes.%.*s.clear_value.r\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + write_stmt->attribute.length, + write_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + sscanf(r_stmt->value.start, "%f", &write->clear.color.r); + } + const rt_parsed_stmt *g_stmt = + rtFindStatement(&state, clear_value_stmt->list_index, "g"); + if (g_stmt) { + if (g_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected a float as the value of " + "\"passes.%.*s.writes.%.*s.clear_value.g\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + write_stmt->attribute.length, + write_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + sscanf(g_stmt->value.start, "%f", &write->clear.color.g); + } + const rt_parsed_stmt *b_stmt = + rtFindStatement(&state, clear_value_stmt->list_index, "b"); + if (b_stmt) { + if (b_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected a float as the value of " + "\"passes.%.*s.writes.%.*s.clear_value.b\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + write_stmt->attribute.length, + write_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + sscanf(b_stmt->value.start, "%f", &write->clear.color.b); + } + const rt_parsed_stmt *a_stmt = + rtFindStatement(&state, clear_value_stmt->list_index, "a"); + if (a_stmt) { + if (a_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected a float as the value of " + "\"passes.%.*s.writes.%.*s.clear_value.a\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + write_stmt->attribute.length, + write_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + sscanf(a_stmt->value.start, "%f", &write->clear.color.a); + } + const rt_parsed_stmt *d_stmt = + rtFindStatement(&state, clear_value_stmt->list_index, "d"); + if (d_stmt) { + if (d_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected a float as the value of " + "\"passes.%.*s.writes.%.*s.clear_value.d\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + write_stmt->attribute.length, + write_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + sscanf(d_stmt->value.start, "%f", &write->clear.depth_stencil.depth); + } + const rt_parsed_stmt *s_stmt = + rtFindStatement(&state, clear_value_stmt->list_index, "s"); + if (s_stmt) { + if (s_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected an integer as the value of " + "\"passes.%.*s.writes.%.*s.clear_value.s\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + write_stmt->attribute.length, + write_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + sscanf(s_stmt->value.start, "%d", &write->clear.depth_stencil.stencil); + } + } + + const rt_parsed_stmt *clear_stmt = + rtFindStatement(&state, write_stmt->list_index, "clear"); + if (clear_stmt) { + if (clear_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected a YES or NO as the value of " + "\"passes.%.*s.writes.%.*s.clear\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + write_stmt->attribute.length, + write_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + if (rtCompareSpanToString(clear_stmt->value, "YES") == 0) { + write->flags |= RT_RENDER_TARGET_WRITE_CLEAR; + } else if (rtCompareSpanToString(clear_stmt->value, "NO") != 0) { + rtLog("AC", + "Expected a YES or NO as the value of " + "\"passes.%.*s.writes.%.*s.clear\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + write_stmt->attribute.length, + write_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + } + + const rt_parsed_stmt *discard_stmt = + rtFindStatement(&state, write_stmt->list_index, "discard"); + if (discard_stmt) { + if (discard_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected YES or NO as the value of " + "\"passes.%.*s.writes.%.*s.clear\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + write_stmt->attribute.length, + write_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + if (rtCompareSpanToString(discard_stmt->value, "YES") == 0) { + write->flags |= RT_RENDER_TARGET_WRITE_DISCARD; + } else if (rtCompareSpanToString(discard_stmt->value, "NO") != 0) { + rtLog("AC", + "Expected YES or NO as the value of " + "\"passes.%.*s.writes.%.*s.discard\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + write_stmt->attribute.length, + write_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + } + } + } + + const rt_parsed_stmt *read_list_stmt = + rtFindStatement(&state, pass_stmt->list_index, "reads"); + if (read_list_stmt) { + RT_ASSERT(read_list_stmt->form == RT_STMT_FORM_LIST, ""); + const rt_parsed_stmt_list *read_list = + &state.statement_lists[read_list_stmt->list_index]; + + rtSetRelptr(&passes[i].read_render_targets, &reads[reads_at]); + const rt_parsed_stmt *read_stmt = &state.statements[read_list->first]; + for (uint32_t j = 0; j < read_list->count; + ++j, read_stmt = &state.statements[read_stmt->next]) { + + if (read_stmt->form != RT_STMT_FORM_LIST) { + rtLog("AC", + "Expected a list as the value of \"passes.%.*s.reads.%.*s\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + read_stmt->attribute.length, + read_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + + rt_render_target_read *read = &reads[reads_at++]; + memset(read, 0, sizeof(*read)); + read->render_target = rtCalculateRenderTargetID(read_stmt->attribute.start, + read_stmt->attribute.length); + if (!RenderTargetExists(framegraph, read->render_target)) { + rtLog("AC", + "Referenced unknown render target \"%.s\" in \"passes.%.*s.reads.%.*s\" " + "in %s", + read_stmt->attribute.length, + read_stmt->attribute.start, + pass_stmt->attribute.length, + pass_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + + const rt_parsed_stmt *mode_stmt = + rtFindStatement(&state, read_stmt->list_index, "mode"); + if (mode_stmt) { + if (mode_stmt->form != RT_STMT_FORM_VALUE) { + rtLog("AC", + "Expected SAMPLED or INPUT_ATTACHMENT as the value of " + "\"passes.%.*s.writes.%.*s.mode\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + read_stmt->attribute.length, + read_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + if (rtCompareSpanToString(mode_stmt->value, "SAMPLED") == 0) { + read->mode = RT_RENDER_TARGET_READ_SAMPLED; + } else if (rtCompareSpanToString(mode_stmt->value, "INPUT_ATTACHMENT") == 0) { + read->mode = RT_RENDER_TARGET_READ_INPUT_ATTACHMENT; + } else { + rtLog("AC", + "Expected SAMPLED or INPUT_ATTACHMENT as the value of " + "\"passes.%.*s.writes.%.*s.mode\" in %s", + pass_stmt->attribute.length, + pass_stmt->attribute.start, + read_stmt->attribute.length, + read_stmt->attribute.start, + file_path); + return RT_INVALID_VALUE; + } + } + } + } + } +#pragma region + + *p_framegraph = framegraph; + return result; +} + +RT_ASSET_PROCESSOR_FN(FramegraphProcessor) { + rt_loaded_asset asset = LoadAsset(file); + if (!asset.buffer) + return RT_UNKNOWN_ERROR; + + rt_framegraph_info *framegraph = NULL; + rt_result result = + ParseFramegraph(asset.buffer, asset.size, rtGetFilePath(file), &framegraph, arena); + if (result != RT_SUCCESS) + goto out; + + rt_resource resource = {0}; + resource.type = RT_RESOURCE_FRAMEGRAPH; + resource.data = framegraph; + + rt_resource_id id; + const char *name = rtGetFilePath(file); + result = rtCreateResources(1, &name, &resource, &id); + if (result != RT_SUCCESS) + goto out; + + new_resources[0] = id; + *new_resource_count = 1; + +out: + rtReleaseBuffer(asset.buffer, asset.size); + return result; +} \ No newline at end of file diff --git a/src/runtime/threading_semaphore.c b/src/runtime/threading_semaphore.c new file mode 100644 index 0000000..743adc6 --- /dev/null +++ b/src/runtime/threading_semaphore.c @@ -0,0 +1,39 @@ +#include "threading.h" + + +RT_DLLEXPORT rt_create_semaphore_result rtCreateSemaphore(int initial_value, int max_value) { + rt_create_semaphore_result res; + res.semaphore.cond = rtCreateConditionVar(); + if (!res.semaphore.cond) { + res.ok = false; + return res; + } + res.semaphore.counter = initial_value; + res.semaphore.max = max_value; + res.ok = true; + return res; +} + +RT_DLLEXPORT void rtDestroySemaphore(rt_semaphore *sem) { + rtDestroyConditionVar(sem->cond); + sem->cond = NULL; + sem->counter = 0; + sem->max = 0; +} + +RT_DLLEXPORT void rtSignalSemaphore(rt_semaphore *sem) { + rtLockConditionVar(sem->cond); + RT_ASSERT(sem->counter < sem->max, + "Tried to signal a semaphore that has reached its maximum value."); + ++sem->counter; + rtUnlockConditionVar(sem->cond, true); +} + +RT_DLLEXPORT void rtWaitOnSemaphore(rt_semaphore *sem) { + rtLockConditionVar(sem->cond); + while (sem->counter == 0) { + rtWaitOnConditionVar(sem->cond); + } + --sem->counter; + rtUnlockConditionVar(sem->cond, false); +} \ No newline at end of file