Fix #124011: GPU: Broken specialization constants

Don't store pointers to `blender::Map` items since, unlike `std::map`,
they're not guaranteed to stay valid.
This resulted in shader binaries being loaded into the wrong
specialization.

Pull Request: https://projects.blender.org/blender/blender/pulls/124051
This commit is contained in:
Miguel Pozo 2024-07-02 16:59:22 +02:00
parent f8e78b8236
commit b0fbd550e0
2 changed files with 28 additions and 15 deletions

@ -1906,6 +1906,20 @@ SpecializationBatchHandle GLShaderCompiler::precompile_specializations(
return handle;
}
GLShader::GLProgram *GLShaderCompiler::SpecializationWork::program_get()
{
for (const SpecializationConstant &constant : constants) {
const ShaderInput *input = shader->interface->constant_get(constant.name.c_str());
BLI_assert_msg(input != nullptr, "The specialization constant doesn't exists");
shader->constants.values[input->location].u = constant.value.u;
}
shader->constants.is_dirty = true;
if (shader->program_cache_.contains(shader->constants.values)) {
return &shader->program_cache_.lookup(shader->constants.values);
}
return nullptr;
}
void GLShaderCompiler::prepare_next_specialization_batch()
{
BLI_assert(current_specialization_batch.is_ready && !specialization_queue.is_empty());
@ -1920,24 +1934,20 @@ void GLShaderCompiler::prepare_next_specialization_batch()
for (auto &specialization : next.specializations) {
GLShader *sh = static_cast<GLShader *>(unwrap(specialization.shader));
for (const SpecializationConstant &constant : specialization.constants) {
const ShaderInput *input = sh->interface->constant_get(constant.name.c_str());
BLI_assert_msg(input != nullptr, "The specialization constant doesn't exists");
sh->constants.values[input->location].u = constant.value.u;
}
sh->constants.is_dirty = true;
if (sh->program_cache_.contains(sh->constants.values)) {
/* Already compiled. */
continue;
}
items.append({});
SpecializationWork &item = items.last();
item.shader = sh;
item.constants = specialization.constants;
if (item.program_get()) {
/* Already compiled. */
items.pop_last();
continue;
}
/** WORKAROUND: Set async_compilation to true, so only the sources are generated. */
sh->async_compilation_ = true;
sh->program_get();
item.program = sh->program_active_;
sh->async_compilation_ = false;
item.sources = sh->get_sources();
@ -1971,8 +1981,9 @@ bool GLShaderCompiler::specialization_batch_is_ready(SpecializationBatchHandle &
}
if (!item.do_async_compilation) {
glDeleteProgram(item.program->program_id);
item.program->program_id = 0;
GLShader::GLProgram *program = item.program_get();
glDeleteProgram(program->program_id);
program->program_id = 0;
item.shader->constants.is_dirty = true;
item.is_ready = true;
continue;
@ -1984,7 +1995,7 @@ bool GLShaderCompiler::specialization_batch_is_ready(SpecializationBatchHandle &
}
else if (item.worker->is_ready()) {
/* Retrieve the binary compiled by the worker. */
if (item.worker->load_program_binary(item.program->program_id)) {
if (item.worker->load_program_binary(item.program_get()->program_id)) {
item.is_ready = true;
}
else {

@ -299,9 +299,11 @@ class GLShaderCompiler : public ShaderCompiler {
struct SpecializationWork {
GLShader *shader = nullptr;
GLShader::GLProgram *program = nullptr;
Vector<shader::SpecializationConstant> constants;
GLSourcesBaked sources;
GLShader::GLProgram *program_get();
GLCompilerWorker *worker = nullptr;
bool do_async_compilation = false;
bool is_ready = false;