Keep shader trap lock held for the duration of an execution

Avoids constant relocking on the GPFIFO thread (~0.5% of total time)
This commit is contained in:
Billy Laws 2022-10-09 13:52:40 +01:00
parent 314a9bccbc
commit 751e3356e1
2 changed files with 26 additions and 7 deletions

View File

@ -236,7 +236,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
return; return;
} }
auto [blockMapping, blockOffset]{ctx.channelCtx.asCtx->gmmu.LookupBlock(engine->programRegion + engine->pipeline.programOffset)}; auto[blockMapping, blockOffset]{ctx.channelCtx.asCtx->gmmu.LookupBlock(engine->programRegion + engine->pipeline.programOffset)};
// Skip looking up the mirror if it is the same as the one used for the previous update // Skip looking up the mirror if it is the same as the one used for the previous update
if (!mirrorBlock.valid() || !mirrorBlock.contains(blockMapping)) { if (!mirrorBlock.valid() || !mirrorBlock.contains(blockMapping)) {
@ -246,8 +246,13 @@ namespace skyline::gpu::interconnect::maxwell3d {
auto newIt{mirrorMap.emplace(blockMapping.data(), std::make_unique<MirrorEntry>(ctx.memory.CreateMirror(blockMapping)))}; auto newIt{mirrorMap.emplace(blockMapping.data(), std::make_unique<MirrorEntry>(ctx.memory.CreateMirror(blockMapping)))};
// We need to create the trap after allocating the entry so that we have an `invalid` pointer we can pass in // We need to create the trap after allocating the entry so that we have an `invalid` pointer we can pass in
auto trapHandle{ctx.nce.CreateTrap(blockMapping, [](){}, [](){ return true; }, [dirty = &newIt.first->second->dirty, mutex = &trapMutex](){ auto trapHandle{ctx.nce.CreateTrap(blockMapping, [mutex = &trapMutex]() {
std::scoped_lock lock{*mutex}; // Don't use lock callback here since we need trapMutex to be always locked on accesses to prevent UAFs std::scoped_lock lock{*mutex};
return;
}, []() { return true; }, [dirty = &newIt.first->second->dirty, mutex = &trapMutex]() {
std::unique_lock lock{*mutex, std::try_to_lock};
if (!lock)
return false;
*dirty = true; *dirty = true;
return true; return true;
})}; })};
@ -264,6 +269,9 @@ namespace skyline::gpu::interconnect::maxwell3d {
mirrorBlock = blockMapping; mirrorBlock = blockMapping;
} }
if (!trapExecutionLock)
trapExecutionLock.emplace(trapMutex);
// If the mirror entry has been written to, clear its shader binary cache and retrap to catch any future writes // If the mirror entry has been written to, clear its shader binary cache and retrap to catch any future writes
if (entry->dirty) { if (entry->dirty) {
entry->cache.clear(); entry->cache.clear();
@ -300,15 +308,20 @@ namespace skyline::gpu::interconnect::maxwell3d {
entry->cache.insert({blockMapping.data() + blockOffset, CacheEntry{binary, hash}}); 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) { bool PipelineStageState::Refresh(InterconnectContext &ctx) {
std::scoped_lock lock{trapMutex}; if (!trapExecutionLock)
trapExecutionLock.emplace(trapMutex);
if (entry && entry->dirty) if (entry && entry->dirty)
return true; return true;
return false; return false;
} }
void PipelineStageState::PurgeCaches() {
trapExecutionLock.reset();
}
PipelineStageState::~PipelineStageState() { PipelineStageState::~PipelineStageState() {
std::scoped_lock lock{trapMutex}; std::scoped_lock lock{trapMutex};
//for (const auto &mirror : mirrorMap) //for (const auto &mirror : mirrorMap)
@ -575,6 +588,8 @@ namespace skyline::gpu::interconnect::maxwell3d {
void PipelineState::PurgeCaches() { void PipelineState::PurgeCaches() {
pipeline = nullptr; pipeline = nullptr;
for (auto &stage : pipelineStages)
stage.MarkDirty(true);
} }
std::shared_ptr<TextureView> PipelineState::GetColorRenderTargetForClear(InterconnectContext &ctx, size_t index) { std::shared_ptr<TextureView> PipelineState::GetColorRenderTargetForClear(InterconnectContext &ctx, size_t index) {

View File

@ -55,7 +55,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
void Flush(InterconnectContext &ctx, PackedPipelineState &packedState); void Flush(InterconnectContext &ctx, PackedPipelineState &packedState);
}; };
class PipelineStageState : dirty::RefreshableManualDirty { class PipelineStageState : dirty::RefreshableManualDirty, dirty::CachedManualDirty {
public: public:
struct EngineRegisters { struct EngineRegisters {
const engine::Pipeline &pipeline; const engine::Pipeline &pipeline;
@ -80,6 +80,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
tsl::robin_map<u8 *, CacheEntry> cache; tsl::robin_map<u8 *, CacheEntry> cache;
std::optional<nce::NCE::TrapHandle> trap; std::optional<nce::NCE::TrapHandle> trap;
bool dirty{}; bool dirty{};
MirrorEntry(span<u8> alignedMirror) : mirror{alignedMirror} {} MirrorEntry(span<u8> alignedMirror) : mirror{alignedMirror} {}
}; };
@ -87,7 +88,8 @@ namespace skyline::gpu::interconnect::maxwell3d {
engine::Pipeline::Shader::Type shaderType; engine::Pipeline::Shader::Type shaderType;
tsl::robin_map<u8 *, std::unique_ptr<MirrorEntry>> mirrorMap; tsl::robin_map<u8 *, std::unique_ptr<MirrorEntry>> mirrorMap;
SpinLock trapMutex; //!< Protects accesses from trap handlers to the mirror map std::mutex trapMutex; //!< Protects accesses from trap handlers to the mirror map
std::optional<std::scoped_lock<std::mutex>> trapExecutionLock;
MirrorEntry *entry{}; MirrorEntry *entry{};
span<u8> mirrorBlock{}; //!< Guest mapped memory block corresponding to `entry` span<u8> mirrorBlock{}; //!< Guest mapped memory block corresponding to `entry`
@ -102,6 +104,8 @@ namespace skyline::gpu::interconnect::maxwell3d {
void Flush(InterconnectContext &ctx); void Flush(InterconnectContext &ctx);
bool Refresh(InterconnectContext &ctx); bool Refresh(InterconnectContext &ctx);
void PurgeCaches();
}; };
class VertexInputState : dirty::ManualDirty { class VertexInputState : dirty::ManualDirty {