Implement simple pipeline transition cache

Avoids the need to hash PipelineState when we can guess the pipeline that will be used next. This could very easily be optimised in the future with generational, usage-based caching if necessary.
This commit is contained in:
Billy Laws 2022-09-14 23:13:00 +01:00
parent 302b2fcc3f
commit 388cff3353
4 changed files with 37 additions and 6 deletions

View File

@ -505,13 +505,27 @@ namespace skyline::gpu::interconnect::maxwell3d {
Pipeline::Pipeline(InterconnectContext &ctx, const PackedPipelineState &packedState, const std::array<ShaderBinary, engine::PipelineCount> &shaderBinaries, span<TextureView *> colorAttachments, TextureView *depthAttachment)
: shaderStages{MakePipelineShaders(ctx, packedState, shaderBinaries)},
descriptorSetLayoutBindings{MakePipelineDescriptorSetLayoutBindings(shaderStages)},
compiledPipeline{MakeCompiledPipeline(ctx, packedState, shaderStages, descriptorSetLayoutBindings, colorAttachments, depthAttachment)} {
compiledPipeline{MakeCompiledPipeline(ctx, packedState, shaderStages, descriptorSetLayoutBindings, colorAttachments, depthAttachment)},
sourcePackedState{packedState} {
}
Pipeline *Pipeline::LookupNext(const PackedPipelineState &packedState) {
auto it{std::find_if(transitionCache.begin(), transitionCache.end(), [&packedState](auto pipeline) {
if (pipeline && pipeline->sourcePackedState == packedState)
return true;
else
return false;
})};
if (it != transitionCache.end())
return *it;
return nullptr;
}
void Pipeline::AddTransition(const PackedPipelineState &packedState, Pipeline *next) {}
void Pipeline::AddTransition(Pipeline *next) {
transitionCache[transitionCacheNextIdx] = next;
transitionCacheNextIdx = (transitionCacheNextIdx + 1) % transitionCache.size();
}
}

View File

@ -32,12 +32,17 @@ namespace skyline::gpu::interconnect::maxwell3d {
std::vector<vk::DescriptorSetLayoutBinding> descriptorSetLayoutBindings;
cache::GraphicsPipelineCache::CompiledPipeline compiledPipeline;
std::array<Pipeline *, 4> transitionCache{};
size_t transitionCacheNextIdx{};
public:
PackedPipelineState sourcePackedState;
Pipeline(InterconnectContext &ctx, const PackedPipelineState &packedState, const std::array<ShaderBinary, engine::PipelineCount> &shaderBinaries, span<TextureView *> colorAttachments, TextureView *depthAttachment);
Pipeline *LookupNext(const PackedPipelineState &packedState);
void AddTransition(const PackedPipelineState &packedState, Pipeline *next);
void AddTransition(Pipeline *next);
};
class PipelineManager {

View File

@ -294,6 +294,15 @@ namespace skyline::gpu::interconnect::maxwell3d {
entry->cache.insert({blockMapping.data() + blockOffset, CacheEntry{binary, hash}});
}
// TODO: this needs to be checked every draw irresspective of pipeline dirtiness
bool PipelineStageState::Refresh(InterconnectContext &ctx) {
std::scoped_lock lock{trapMutex};
if (entry && entry->dirty)
return true;
return false;
}
PipelineStageState::~PipelineStageState() {
std::scoped_lock lock{trapMutex};
//for (const auto &mirror : mirrorMap)
@ -504,10 +513,11 @@ namespace skyline::gpu::interconnect::maxwell3d {
pipeline = newPipeline;
return;
}
}
}
auto newPipeline{pipelineManager.FindOrCreate(ctx, packedState, shaderBinaries, colorAttachments, depthAttachment)};
pipeline->AddTransition(packedState, newPipeline);
if (pipeline)
pipeline->AddTransition(newPipeline);
pipeline = newPipeline;
}

View File

@ -55,7 +55,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
void Flush(InterconnectContext &ctx, PackedPipelineState &packedState);
};
class PipelineStageState : dirty::ManualDirty {
class PipelineStageState : dirty::RefreshableManualDirty {
public:
struct EngineRegisters {
const engine::Pipeline &pipeline;
@ -100,6 +100,8 @@ namespace skyline::gpu::interconnect::maxwell3d {
~PipelineStageState();
void Flush(InterconnectContext &ctx);
bool Refresh(InterconnectContext &ctx);
};
class VertexInputState : dirty::ManualDirty {