mirror of https://github.com/AxioDL/metaforce.git
aurora: Async pipeline creation
This commit is contained in:
parent
b6b68135ef
commit
5183809027
|
@ -37,6 +37,7 @@ static void set_window_icon(Icon icon) noexcept {
|
||||||
);
|
);
|
||||||
if (iconSurface == nullptr) {
|
if (iconSurface == nullptr) {
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("Failed to create icon surface: {}"), SDL_GetError());
|
Log.report(logvisor::Fatal, FMT_STRING("Failed to create icon surface: {}"), SDL_GetError());
|
||||||
|
unreachable();
|
||||||
}
|
}
|
||||||
SDL_SetWindowIcon(g_Window, iconSurface);
|
SDL_SetWindowIcon(g_Window, iconSurface);
|
||||||
SDL_FreeSurface(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) {
|
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("Error initializing SDL: {}"), SDL_GetError());
|
Log.report(logvisor::Fatal, FMT_STRING("Error initializing SDL: {}"), SDL_GetError());
|
||||||
|
unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
|
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);
|
g_Window = SDL_CreateWindow("Metaforce", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, flags);
|
||||||
if (g_Window == nullptr) {
|
if (g_Window == nullptr) {
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("Error creating window: {}"), SDL_GetError());
|
Log.report(logvisor::Fatal, FMT_STRING("Error creating window: {}"), SDL_GetError());
|
||||||
|
unreachable();
|
||||||
}
|
}
|
||||||
set_window_icon(std::move(icon));
|
set_window_icon(std::move(icon));
|
||||||
|
|
||||||
gpu::initialize(g_Window);
|
gpu::initialize(g_Window);
|
||||||
gfx::construct_state();
|
gfx::initialize();
|
||||||
|
|
||||||
imgui::create_context();
|
imgui::create_context();
|
||||||
g_AppDelegate->onImGuiInit(1.f); // TODO scale
|
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();
|
g_AppDelegate->onAppExiting();
|
||||||
|
|
||||||
imgui::shutdown();
|
imgui::shutdown();
|
||||||
|
gfx::shutdown();
|
||||||
gpu::shutdown();
|
gpu::shutdown();
|
||||||
SDL_DestroyWindow(g_Window);
|
SDL_DestroyWindow(g_Window);
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
#include "../gpu.hpp"
|
#include "../gpu.hpp"
|
||||||
#include "movie_player/shader.hpp"
|
#include "movie_player/shader.hpp"
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <deque>
|
||||||
#include <logvisor/logvisor.hpp>
|
#include <logvisor/logvisor.hpp>
|
||||||
|
#include <thread>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace aurora::gfx {
|
namespace aurora::gfx {
|
||||||
|
@ -55,9 +58,13 @@ zeus::CMatrix4f g_mvInv;
|
||||||
zeus::CMatrix4f g_proj;
|
zeus::CMatrix4f g_proj;
|
||||||
metaforce::CFogState g_fogState;
|
metaforce::CFogState g_fogState;
|
||||||
|
|
||||||
|
using NewPipelineCallback = std::function<wgpu::RenderPipeline()>;
|
||||||
static std::mutex g_pipelineMutex;
|
static std::mutex g_pipelineMutex;
|
||||||
static std::unordered_map<uint64_t, wgpu::RenderPipeline> g_pipelines;
|
static std::thread g_pipelineThread;
|
||||||
static std::vector<PipelineCreateCommand> g_queuedPipelines;
|
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 std::unordered_map<BindGroupRef, wgpu::BindGroup> g_cachedBindGroups;
|
||||||
|
|
||||||
static ByteBuffer g_verts;
|
static ByteBuffer g_verts;
|
||||||
|
@ -72,22 +79,26 @@ static PipelineRef g_currentPipeline;
|
||||||
|
|
||||||
static std::vector<Command> g_commands;
|
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);
|
const auto hash = xxh3_hash(command);
|
||||||
bool found;
|
bool found = false;
|
||||||
{
|
{
|
||||||
std::lock_guard guard{g_pipelineMutex};
|
std::lock_guard guard{g_pipelineMutex};
|
||||||
const auto ref = g_pipelines.find(hash);
|
found = g_pipelines.find(hash) != g_pipelines.end();
|
||||||
found = ref != 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) {
|
if (!found) {
|
||||||
// TODO another thread
|
|
||||||
wgpu::RenderPipeline pipeline = cb();
|
|
||||||
{
|
{
|
||||||
std::lock_guard guard{g_pipelineMutex};
|
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;
|
return hash;
|
||||||
}
|
}
|
||||||
|
@ -157,7 +168,37 @@ PipelineRef pipeline_ref(movie_player::PipelineConfig config) {
|
||||||
[=]() { return create_pipeline(g_state.moviePlayer, 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{
|
const auto uniformDescriptor = wgpu::BufferDescriptor{
|
||||||
.label = "Shared Uniform Buffer",
|
.label = "Shared Uniform Buffer",
|
||||||
|
@ -186,6 +227,12 @@ void construct_state() {
|
||||||
g_state.moviePlayer = movie_player::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) {
|
void render(const wgpu::RenderPassEncoder& pass) {
|
||||||
{
|
{
|
||||||
g_queue.WriteBuffer(g_vertexBuffer, 0, g_verts.data(), g_verts.size());
|
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) {
|
const wgpu::BindGroup& find_bind_group(BindGroupRef id) {
|
||||||
if (!g_cachedBindGroups.contains(id)) {
|
if (!g_cachedBindGroups.contains(id)) {
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("get_bind_group: failed to locate {}"), id);
|
Log.report(logvisor::Fatal, FMT_STRING("get_bind_group: failed to locate {}"), id);
|
||||||
|
unreachable();
|
||||||
}
|
}
|
||||||
return g_cachedBindGroups[id];
|
return g_cachedBindGroups[id];
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,8 @@ enum class ShaderType {
|
||||||
MoviePlayer,
|
MoviePlayer,
|
||||||
};
|
};
|
||||||
|
|
||||||
void construct_state();
|
void initialize();
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
void render(const wgpu::RenderPassEncoder& pass);
|
void render(const wgpu::RenderPassEncoder& pass);
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "../gpu.hpp"
|
#include "../gpu.hpp"
|
||||||
|
|
||||||
|
#include <logvisor/logvisor.hpp>
|
||||||
#include <magic_enum.hpp>
|
#include <magic_enum.hpp>
|
||||||
|
|
||||||
namespace aurora::gfx {
|
namespace aurora::gfx {
|
||||||
|
@ -45,7 +46,7 @@ static TextureFormatInfo format_info(wgpu::TextureFormat format) {
|
||||||
return {4, 4, 8, true};
|
return {4, 4, 8, true};
|
||||||
default:
|
default:
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("format_info: unimplemented format {}"), magic_enum::enum_name(format));
|
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) {
|
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()) {
|
if (offset + dataSize > data.size()) {
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("new_static_texture_2d[{}]: expected at least {} bytes, got {}"), label,
|
Log.report(logvisor::Fatal, FMT_STRING("new_static_texture_2d[{}]: expected at least {} bytes, got {}"), label,
|
||||||
offset + dataSize, data.size());
|
offset + dataSize, data.size());
|
||||||
return {};
|
unreachable();
|
||||||
}
|
}
|
||||||
const auto dstView = wgpu::ImageCopyTexture{
|
const auto dstView = wgpu::ImageCopyTexture{
|
||||||
.texture = ref.texture,
|
.texture = ref.texture,
|
||||||
|
@ -133,7 +134,7 @@ void write_texture(const TextureHandle& handle, ArrayRef<uint8_t> data) noexcept
|
||||||
};
|
};
|
||||||
if (ref.size.depthOrArrayLayers != 1) {
|
if (ref.size.depthOrArrayLayers != 1) {
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("write_texture: unsupported depth {}"), ref.size.depthOrArrayLayers);
|
Log.report(logvisor::Fatal, FMT_STRING("write_texture: unsupported depth {}"), ref.size.depthOrArrayLayers);
|
||||||
return;
|
unreachable();
|
||||||
}
|
}
|
||||||
const auto info = format_info(ref.format);
|
const auto info = format_info(ref.format);
|
||||||
const auto physicalSize = physical_size(ref.size, info);
|
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;
|
const uint32_t dataSize = bytesPerRow * heightBlocks * ref.size.depthOrArrayLayers;
|
||||||
if (dataSize > data.size()) {
|
if (dataSize > data.size()) {
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("write_texture: expected at least {} bytes, got {}"), 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{
|
const auto dataLayout = wgpu::TextureDataLayout{
|
||||||
.bytesPerRow = bytesPerRow,
|
.bytesPerRow = bytesPerRow,
|
||||||
|
|
|
@ -129,6 +129,7 @@ void initialize(SDL_Window* window) {
|
||||||
});
|
});
|
||||||
if (adapterIt == adapters.end()) {
|
if (adapterIt == adapters.end()) {
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("Failed to find usable graphics backend"));
|
Log.report(logvisor::Fatal, FMT_STRING("Failed to find usable graphics backend"));
|
||||||
|
unreachable();
|
||||||
}
|
}
|
||||||
g_Adapter = *adapterIt;
|
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()));
|
std::unique_ptr<utils::BackendBinding>(utils::CreateBinding(g_backendType, window, g_device.Get()));
|
||||||
if (!g_BackendBinding) {
|
if (!g_BackendBinding) {
|
||||||
Log.report(logvisor::Fatal, FMT_STRING("Unsupported backend {}"), backendName);
|
Log.report(logvisor::Fatal, FMT_STRING("Unsupported backend {}"), backendName);
|
||||||
|
unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto swapChainFormat = static_cast<wgpu::TextureFormat>(g_BackendBinding->GetPreferredSwapChainTextureFormat());
|
auto swapChainFormat = static_cast<wgpu::TextureFormat>(g_BackendBinding->GetPreferredSwapChainTextureFormat());
|
||||||
|
|
|
@ -4,6 +4,14 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <dawn/webgpu_cpp.h>
|
#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;
|
struct SDL_Window;
|
||||||
|
|
||||||
namespace aurora::gpu {
|
namespace aurora::gpu {
|
||||||
|
|
Loading…
Reference in New Issue