aurora: Async pipeline creation

This commit is contained in:
Luke Street 2022-02-19 01:41:21 -05:00
parent b6b68135ef
commit 5183809027
7 changed files with 81 additions and 59 deletions

View File

@ -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<AppDelegate> 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<AppDelegate> 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<AppDelegate> app, Icon icon, int argc, char** argv)
g_AppDelegate->onAppExiting();
imgui::shutdown();
gfx::shutdown();
gpu::shutdown();
SDL_DestroyWindow(g_Window);
SDL_Quit();

View File

@ -3,7 +3,10 @@
#include "../gpu.hpp"
#include "movie_player/shader.hpp"
#include <condition_variable>
#include <deque>
#include <logvisor/logvisor.hpp>
#include <thread>
#include <unordered_map>
namespace aurora::gfx {
@ -55,9 +58,13 @@ zeus::CMatrix4f g_mvInv;
zeus::CMatrix4f g_proj;
metaforce::CFogState g_fogState;
using NewPipelineCallback = std::function<wgpu::RenderPipeline()>;
static std::mutex g_pipelineMutex;
static std::unordered_map<uint64_t, wgpu::RenderPipeline> g_pipelines;
static std::vector<PipelineCreateCommand> g_queuedPipelines;
static std::thread g_pipelineThread;
static bool g_pipelineThreadEnd = false;
static std::condition_variable g_pipelineCv;
static std::unordered_map<PipelineRef, wgpu::RenderPipeline> g_pipelines;
static std::deque<std::pair<PipelineRef, NewPipelineCallback>> g_queuedPipelines;
static std::unordered_map<BindGroupRef, wgpu::BindGroup> g_cachedBindGroups;
static ByteBuffer g_verts;
@ -72,22 +79,26 @@ static PipelineRef g_currentPipeline;
static std::vector<Command> g_commands;
using NewPipelineCallback = std::function<wgpu::RenderPipeline()>;
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<PipelineRef, NewPipelineCallback> 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];
}

View File

@ -126,7 +126,8 @@ enum class ShaderType {
MoviePlayer,
};
void construct_state();
void initialize();
void shutdown();
void render(const wgpu::RenderPassEncoder& pass);

View File

@ -1,42 +0,0 @@
struct Uniform {
xf: mat4x4<f32>;
color: vec4<f32>;
};
@group(0) @binding(0)
var<uniform> ubuf: Uniform;
@group(0) @binding(1)
var tex_sampler: sampler;
@group(1) @binding(0)
var tex_y: texture_2d<f32>;
@group(1) @binding(1)
var tex_u: texture_2d<f32>;
@group(1) @binding(2)
var tex_v: texture_2d<f32>;
struct VertexOutput {
@builtin(position) pos: vec4<f32>;
@location(0) uv: vec2<f32>;
};
@stage(vertex)
fn vs_main(@location(0) in_pos: vec3<f32>, @location(1) in_uv: vec2<f32>) -> VertexOutput {
var out: VertexOutput;
out.pos = ubuf.xf * vec4<f32>(in_pos, 1.0);
out.uv = in_uv;
return out;
}
@stage(fragment)
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
var yuv = vec3<f32>(
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<f32>(
yuv.x + 1.5958 * yuv.z,
yuv.x - 0.39173 * yuv.y - 0.8129 * yuv.z,
yuv.x + 2.017 * yuv.y,
1.0
);
}

View File

@ -2,6 +2,7 @@
#include "../gpu.hpp"
#include <logvisor/logvisor.hpp>
#include <magic_enum.hpp>
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<uint8_t> 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<uint8_t> 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,

View File

@ -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::BackendBinding>(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<wgpu::TextureFormat>(g_BackendBinding->GetPreferredSwapChainTextureFormat());

View File

@ -4,6 +4,14 @@
#include <cstdint>
#include <dawn/webgpu_cpp.h>
#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 {