vk/interpreter: Implement base pipeline propagation
This commit is contained in:
@@ -13,7 +13,8 @@
|
|||||||
namespace gl
|
namespace gl
|
||||||
{
|
{
|
||||||
using glsl::shader;
|
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
|
namespace interpreter
|
||||||
{
|
{
|
||||||
@@ -88,7 +89,7 @@ namespace gl
|
|||||||
|
|
||||||
// Second stage. Propagate base pipelines to all compatible
|
// Second stage. Propagate base pipelines to all compatible
|
||||||
ctr = 0;
|
ctr = 0;
|
||||||
reader_lock lock(m_program_cache_lock);
|
std::lock_guard lock(m_program_cache_lock);
|
||||||
|
|
||||||
for (const auto& variant : variants.pipelines)
|
for (const auto& variant : variants.pipelines)
|
||||||
{
|
{
|
||||||
@@ -107,7 +108,7 @@ namespace gl
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto data = new interpreter::cached_program();
|
auto data = new interpreter::cached_program();
|
||||||
data->flags |= interpreter::CACHED_PIPE_UNOPTIMIZED;
|
data->flags |= CACHED_PIPE_UNOPTIMIZED;
|
||||||
data->allocator = base_pipeline->second->allocator;
|
data->allocator = base_pipeline->second->allocator;
|
||||||
data->vertex_shader = base_pipeline->second->vertex_shader;
|
data->vertex_shader = base_pipeline->second->vertex_shader;
|
||||||
data->fragment_shader = base_pipeline->second->fragment_shader;
|
data->fragment_shader = base_pipeline->second->fragment_shader;
|
||||||
@@ -193,13 +194,13 @@ namespace gl
|
|||||||
|
|
||||||
if (m_current_interpreter)
|
if (m_current_interpreter)
|
||||||
{
|
{
|
||||||
constexpr u32 test_mask = (interpreter::CACHED_PIPE_UNOPTIMIZED | interpreter::CACHED_PIPE_RECOMPILING);
|
constexpr u32 test_mask = (CACHED_PIPE_UNOPTIMIZED | CACHED_PIPE_RECOMPILING);
|
||||||
constexpr u32 unoptimized_mask = interpreter::CACHED_PIPE_UNOPTIMIZED;
|
constexpr u32 unoptimized_mask = CACHED_PIPE_UNOPTIMIZED;
|
||||||
if ((m_current_interpreter->flags & test_mask) == unoptimized_mask)
|
if ((m_current_interpreter->flags & test_mask) == unoptimized_mask)
|
||||||
{
|
{
|
||||||
// Interpreter is unoptimized and we haven't tried to recompile it
|
// 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.
|
// 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, {});
|
build_program_async(opt, {});
|
||||||
}
|
}
|
||||||
return m_current_interpreter->prog.get();
|
return m_current_interpreter->prog.get();
|
||||||
|
|||||||
@@ -62,12 +62,6 @@ namespace gl
|
|||||||
void allocate(int size);
|
void allocate(int size);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum cached_program_flags
|
|
||||||
{
|
|
||||||
CACHED_PIPE_UNOPTIMIZED = (1 << 0),
|
|
||||||
CACHED_PIPE_RECOMPILING = (1 << 1),
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cached_program
|
struct cached_program
|
||||||
{
|
{
|
||||||
u32 flags = 0;
|
u32 flags = 0;
|
||||||
|
|||||||
@@ -45,6 +45,12 @@ namespace program_common
|
|||||||
COMPILER_OPT_VS_MIN = COMPILER_OPT_ENABLE_INSTANCING,
|
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()
|
static std::string get_vertex_interpreter()
|
||||||
{
|
{
|
||||||
const char* s =
|
const char* s =
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
namespace vk
|
namespace vk
|
||||||
{
|
{
|
||||||
using enum program_common::interpreter::compiler_option;
|
using enum program_common::interpreter::compiler_option;
|
||||||
|
using enum program_common::interpreter::cached_pipeline_flags;
|
||||||
|
|
||||||
class async_pipe_compiler_context
|
class async_pipe_compiler_context
|
||||||
{
|
{
|
||||||
@@ -497,7 +498,13 @@ namespace vk
|
|||||||
|
|
||||||
std::shared_ptr<glsl::program> result = std::move(prog);
|
std::shared_ptr<glsl::program> result = std::move(prog);
|
||||||
std::lock_guard lock(this->m_program_cache_lock);
|
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)
|
if (async_callback)
|
||||||
{
|
{
|
||||||
@@ -601,15 +608,21 @@ namespace vk
|
|||||||
auto found = m_program_cache.find(key);
|
auto found = m_program_cache.find(key);
|
||||||
if (found != m_program_cache.end()) [[likely]]
|
if (found != m_program_cache.end()) [[likely]]
|
||||||
{
|
{
|
||||||
m_current_interpreter = found->second;
|
m_current_interpreter = found->second.program;
|
||||||
return m_current_interpreter.get();
|
return m_current_interpreter.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_current_interpreter = link(properties, key.compiler_opt);
|
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);
|
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();
|
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
|
do
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for(16ms);
|
std::this_thread::sleep_for(16ms);
|
||||||
@@ -719,26 +734,40 @@ namespace vk
|
|||||||
}
|
}
|
||||||
while (ctr.load() < limit1);
|
while (ctr.load() < limit1);
|
||||||
|
|
||||||
// TODO: Propagate base pipelines for faster startup
|
|
||||||
ctr = 0;
|
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->set_value(1, limit2);
|
||||||
dlg->refresh();
|
dlg->refresh();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,13 @@ namespace vk
|
|||||||
u32 fragment_textures_location = 0;
|
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<VKVertexProgram>> m_vs_shader_cache;
|
||||||
std::unordered_map<u64, std::shared_ptr<VKFragmentProgram>> m_fs_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;
|
std::unordered_map<u64, pipeline_info_ex_t> m_pipeline_info_cache;
|
||||||
|
|||||||
Reference in New Issue
Block a user