gl_shader_cache: Implement locker variants invalidation
This commit is contained in:
parent
ec85648af3
commit
78f3e8a757
4 changed files with 104 additions and 44 deletions
|
@ -225,6 +225,34 @@ std::string GetShaderId(u64 unique_identifier, ProgramType program_type) {
|
||||||
return fmt::format("{}{:016X}", GetProgramTypeName(program_type), unique_identifier);
|
return fmt::format("{}{:016X}", GetProgramTypeName(program_type), unique_identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Tegra::Engines::ConstBufferEngineInterface& GetConstBufferEngineInterface(
|
||||||
|
Core::System& system, ProgramType program_type) {
|
||||||
|
if (program_type == ProgramType::Compute) {
|
||||||
|
return system.GPU().KeplerCompute();
|
||||||
|
} else {
|
||||||
|
return system.GPU().Maxwell3D();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ConstBufferLocker> MakeLocker(Core::System& system, ProgramType program_type) {
|
||||||
|
return std::make_unique<ConstBufferLocker>(GetEnginesShaderType(program_type),
|
||||||
|
GetConstBufferEngineInterface(system, program_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillLocker(ConstBufferLocker& locker, const ShaderDiskCacheUsage& usage) {
|
||||||
|
for (const auto& key : usage.keys) {
|
||||||
|
const auto [buffer, offset] = key.first;
|
||||||
|
locker.InsertKey(buffer, offset, key.second);
|
||||||
|
}
|
||||||
|
for (const auto& [offset, sampler] : usage.bound_samplers) {
|
||||||
|
locker.InsertBoundSampler(offset, sampler);
|
||||||
|
}
|
||||||
|
for (const auto& [key, sampler] : usage.bindless_samplers) {
|
||||||
|
const auto [buffer, offset] = key;
|
||||||
|
locker.InsertBindlessSampler(buffer, offset, sampler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CachedProgram BuildShader(const Device& device, u64 unique_identifier, ProgramType program_type,
|
CachedProgram BuildShader(const Device& device, u64 unique_identifier, ProgramType program_type,
|
||||||
const ProgramCode& program_code, const ProgramCode& program_code_b,
|
const ProgramCode& program_code, const ProgramCode& program_code_b,
|
||||||
const ProgramVariant& variant, ConstBufferLocker& locker,
|
const ProgramVariant& variant, ConstBufferLocker& locker,
|
||||||
|
@ -336,11 +364,27 @@ CachedShader::CachedShader(const ShaderParameters& params, ProgramType program_t
|
||||||
disk_cache{params.disk_cache}, device{params.device}, cpu_addr{params.cpu_addr},
|
disk_cache{params.disk_cache}, device{params.device}, cpu_addr{params.cpu_addr},
|
||||||
unique_identifier{params.unique_identifier}, program_type{program_type}, entries{entries},
|
unique_identifier{params.unique_identifier}, program_type{program_type}, entries{entries},
|
||||||
program_code{std::move(program_code)}, program_code_b{std::move(program_code_b)} {
|
program_code{std::move(program_code)}, program_code_b{std::move(program_code_b)} {
|
||||||
if (params.precompiled_variants) {
|
if (!params.precompiled_variants) {
|
||||||
for (const auto& pair : *params.precompiled_variants) {
|
return;
|
||||||
const auto& variant = pair->first.variant;
|
}
|
||||||
programs.emplace(variant, pair->second);
|
for (const auto& pair : *params.precompiled_variants) {
|
||||||
|
auto locker = MakeLocker(system, program_type);
|
||||||
|
const auto& usage = pair->first;
|
||||||
|
FillLocker(*locker, usage);
|
||||||
|
|
||||||
|
std::unique_ptr<LockerVariant>* locker_variant = nullptr;
|
||||||
|
const auto it =
|
||||||
|
std::find_if(locker_variants.begin(), locker_variants.end(), [&](const auto& variant) {
|
||||||
|
return variant->locker->HasEqualKeys(*locker);
|
||||||
|
});
|
||||||
|
if (it == locker_variants.end()) {
|
||||||
|
locker_variant = &locker_variants.emplace_back();
|
||||||
|
*locker_variant = std::make_unique<LockerVariant>();
|
||||||
|
locker_variant->get()->locker = std::move(locker);
|
||||||
|
} else {
|
||||||
|
locker_variant = &*it;
|
||||||
}
|
}
|
||||||
|
locker_variant->get()->programs.emplace(usage.variant, pair->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,19 +424,14 @@ Shader CachedShader::CreateFromCache(const ShaderParameters& params,
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(const ProgramVariant& variant) {
|
std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(const ProgramVariant& variant) {
|
||||||
const auto [entry, is_cache_miss] = programs.try_emplace(variant);
|
UpdateVariant();
|
||||||
|
|
||||||
|
const auto [entry, is_cache_miss] = curr_variant->programs.try_emplace(variant);
|
||||||
auto& program = entry->second;
|
auto& program = entry->second;
|
||||||
if (is_cache_miss) {
|
if (is_cache_miss) {
|
||||||
Tegra::Engines::ConstBufferEngineInterface* engine = nullptr;
|
|
||||||
if (program_type == ProgramType::Compute) {
|
|
||||||
engine = &system.GPU().KeplerCompute();
|
|
||||||
} else {
|
|
||||||
engine = &system.GPU().Maxwell3D();
|
|
||||||
}
|
|
||||||
ConstBufferLocker locker(GetEnginesShaderType(program_type), *engine);
|
|
||||||
program = BuildShader(device, unique_identifier, program_type, program_code, program_code_b,
|
program = BuildShader(device, unique_identifier, program_type, program_code, program_code_b,
|
||||||
variant, locker);
|
variant, *curr_variant->locker);
|
||||||
disk_cache.SaveUsage(GetUsage(variant, locker));
|
disk_cache.SaveUsage(GetUsage(variant, *curr_variant->locker));
|
||||||
|
|
||||||
LabelGLObject(GL_PROGRAM, program->handle, cpu_addr);
|
LabelGLObject(GL_PROGRAM, program->handle, cpu_addr);
|
||||||
}
|
}
|
||||||
|
@ -408,6 +447,25 @@ std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(const ProgramVar
|
||||||
return {program->handle, base_bindings};
|
return {program->handle, base_bindings};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CachedShader::UpdateVariant() {
|
||||||
|
if (curr_variant && !curr_variant->locker->IsConsistent()) {
|
||||||
|
curr_variant = nullptr;
|
||||||
|
}
|
||||||
|
if (!curr_variant) {
|
||||||
|
for (auto& variant : locker_variants) {
|
||||||
|
if (variant->locker->IsConsistent()) {
|
||||||
|
curr_variant = variant.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!curr_variant) {
|
||||||
|
auto& new_variant = locker_variants.emplace_back();
|
||||||
|
new_variant = std::make_unique<LockerVariant>();
|
||||||
|
new_variant->locker = MakeLocker(system, program_type);
|
||||||
|
curr_variant = new_variant.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant,
|
ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant,
|
||||||
const ConstBufferLocker& locker) const {
|
const ConstBufferLocker& locker) const {
|
||||||
ShaderDiskCacheUsage usage;
|
ShaderDiskCacheUsage usage;
|
||||||
|
@ -475,21 +533,11 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!shader) {
|
if (!shader) {
|
||||||
ConstBufferLocker locker(GetEnginesShaderType(unspecialized.program_type));
|
auto locker{MakeLocker(system, unspecialized.program_type)};
|
||||||
for (const auto& key : usage.keys) {
|
FillLocker(*locker, usage);
|
||||||
const auto [buffer, offset] = key.first;
|
|
||||||
locker.InsertKey(buffer, offset, key.second);
|
|
||||||
}
|
|
||||||
for (const auto& [offset, sampler] : usage.bound_samplers) {
|
|
||||||
locker.InsertBoundSampler(offset, sampler);
|
|
||||||
}
|
|
||||||
for (const auto& [key, sampler] : usage.bindless_samplers) {
|
|
||||||
const auto [buffer, offset] = key;
|
|
||||||
locker.InsertBindlessSampler(buffer, offset, sampler);
|
|
||||||
}
|
|
||||||
shader = BuildShader(device, usage.unique_identifier, unspecialized.program_type,
|
shader = BuildShader(device, usage.unique_identifier, unspecialized.program_type,
|
||||||
unspecialized.code, unspecialized.code_b, usage.variant,
|
unspecialized.code, unspecialized.code_b, usage.variant,
|
||||||
locker, true);
|
*locker, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::scoped_lock lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
|
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
|
||||||
|
#include "video_core/shader/const_buffer_locker.h"
|
||||||
#include "video_core/shader/shader_ir.h"
|
#include "video_core/shader/shader_ir.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -31,10 +32,6 @@ namespace Core::Frontend {
|
||||||
class EmuWindow;
|
class EmuWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace VideoCommon::Shader {
|
|
||||||
class ConstBufferLocker;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
class CachedShader;
|
class CachedShader;
|
||||||
|
@ -92,10 +89,17 @@ public:
|
||||||
std::tuple<GLuint, BaseBindings> GetProgramHandle(const ProgramVariant& variant);
|
std::tuple<GLuint, BaseBindings> GetProgramHandle(const ProgramVariant& variant);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct LockerVariant {
|
||||||
|
std::unique_ptr<VideoCommon::Shader::ConstBufferLocker> locker;
|
||||||
|
std::unordered_map<ProgramVariant, CachedProgram> programs;
|
||||||
|
};
|
||||||
|
|
||||||
explicit CachedShader(const ShaderParameters& params, ProgramType program_type,
|
explicit CachedShader(const ShaderParameters& params, ProgramType program_type,
|
||||||
GLShader::ShaderEntries entries, ProgramCode program_code,
|
GLShader::ShaderEntries entries, ProgramCode program_code,
|
||||||
ProgramCode program_code_b);
|
ProgramCode program_code_b);
|
||||||
|
|
||||||
|
void UpdateVariant();
|
||||||
|
|
||||||
ShaderDiskCacheUsage GetUsage(const ProgramVariant& variant,
|
ShaderDiskCacheUsage GetUsage(const ProgramVariant& variant,
|
||||||
const VideoCommon::Shader::ConstBufferLocker& locker) const;
|
const VideoCommon::Shader::ConstBufferLocker& locker) const;
|
||||||
|
|
||||||
|
@ -113,7 +117,8 @@ private:
|
||||||
ProgramCode program_code;
|
ProgramCode program_code;
|
||||||
ProgramCode program_code_b;
|
ProgramCode program_code_b;
|
||||||
|
|
||||||
std::unordered_map<ProgramVariant, CachedProgram> programs;
|
LockerVariant* curr_variant = nullptr;
|
||||||
|
std::vector<std::unique_ptr<LockerVariant>> locker_variants;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
|
class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
|
||||||
|
|
|
@ -82,23 +82,27 @@ bool ConstBufferLocker::IsConsistent() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return std::all_of(keys.begin(), keys.end(),
|
return std::all_of(keys.begin(), keys.end(),
|
||||||
[](const auto& key) {
|
[this](const auto& pair) {
|
||||||
const auto [value, other_value] = key.first;
|
const auto [cbuf, offset] = pair.first;
|
||||||
return value == other_value;
|
const auto value = pair.second;
|
||||||
|
return value == engine->AccessConstBuffer32(stage, cbuf, offset);
|
||||||
}) &&
|
}) &&
|
||||||
std::all_of(bound_samplers.begin(), bound_samplers.end(),
|
std::all_of(bound_samplers.begin(), bound_samplers.end(),
|
||||||
[this](const auto& sampler) {
|
[this](const auto& sampler) {
|
||||||
const auto [key, value] = sampler;
|
const auto [key, value] = sampler;
|
||||||
const auto other_value = engine->AccessBoundSampler(stage, key);
|
return value == engine->AccessBoundSampler(stage, key);
|
||||||
return value == other_value;
|
|
||||||
}) &&
|
}) &&
|
||||||
std::all_of(
|
std::all_of(bindless_samplers.begin(), bindless_samplers.end(),
|
||||||
bindless_samplers.begin(), bindless_samplers.end(), [this](const auto& sampler) {
|
[this](const auto& sampler) {
|
||||||
const auto [cbuf, offset] = sampler.first;
|
const auto [cbuf, offset] = sampler.first;
|
||||||
const auto value = sampler.second;
|
const auto value = sampler.second;
|
||||||
const auto other_value = engine->AccessBindlessSampler(stage, cbuf, offset);
|
return value == engine->AccessBindlessSampler(stage, cbuf, offset);
|
||||||
return value == other_value;
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
bool ConstBufferLocker::HasEqualKeys(const ConstBufferLocker& rhs) const {
|
||||||
|
return keys == rhs.keys && bound_samplers == rhs.bound_samplers &&
|
||||||
|
bindless_samplers == rhs.bindless_samplers;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace VideoCommon::Shader
|
} // namespace VideoCommon::Shader
|
||||||
|
|
|
@ -44,6 +44,9 @@ public:
|
||||||
/// the same value, false otherwise;
|
/// the same value, false otherwise;
|
||||||
bool IsConsistent() const;
|
bool IsConsistent() const;
|
||||||
|
|
||||||
|
/// Returns true if the keys are equal to the other ones in the locker.
|
||||||
|
bool HasEqualKeys(const ConstBufferLocker& rhs) const;
|
||||||
|
|
||||||
/// Gives an getter to the const buffer keys in the database.
|
/// Gives an getter to the const buffer keys in the database.
|
||||||
const KeyMap& GetKeys() const {
|
const KeyMap& GetKeys() const {
|
||||||
return keys;
|
return keys;
|
||||||
|
|
Loading…
Reference in a new issue