mirror of https://github.com/AxioDL/metaforce.git
aurora: Add naive pipeline cache
This commit is contained in:
parent
0d52438297
commit
64baad395a
|
@ -10,6 +10,7 @@
|
|||
#include <deque>
|
||||
#include <logvisor/logvisor.hpp>
|
||||
#include <thread>
|
||||
#include <fstream>
|
||||
|
||||
namespace aurora::gfx {
|
||||
static logvisor::Module Log("aurora::gfx");
|
||||
|
@ -124,7 +125,23 @@ static PipelineRef g_currentPipeline;
|
|||
|
||||
static std::vector<Command> g_commands;
|
||||
|
||||
static void find_pipeline(PipelineRef hash, NewPipelineCallback&& cb) {
|
||||
static ByteBuffer g_serializedPipelines;
|
||||
static u32 g_serializedPipelineCount = 0;
|
||||
|
||||
template <typename PipelineConfig>
|
||||
static void serialize_pipeline_config(ShaderType type, const PipelineConfig& config) {
|
||||
static_assert(std::has_unique_object_representations_v<PipelineConfig>);
|
||||
g_serializedPipelines.append(&type, sizeof(type));
|
||||
const u32 configSize = sizeof(config);
|
||||
g_serializedPipelines.append(&configSize, sizeof(configSize));
|
||||
g_serializedPipelines.append(&config, configSize);
|
||||
++g_serializedPipelineCount;
|
||||
}
|
||||
|
||||
template <typename PipelineConfig>
|
||||
static PipelineRef find_pipeline(ShaderType type, const PipelineConfig& config, NewPipelineCallback&& cb,
|
||||
bool serialize = true) {
|
||||
PipelineRef hash = xxh3_hash(config, static_cast<XXH64_hash_t>(type));
|
||||
bool found = false;
|
||||
{
|
||||
std::scoped_lock guard{g_pipelineMutex};
|
||||
|
@ -138,17 +155,24 @@ static void find_pipeline(PipelineRef hash, NewPipelineCallback&& cb) {
|
|||
}
|
||||
} else {
|
||||
g_pipelines.try_emplace(hash, cb());
|
||||
if (serialize) {
|
||||
serialize_pipeline_config(type, config);
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
g_queuedPipelines.emplace_back(std::pair{hash, std::move(cb)});
|
||||
if (serialize) {
|
||||
serialize_pipeline_config(type, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
g_pipelineCv.notify_one();
|
||||
queuedPipelines++;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static void push_draw_command(ShaderDrawCommand data) {
|
||||
|
@ -204,9 +228,7 @@ void queue_movie_player(const TextureHandle& tex_y, const TextureHandle& tex_u,
|
|||
}
|
||||
template <>
|
||||
PipelineRef pipeline_ref(movie_player::PipelineConfig config) {
|
||||
PipelineRef ref = xxh3_hash(config, static_cast<XXH64_hash_t>(ShaderType::MoviePlayer));
|
||||
find_pipeline(ref, [=]() { return create_pipeline(g_state.moviePlayer, config); });
|
||||
return ref;
|
||||
return find_pipeline(ShaderType::MoviePlayer, config, [=]() { return create_pipeline(g_state.moviePlayer, config); });
|
||||
}
|
||||
|
||||
template <>
|
||||
|
@ -219,9 +241,7 @@ void push_draw_command(stream::DrawData data) {
|
|||
}
|
||||
template <>
|
||||
PipelineRef pipeline_ref(stream::PipelineConfig config) {
|
||||
PipelineRef ref = xxh3_hash(config, static_cast<XXH64_hash_t>(ShaderType::Stream));
|
||||
find_pipeline(ref, [=]() { return create_pipeline(g_state.stream, config); });
|
||||
return ref;
|
||||
return find_pipeline(ShaderType::Stream, config, [=]() { return create_pipeline(g_state.stream, config); });
|
||||
}
|
||||
|
||||
template <>
|
||||
|
@ -230,9 +250,7 @@ void push_draw_command(model::DrawData data) {
|
|||
}
|
||||
template <>
|
||||
PipelineRef pipeline_ref(model::PipelineConfig config) {
|
||||
PipelineRef ref = xxh3_hash(config, static_cast<XXH64_hash_t>(ShaderType::Model));
|
||||
find_pipeline(ref, [=]() { return create_pipeline(g_state.model, config); });
|
||||
return ref;
|
||||
return find_pipeline(ShaderType::Model, config, [=]() { return create_pipeline(g_state.model, config); });
|
||||
}
|
||||
|
||||
static void pipeline_worker() {
|
||||
|
@ -301,6 +319,53 @@ void initialize() {
|
|||
g_state.moviePlayer = movie_player::construct_state();
|
||||
g_state.stream = stream::construct_state();
|
||||
g_state.model = model::construct_state();
|
||||
|
||||
g_serializedPipelines = {};
|
||||
{
|
||||
// Load serialized pipeline cache
|
||||
std::ifstream file("pipeline_cache.bin", std::ios::in | std::ios::binary | std::ios::ate);
|
||||
const size_t size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
constexpr size_t headerSize = sizeof(g_serializedPipelineCount);
|
||||
if (size != -1 && size > headerSize) {
|
||||
g_serializedPipelines.append_zeroes(size - headerSize);
|
||||
file.read(reinterpret_cast<char*>(&g_serializedPipelineCount), headerSize);
|
||||
file.read(reinterpret_cast<char*>(g_serializedPipelines.data()), size - headerSize);
|
||||
}
|
||||
}
|
||||
if (g_serializedPipelineCount > 0) {
|
||||
size_t offset = 0;
|
||||
while (offset < g_serializedPipelines.size()) {
|
||||
ShaderType type = *reinterpret_cast<const ShaderType*>(g_serializedPipelines.data() + offset);
|
||||
offset += sizeof(ShaderType);
|
||||
u32 size = *reinterpret_cast<const u32*>(g_serializedPipelines.data() + offset);
|
||||
offset += sizeof(u32);
|
||||
switch (type) {
|
||||
case ShaderType::MoviePlayer: {
|
||||
const movie_player::PipelineConfig config =
|
||||
*reinterpret_cast<const movie_player::PipelineConfig*>(g_serializedPipelines.data() + offset);
|
||||
find_pipeline(
|
||||
type, config, [=]() { return movie_player::create_pipeline(g_state.moviePlayer, config); }, false);
|
||||
} break;
|
||||
case ShaderType::Stream: {
|
||||
const stream::PipelineConfig config =
|
||||
*reinterpret_cast<const stream::PipelineConfig*>(g_serializedPipelines.data() + offset);
|
||||
find_pipeline(
|
||||
type, config, [=]() { return stream::create_pipeline(g_state.stream, config); }, false);
|
||||
} break;
|
||||
case ShaderType::Model: {
|
||||
const model::PipelineConfig config =
|
||||
*reinterpret_cast<const model::PipelineConfig*>(g_serializedPipelines.data() + offset);
|
||||
find_pipeline(
|
||||
type, config, [=]() { return model::create_pipeline(g_state.model, config); }, false);
|
||||
} break;
|
||||
default:
|
||||
Log.report(logvisor::Warning, FMT_STRING("Unknown pipeline type {}"), type);
|
||||
break;
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
|
@ -310,6 +375,13 @@ void shutdown() {
|
|||
g_pipelineThread.join();
|
||||
}
|
||||
|
||||
{
|
||||
// Write serialized pipelines to file
|
||||
std::ofstream file("pipeline_cache.bin", std::ios::out | std::ios::trunc | std::ios::binary);
|
||||
file.write(reinterpret_cast<const char*>(&g_serializedPipelineCount), sizeof(g_serializedPipelineCount));
|
||||
file.write(reinterpret_cast<const char*>(g_serializedPipelines.data()), g_serializedPipelines.size());
|
||||
}
|
||||
|
||||
gx::shutdown();
|
||||
|
||||
g_cachedBindGroups.clear();
|
||||
|
|
|
@ -179,7 +179,10 @@ struct ShaderConfig {
|
|||
bool operator==(const ShaderConfig&) const = default;
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<ShaderConfig>);
|
||||
|
||||
constexpr u32 GXPipelineConfigVersion = 1;
|
||||
struct PipelineConfig {
|
||||
u32 version = GXPipelineConfigVersion;
|
||||
ShaderConfig shaderConfig;
|
||||
GX::Primitive primitive;
|
||||
GX::Compare depthFunc;
|
||||
|
@ -189,9 +192,10 @@ struct PipelineConfig {
|
|||
GX::LogicOp blendOp;
|
||||
u32 dstAlpha;
|
||||
bool depthCompare, depthUpdate, alphaUpdate;
|
||||
u8 _pad;
|
||||
u8 _pad = 0;
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<PipelineConfig>);
|
||||
|
||||
struct GXBindGroupLayouts {
|
||||
wgpu::BindGroupLayout uniformLayout;
|
||||
wgpu::BindGroupLayout samplerLayout;
|
||||
|
|
|
@ -190,7 +190,7 @@ void queue_surface(const u8* dlStart, u32 dlSize) noexcept {
|
|||
|
||||
State construct_state() { return {}; }
|
||||
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config) {
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] const PipelineConfig& config) {
|
||||
const auto info = build_shader_info(config.shaderConfig); // TODO remove
|
||||
const auto shader = build_shader(config.shaderConfig, info);
|
||||
|
||||
|
|
|
@ -20,6 +20,6 @@ struct PipelineConfig : gx::PipelineConfig {};
|
|||
struct State {};
|
||||
|
||||
State construct_state();
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config);
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] const PipelineConfig& config);
|
||||
void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass);
|
||||
} // namespace aurora::gfx::model
|
||||
|
|
|
@ -146,7 +146,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|||
};
|
||||
}
|
||||
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config) {
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] const PipelineConfig& config) {
|
||||
const auto attributes =
|
||||
make_vertex_attributes(std::array{wgpu::VertexFormat::Float32x3, wgpu::VertexFormat::Float32x2});
|
||||
const std::array vertexBuffers{make_vertex_buffer_layout(sizeof(Vert), attributes)};
|
||||
|
|
|
@ -33,7 +33,7 @@ struct alignas(4) Vert {
|
|||
};
|
||||
|
||||
State construct_state();
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config);
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] const PipelineConfig& config);
|
||||
DrawData make_draw_data(const State& state, const TextureHandle& tex_y, const TextureHandle& tex_u,
|
||||
const TextureHandle& tex_v, float h_pad, float v_pad);
|
||||
void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass);
|
||||
|
|
|
@ -11,7 +11,7 @@ static logvisor::Module Log("aurora::gfx::stream");
|
|||
|
||||
using gpu::g_device;
|
||||
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config) {
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] const PipelineConfig& config) {
|
||||
const auto info = build_shader_info(config.shaderConfig); // TODO remove
|
||||
const auto shader = build_shader(config.shaderConfig, info);
|
||||
|
||||
|
|
|
@ -19,6 +19,6 @@ struct PipelineConfig : public gx::PipelineConfig {};
|
|||
struct State {};
|
||||
|
||||
State construct_state();
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config);
|
||||
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] const PipelineConfig& config);
|
||||
void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass);
|
||||
} // namespace aurora::gfx::stream
|
||||
|
|
Loading…
Reference in New Issue