vk/interpreter: Implement base pipeline propagation

This commit is contained in:
kd-11 2026-04-13 00:58:41 +03:00 committed by kd-11
parent d519571e18
commit ac28c391f6
5 changed files with 66 additions and 30 deletions

View file

@ -13,7 +13,8 @@
namespace gl
{
using glsl::shader;
using enum program_common::interpreter::compiler_option;
using enum program_common::interpreter::compiler_option;
using enum program_common::interpreter::cached_pipeline_flags;
namespace interpreter
{
@ -88,7 +89,7 @@ namespace gl
// Second stage. Propagate base pipelines to all compatible
ctr = 0;
reader_lock lock(m_program_cache_lock);
std::lock_guard lock(m_program_cache_lock);
for (const auto& variant : variants.pipelines)
{
@ -107,7 +108,7 @@ namespace gl
}
auto data = new interpreter::cached_program();
data->flags |= interpreter::CACHED_PIPE_UNOPTIMIZED;
data->flags |= CACHED_PIPE_UNOPTIMIZED;
data->allocator = base_pipeline->second->allocator;
data->vertex_shader = base_pipeline->second->vertex_shader;
data->fragment_shader = base_pipeline->second->fragment_shader;
@ -193,13 +194,13 @@ namespace gl
if (m_current_interpreter)
{
constexpr u32 test_mask = (interpreter::CACHED_PIPE_UNOPTIMIZED | interpreter::CACHED_PIPE_RECOMPILING);
constexpr u32 unoptimized_mask = interpreter::CACHED_PIPE_UNOPTIMIZED;
constexpr u32 test_mask = (CACHED_PIPE_UNOPTIMIZED | CACHED_PIPE_RECOMPILING);
constexpr u32 unoptimized_mask = CACHED_PIPE_UNOPTIMIZED;
if ((m_current_interpreter->flags & test_mask) == unoptimized_mask)
{
// Interpreter is unoptimized and we haven't tried to recompile it
// NOTE: This operation effectively orphans the current interpreter, but since unoptimized pipelines just have a non-owning reference then it's actually fine and we don't really leak anything.
m_current_interpreter->flags |= interpreter::CACHED_PIPE_RECOMPILING;
m_current_interpreter->flags |= CACHED_PIPE_RECOMPILING;
build_program_async(opt, {});
}
return m_current_interpreter->prog.get();

View file

@ -62,12 +62,6 @@ namespace gl
void allocate(int size);
};
enum cached_program_flags
{
CACHED_PIPE_UNOPTIMIZED = (1 << 0),
CACHED_PIPE_RECOMPILING = (1 << 1),
};
struct cached_program
{
u32 flags = 0;

View file

@ -45,6 +45,12 @@ namespace program_common
COMPILER_OPT_VS_MIN = COMPILER_OPT_ENABLE_INSTANCING,
};
enum cached_pipeline_flags : u32
{
CACHED_PIPE_UNOPTIMIZED = (1 << 0),
CACHED_PIPE_RECOMPILING = (1 << 1),
};
static std::string get_vertex_interpreter()
{
const char* s =

View file

@ -15,6 +15,7 @@
namespace vk
{
using enum program_common::interpreter::compiler_option;
using enum program_common::interpreter::cached_pipeline_flags;
class async_pipe_compiler_context
{
@ -497,7 +498,13 @@ namespace vk
std::shared_ptr<glsl::program> result = std::move(prog);
std::lock_guard lock(this->m_program_cache_lock);
this->m_program_cache[key] = result;
pipeline_cache_entry_t cached_program
{
.flags = 0,
.program = result
};
this->m_program_cache[key] = cached_program;
if (async_callback)
{
@ -601,15 +608,21 @@ namespace vk
auto found = m_program_cache.find(key);
if (found != m_program_cache.end()) [[likely]]
{
m_current_interpreter = found->second;
m_current_interpreter = found->second.program;
return m_current_interpreter.get();
}
}
m_current_interpreter = link(properties, key.compiler_opt);
pipeline_cache_entry_t cache_entry
{
.flags = 0,
.program = m_current_interpreter
};
std::lock_guard lock(m_program_cache_lock);
m_program_cache[key] = m_current_interpreter;
m_program_cache[key] = cache_entry;
return m_current_interpreter.get();
}
@ -709,6 +722,8 @@ namespace vk
}
}
// Drain the queue.
// FIXME: Since the queue is executing from the context of the pipe compiler, we cannot properly stop this process.
do
{
std::this_thread::sleep_for(16ms);
@ -719,26 +734,40 @@ namespace vk
}
while (ctr.load() < limit1);
// TODO: Propagate base pipelines for faster startup
ctr = 0;
for (auto& variant : variants.pipelines)
std::lock_guard lock(m_program_cache_lock);
for (const auto& props : pipe_properties)
{
for (const auto& props : pipe_properties)
pipeline_key base_key;
base_key.properties = props;
for (auto& variant : variants.pipelines)
{
link(props, variant.vs_opts.shader_opt | variant.fs_opts.shader_opt, true, [&](std::shared_ptr<glsl::program>&) { ctr++; });
// Check if we have an exact match
base_key.compiler_opt = variant.vs_opts.shader_opt | variant.fs_opts.shader_opt;
if (auto found = m_program_cache.find(base_key);
found != m_program_cache.end())
{
// We have a perfect match, no propagation required
continue;
}
// Find a compatible pipeline
auto compat_key = base_key;
compat_key.compiler_opt = variant.vs_opts.compatible_shader_opts | variant.fs_opts.compatible_shader_opts;
auto found = m_program_cache.find(compat_key);
ensure(found != m_program_cache.end(), "Invalid interpreter configuration.");
pipeline_cache_entry_t cache_entry
{
.flags = CACHED_PIPE_UNOPTIMIZED,
.program = found->second.program
};
m_program_cache[base_key] = cache_entry;
}
}
do
{
std::this_thread::sleep_for(16ms);
const auto completed = ctr.load();
dlg->update_msg(1, fmt::format("Linking variant %u of %u...", completed, limit2));
dlg->set_value(1, completed);
}
while (ctr.load() < limit2);
dlg->set_value(1, limit2);
dlg->refresh();
}

View file

@ -57,7 +57,13 @@ namespace vk
u32 fragment_textures_location = 0;
};
std::unordered_map<pipeline_key, std::shared_ptr<glsl::program>, key_hasher> m_program_cache;
struct pipeline_cache_entry_t
{
u32 flags = 0;
std::shared_ptr<glsl::program> program;
};
std::unordered_map<pipeline_key, pipeline_cache_entry_t, key_hasher> m_program_cache;
std::unordered_map<u64, std::shared_ptr<VKVertexProgram>> m_vs_shader_cache;
std::unordered_map<u64, std::shared_ptr<VKFragmentProgram>> m_fs_shader_cache;
std::unordered_map<u64, pipeline_info_ex_t> m_pipeline_info_cache;