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 <deque>
|
||||||
#include <logvisor/logvisor.hpp>
|
#include <logvisor/logvisor.hpp>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
namespace aurora::gfx {
|
namespace aurora::gfx {
|
||||||
static logvisor::Module Log("aurora::gfx");
|
static logvisor::Module Log("aurora::gfx");
|
||||||
|
@ -124,7 +125,23 @@ static PipelineRef g_currentPipeline;
|
||||||
|
|
||||||
static std::vector<Command> g_commands;
|
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;
|
bool found = false;
|
||||||
{
|
{
|
||||||
std::scoped_lock guard{g_pipelineMutex};
|
std::scoped_lock guard{g_pipelineMutex};
|
||||||
|
@ -138,17 +155,24 @@ static void find_pipeline(PipelineRef hash, NewPipelineCallback&& cb) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
g_pipelines.try_emplace(hash, cb());
|
g_pipelines.try_emplace(hash, cb());
|
||||||
|
if (serialize) {
|
||||||
|
serialize_pipeline_config(type, config);
|
||||||
|
}
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
g_queuedPipelines.emplace_back(std::pair{hash, std::move(cb)});
|
g_queuedPipelines.emplace_back(std::pair{hash, std::move(cb)});
|
||||||
|
if (serialize) {
|
||||||
|
serialize_pipeline_config(type, config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
g_pipelineCv.notify_one();
|
g_pipelineCv.notify_one();
|
||||||
queuedPipelines++;
|
queuedPipelines++;
|
||||||
}
|
}
|
||||||
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void push_draw_command(ShaderDrawCommand data) {
|
static void push_draw_command(ShaderDrawCommand data) {
|
||||||
|
@ -204,9 +228,7 @@ void queue_movie_player(const TextureHandle& tex_y, const TextureHandle& tex_u,
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
PipelineRef pipeline_ref(movie_player::PipelineConfig config) {
|
PipelineRef pipeline_ref(movie_player::PipelineConfig config) {
|
||||||
PipelineRef ref = xxh3_hash(config, static_cast<XXH64_hash_t>(ShaderType::MoviePlayer));
|
return find_pipeline(ShaderType::MoviePlayer, config, [=]() { return create_pipeline(g_state.moviePlayer, config); });
|
||||||
find_pipeline(ref, [=]() { return create_pipeline(g_state.moviePlayer, config); });
|
|
||||||
return ref;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -219,9 +241,7 @@ void push_draw_command(stream::DrawData data) {
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
PipelineRef pipeline_ref(stream::PipelineConfig config) {
|
PipelineRef pipeline_ref(stream::PipelineConfig config) {
|
||||||
PipelineRef ref = xxh3_hash(config, static_cast<XXH64_hash_t>(ShaderType::Stream));
|
return find_pipeline(ShaderType::Stream, config, [=]() { return create_pipeline(g_state.stream, config); });
|
||||||
find_pipeline(ref, [=]() { return create_pipeline(g_state.stream, config); });
|
|
||||||
return ref;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -230,9 +250,7 @@ void push_draw_command(model::DrawData data) {
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
PipelineRef pipeline_ref(model::PipelineConfig config) {
|
PipelineRef pipeline_ref(model::PipelineConfig config) {
|
||||||
PipelineRef ref = xxh3_hash(config, static_cast<XXH64_hash_t>(ShaderType::Model));
|
return find_pipeline(ShaderType::Model, config, [=]() { return create_pipeline(g_state.model, config); });
|
||||||
find_pipeline(ref, [=]() { return create_pipeline(g_state.model, config); });
|
|
||||||
return ref;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pipeline_worker() {
|
static void pipeline_worker() {
|
||||||
|
@ -301,6 +319,53 @@ void initialize() {
|
||||||
g_state.moviePlayer = movie_player::construct_state();
|
g_state.moviePlayer = movie_player::construct_state();
|
||||||
g_state.stream = stream::construct_state();
|
g_state.stream = stream::construct_state();
|
||||||
g_state.model = model::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() {
|
void shutdown() {
|
||||||
|
@ -310,6 +375,13 @@ void shutdown() {
|
||||||
g_pipelineThread.join();
|
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();
|
gx::shutdown();
|
||||||
|
|
||||||
g_cachedBindGroups.clear();
|
g_cachedBindGroups.clear();
|
||||||
|
|
|
@ -179,7 +179,10 @@ struct ShaderConfig {
|
||||||
bool operator==(const ShaderConfig&) const = default;
|
bool operator==(const ShaderConfig&) const = default;
|
||||||
};
|
};
|
||||||
static_assert(std::has_unique_object_representations_v<ShaderConfig>);
|
static_assert(std::has_unique_object_representations_v<ShaderConfig>);
|
||||||
|
|
||||||
|
constexpr u32 GXPipelineConfigVersion = 1;
|
||||||
struct PipelineConfig {
|
struct PipelineConfig {
|
||||||
|
u32 version = GXPipelineConfigVersion;
|
||||||
ShaderConfig shaderConfig;
|
ShaderConfig shaderConfig;
|
||||||
GX::Primitive primitive;
|
GX::Primitive primitive;
|
||||||
GX::Compare depthFunc;
|
GX::Compare depthFunc;
|
||||||
|
@ -189,9 +192,10 @@ struct PipelineConfig {
|
||||||
GX::LogicOp blendOp;
|
GX::LogicOp blendOp;
|
||||||
u32 dstAlpha;
|
u32 dstAlpha;
|
||||||
bool depthCompare, depthUpdate, alphaUpdate;
|
bool depthCompare, depthUpdate, alphaUpdate;
|
||||||
u8 _pad;
|
u8 _pad = 0;
|
||||||
};
|
};
|
||||||
static_assert(std::has_unique_object_representations_v<PipelineConfig>);
|
static_assert(std::has_unique_object_representations_v<PipelineConfig>);
|
||||||
|
|
||||||
struct GXBindGroupLayouts {
|
struct GXBindGroupLayouts {
|
||||||
wgpu::BindGroupLayout uniformLayout;
|
wgpu::BindGroupLayout uniformLayout;
|
||||||
wgpu::BindGroupLayout samplerLayout;
|
wgpu::BindGroupLayout samplerLayout;
|
||||||
|
|
|
@ -190,7 +190,7 @@ void queue_surface(const u8* dlStart, u32 dlSize) noexcept {
|
||||||
|
|
||||||
State construct_state() { return {}; }
|
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 info = build_shader_info(config.shaderConfig); // TODO remove
|
||||||
const auto shader = build_shader(config.shaderConfig, info);
|
const auto shader = build_shader(config.shaderConfig, info);
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,6 @@ struct PipelineConfig : gx::PipelineConfig {};
|
||||||
struct State {};
|
struct State {};
|
||||||
|
|
||||||
State construct_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);
|
void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass);
|
||||||
} // namespace aurora::gfx::model
|
} // 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 =
|
const auto attributes =
|
||||||
make_vertex_attributes(std::array{wgpu::VertexFormat::Float32x3, wgpu::VertexFormat::Float32x2});
|
make_vertex_attributes(std::array{wgpu::VertexFormat::Float32x3, wgpu::VertexFormat::Float32x2});
|
||||||
const std::array vertexBuffers{make_vertex_buffer_layout(sizeof(Vert), attributes)};
|
const std::array vertexBuffers{make_vertex_buffer_layout(sizeof(Vert), attributes)};
|
||||||
|
|
|
@ -33,7 +33,7 @@ struct alignas(4) Vert {
|
||||||
};
|
};
|
||||||
|
|
||||||
State construct_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);
|
||||||
DrawData make_draw_data(const State& state, const TextureHandle& tex_y, const TextureHandle& tex_u,
|
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);
|
const TextureHandle& tex_v, float h_pad, float v_pad);
|
||||||
void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass);
|
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;
|
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 info = build_shader_info(config.shaderConfig); // TODO remove
|
||||||
const auto shader = build_shader(config.shaderConfig, info);
|
const auto shader = build_shader(config.shaderConfig, info);
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,6 @@ struct PipelineConfig : public gx::PipelineConfig {};
|
||||||
struct State {};
|
struct State {};
|
||||||
|
|
||||||
State construct_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);
|
void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass);
|
||||||
} // namespace aurora::gfx::stream
|
} // namespace aurora::gfx::stream
|
||||||
|
|
Loading…
Reference in New Issue