From 5183809027a6b9b534e3e0b384aaba4cb46ace0f Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 19 Feb 2022 01:41:21 -0500 Subject: [PATCH] aurora: Async pipeline creation --- aurora/lib/aurora.cpp | 6 ++- aurora/lib/gfx/common.cpp | 70 +++++++++++++++++++++---- aurora/lib/gfx/common.hpp | 3 +- aurora/lib/gfx/movie_player/shader.wgsl | 42 --------------- aurora/lib/gfx/texture.cpp | 9 ++-- aurora/lib/gpu.cpp | 2 + aurora/lib/gpu.hpp | 8 +++ 7 files changed, 81 insertions(+), 59 deletions(-) delete mode 100644 aurora/lib/gfx/movie_player/shader.wgsl diff --git a/aurora/lib/aurora.cpp b/aurora/lib/aurora.cpp index 2f95aab25..3f57a4c80 100644 --- a/aurora/lib/aurora.cpp +++ b/aurora/lib/aurora.cpp @@ -37,6 +37,7 @@ static void set_window_icon(Icon icon) noexcept { ); if (iconSurface == nullptr) { Log.report(logvisor::Fatal, FMT_STRING("Failed to create icon surface: {}"), SDL_GetError()); + unreachable(); } SDL_SetWindowIcon(g_Window, iconSurface); SDL_FreeSurface(iconSurface); @@ -173,6 +174,7 @@ void app_run(std::unique_ptr app, Icon icon, int argc, char** argv) if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { Log.report(logvisor::Fatal, FMT_STRING("Error initializing SDL: {}"), SDL_GetError()); + unreachable(); } Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE; @@ -198,11 +200,12 @@ void app_run(std::unique_ptr app, Icon icon, int argc, char** argv) g_Window = SDL_CreateWindow("Metaforce", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, flags); if (g_Window == nullptr) { Log.report(logvisor::Fatal, FMT_STRING("Error creating window: {}"), SDL_GetError()); + unreachable(); } set_window_icon(std::move(icon)); gpu::initialize(g_Window); - gfx::construct_state(); + gfx::initialize(); imgui::create_context(); g_AppDelegate->onImGuiInit(1.f); // TODO scale @@ -282,6 +285,7 @@ void app_run(std::unique_ptr app, Icon icon, int argc, char** argv) g_AppDelegate->onAppExiting(); imgui::shutdown(); + gfx::shutdown(); gpu::shutdown(); SDL_DestroyWindow(g_Window); SDL_Quit(); diff --git a/aurora/lib/gfx/common.cpp b/aurora/lib/gfx/common.cpp index 276813091..a30948d02 100644 --- a/aurora/lib/gfx/common.cpp +++ b/aurora/lib/gfx/common.cpp @@ -3,7 +3,10 @@ #include "../gpu.hpp" #include "movie_player/shader.hpp" +#include +#include #include +#include #include namespace aurora::gfx { @@ -55,9 +58,13 @@ zeus::CMatrix4f g_mvInv; zeus::CMatrix4f g_proj; metaforce::CFogState g_fogState; +using NewPipelineCallback = std::function; static std::mutex g_pipelineMutex; -static std::unordered_map g_pipelines; -static std::vector g_queuedPipelines; +static std::thread g_pipelineThread; +static bool g_pipelineThreadEnd = false; +static std::condition_variable g_pipelineCv; +static std::unordered_map g_pipelines; +static std::deque> g_queuedPipelines; static std::unordered_map g_cachedBindGroups; static ByteBuffer g_verts; @@ -72,22 +79,26 @@ static PipelineRef g_currentPipeline; static std::vector g_commands; -using NewPipelineCallback = std::function; -static PipelineRef find_pipeline(PipelineCreateCommand command, NewPipelineCallback cb) { +static PipelineRef find_pipeline(PipelineCreateCommand command, NewPipelineCallback&& cb) { const auto hash = xxh3_hash(command); - bool found; + bool found = false; { std::lock_guard guard{g_pipelineMutex}; - const auto ref = g_pipelines.find(hash); - found = ref != g_pipelines.end(); + found = g_pipelines.find(hash) != g_pipelines.end(); + if (!found) { + const auto ref = + std::find_if(g_queuedPipelines.begin(), g_queuedPipelines.end(), [=](auto v) { return v.first == hash; }); + if (ref != g_queuedPipelines.end()) { + found = true; + } + } } if (!found) { - // TODO another thread - wgpu::RenderPipeline pipeline = cb(); { std::lock_guard guard{g_pipelineMutex}; - g_pipelines[hash] = std::move(pipeline); + g_queuedPipelines.emplace_back(std::pair{hash, std::move(cb)}); } + g_pipelineCv.notify_one(); } return hash; } @@ -157,7 +168,37 @@ PipelineRef pipeline_ref(movie_player::PipelineConfig config) { [=]() { return create_pipeline(g_state.moviePlayer, config); }); } -void construct_state() { +static void pipeline_worker() { + bool hasMore = false; + while (true) { + std::pair cb; + { + std::unique_lock lock{g_pipelineMutex}; + if (!hasMore) { + g_pipelineCv.wait(lock, [] { return !g_queuedPipelines.empty() || g_pipelineThreadEnd; }); + } + if (g_pipelineThreadEnd) { + break; + } + cb = std::move(g_queuedPipelines.front()); + } + auto result = cb.second(); + { + std::lock_guard lock{g_pipelineMutex}; + if (g_pipelines.contains(cb.first)) { + Log.report(logvisor::Fatal, FMT_STRING("Duplicate pipeline {}"), cb.first); + unreachable(); + } + g_pipelines[cb.first] = result; + g_queuedPipelines.pop_front(); + hasMore = !g_queuedPipelines.empty(); + } + } +} + +void initialize() { + g_pipelineThread = std::thread(pipeline_worker); + { const auto uniformDescriptor = wgpu::BufferDescriptor{ .label = "Shared Uniform Buffer", @@ -186,6 +227,12 @@ void construct_state() { g_state.moviePlayer = movie_player::construct_state(); } +void shutdown() { + g_pipelineThreadEnd = true; + g_pipelineCv.notify_all(); + g_pipelineThread.join(); +} + void render(const wgpu::RenderPassEncoder& pass) { { g_queue.WriteBuffer(g_vertexBuffer, 0, g_verts.data(), g_verts.size()); @@ -272,6 +319,7 @@ BindGroupRef bind_group_ref(const wgpu::BindGroupDescriptor& descriptor) { const wgpu::BindGroup& find_bind_group(BindGroupRef id) { if (!g_cachedBindGroups.contains(id)) { Log.report(logvisor::Fatal, FMT_STRING("get_bind_group: failed to locate {}"), id); + unreachable(); } return g_cachedBindGroups[id]; } diff --git a/aurora/lib/gfx/common.hpp b/aurora/lib/gfx/common.hpp index 569dad084..6472ca248 100644 --- a/aurora/lib/gfx/common.hpp +++ b/aurora/lib/gfx/common.hpp @@ -126,7 +126,8 @@ enum class ShaderType { MoviePlayer, }; -void construct_state(); +void initialize(); +void shutdown(); void render(const wgpu::RenderPassEncoder& pass); diff --git a/aurora/lib/gfx/movie_player/shader.wgsl b/aurora/lib/gfx/movie_player/shader.wgsl deleted file mode 100644 index 5428d36e5..000000000 --- a/aurora/lib/gfx/movie_player/shader.wgsl +++ /dev/null @@ -1,42 +0,0 @@ -struct Uniform { - xf: mat4x4; - color: vec4; -}; -@group(0) @binding(0) -var ubuf: Uniform; -@group(0) @binding(1) -var tex_sampler: sampler; -@group(1) @binding(0) -var tex_y: texture_2d; -@group(1) @binding(1) -var tex_u: texture_2d; -@group(1) @binding(2) -var tex_v: texture_2d; - -struct VertexOutput { - @builtin(position) pos: vec4; - @location(0) uv: vec2; -}; - -@stage(vertex) -fn vs_main(@location(0) in_pos: vec3, @location(1) in_uv: vec2) -> VertexOutput { - var out: VertexOutput; - out.pos = ubuf.xf * vec4(in_pos, 1.0); - out.uv = in_uv; - return out; -} - -@stage(fragment) -fn fs_main(in: VertexOutput) -> @location(0) vec4 { - var yuv = vec3( - 1.1643 * (textureSample(tex_y, tex_sampler, in.uv).x - 0.0625), - textureSample(tex_u, tex_sampler, in.uv).x - 0.5, - textureSample(tex_v, tex_sampler, in.uv).x - 0.5 - ); - return ubuf.color * vec4( - yuv.x + 1.5958 * yuv.z, - yuv.x - 0.39173 * yuv.y - 0.8129 * yuv.z, - yuv.x + 2.017 * yuv.y, - 1.0 - ); -} diff --git a/aurora/lib/gfx/texture.cpp b/aurora/lib/gfx/texture.cpp index d749e8377..f84ef6871 100644 --- a/aurora/lib/gfx/texture.cpp +++ b/aurora/lib/gfx/texture.cpp @@ -2,6 +2,7 @@ #include "../gpu.hpp" +#include #include namespace aurora::gfx { @@ -45,7 +46,7 @@ static TextureFormatInfo format_info(wgpu::TextureFormat format) { return {4, 4, 8, true}; default: Log.report(logvisor::Fatal, FMT_STRING("format_info: unimplemented format {}"), magic_enum::enum_name(format)); - return {0, 0, 0, false}; + unreachable(); } } static wgpu::Extent3D physical_size(wgpu::Extent3D size, TextureFormatInfo info) { @@ -74,7 +75,7 @@ TextureHandle new_static_texture_2d(uint32_t width, uint32_t height, uint32_t mi if (offset + dataSize > data.size()) { Log.report(logvisor::Fatal, FMT_STRING("new_static_texture_2d[{}]: expected at least {} bytes, got {}"), label, offset + dataSize, data.size()); - return {}; + unreachable(); } const auto dstView = wgpu::ImageCopyTexture{ .texture = ref.texture, @@ -133,7 +134,7 @@ void write_texture(const TextureHandle& handle, ArrayRef data) noexcept }; if (ref.size.depthOrArrayLayers != 1) { Log.report(logvisor::Fatal, FMT_STRING("write_texture: unsupported depth {}"), ref.size.depthOrArrayLayers); - return; + unreachable(); } const auto info = format_info(ref.format); const auto physicalSize = physical_size(ref.size, info); @@ -143,7 +144,7 @@ void write_texture(const TextureHandle& handle, ArrayRef data) noexcept const uint32_t dataSize = bytesPerRow * heightBlocks * ref.size.depthOrArrayLayers; if (dataSize > data.size()) { Log.report(logvisor::Fatal, FMT_STRING("write_texture: expected at least {} bytes, got {}"), dataSize, data.size()); - return; + unreachable(); } const auto dataLayout = wgpu::TextureDataLayout{ .bytesPerRow = bytesPerRow, diff --git a/aurora/lib/gpu.cpp b/aurora/lib/gpu.cpp index 4abb1ed66..d1ab4753f 100644 --- a/aurora/lib/gpu.cpp +++ b/aurora/lib/gpu.cpp @@ -129,6 +129,7 @@ void initialize(SDL_Window* window) { }); if (adapterIt == adapters.end()) { Log.report(logvisor::Fatal, FMT_STRING("Failed to find usable graphics backend")); + unreachable(); } g_Adapter = *adapterIt; } @@ -157,6 +158,7 @@ void initialize(SDL_Window* window) { std::unique_ptr(utils::CreateBinding(g_backendType, window, g_device.Get())); if (!g_BackendBinding) { Log.report(logvisor::Fatal, FMT_STRING("Unsupported backend {}"), backendName); + unreachable(); } auto swapChainFormat = static_cast(g_BackendBinding->GetPreferredSwapChainTextureFormat()); diff --git a/aurora/lib/gpu.hpp b/aurora/lib/gpu.hpp index 5648ecd84..7fcee6b49 100644 --- a/aurora/lib/gpu.hpp +++ b/aurora/lib/gpu.hpp @@ -4,6 +4,14 @@ #include #include +#ifdef __GNUC__ +[[noreturn]] inline __attribute__((always_inline)) void unreachable() { __builtin_unreachable(); } +#elif defined(_MSC_VER) +[[noreturn]] __forceinline void unreachable() { __assume(false); } +#else +#error Unknown compiler +#endif + struct SDL_Window; namespace aurora::gpu {