aurora: Add naive pipeline cache

This commit is contained in:
Luke Street 2022-05-03 01:32:06 -04:00
parent 0d52438297
commit 64baad395a
8 changed files with 93 additions and 17 deletions

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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)};

View File

@ -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);

View File

@ -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);

View File

@ -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