mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 15:00:30 +02:00
SoftGPU: Wake only threads with tiles to draw, and chain the wakes
Waking a worker is a condition variable signal, a system call, and the main thread paid one per idle worker every 48 queued items. With Tekken 6's tiny draws (about 5 vertices each) the workers keep running dry, and those signals were about 10% of the main thread, which is what limits it there. Now no more threads are woken than tiles have work, and only the first is woken from the main thread; it wakes the rest. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
bec1eacd77
commit
1228837688
2 files changed
+40
-6
No files matched your search
@@ -20,6 +20,7 @@
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
#include "Common/BitSet.h"
|
||||
#include "Common/Profiler/Profiler.h"
|
||||
#include "Common/Thread/ParallelLoop.h"
|
||||
#include "Common/Thread/ThreadManager.h"
|
||||
@@ -126,6 +127,7 @@ public:
|
||||
}
|
||||
|
||||
void Run() override {
|
||||
binner_->WakeChained();
|
||||
binner_->ProcessTiles(index_);
|
||||
status_ = false;
|
||||
// Work queued after the last look, but before status_ said we were done, would otherwise wait.
|
||||
@@ -652,18 +654,47 @@ void BinManager::ReclaimItems() {
|
||||
}
|
||||
|
||||
void BinManager::WakeTasks() {
|
||||
int threads = 0;
|
||||
for (int i = 0; i < maxTasks_; ++i) {
|
||||
threads++;
|
||||
if (taskStatus_[i])
|
||||
// No more threads than tiles with work.
|
||||
int pending = 0;
|
||||
const int count = activeCount_.load(std::memory_order_relaxed);
|
||||
for (int n = 0; n < count && pending < maxTasks_; ++n) {
|
||||
const Tile &tile = tiles_[activeTiles_[n]];
|
||||
if (tile.head.load(std::memory_order_relaxed) != tile.tail.load(std::memory_order_relaxed))
|
||||
pending++;
|
||||
}
|
||||
|
||||
int first = -1;
|
||||
uint64_t chain = 0;
|
||||
for (int i = 0; i < maxTasks_ && pending > 0; ++i) {
|
||||
if (taskStatus_[i]) {
|
||||
pending--;
|
||||
continue;
|
||||
}
|
||||
|
||||
waitable_->Fill();
|
||||
taskStatus_[i] = true;
|
||||
g_threadManager.EnqueueTaskOnThread(i, taskLists_[i].Next());
|
||||
if (first < 0)
|
||||
first = i;
|
||||
else
|
||||
chain |= 1ULL << i;
|
||||
pending--;
|
||||
enqueues_++;
|
||||
}
|
||||
mostThreads_ = std::max(mostThreads_, threads);
|
||||
if (first < 0)
|
||||
return;
|
||||
if (chain != 0)
|
||||
chainWake_.fetch_or(chain, std::memory_order_release);
|
||||
g_threadManager.EnqueueTaskOnThread(first, taskLists_[first].Next());
|
||||
mostThreads_ = std::max(mostThreads_, maxTasks_);
|
||||
}
|
||||
|
||||
void BinManager::WakeChained() {
|
||||
uint64_t chain = chainWake_.exchange(0, std::memory_order_acquire);
|
||||
while (chain != 0) {
|
||||
const int i = LeastSignificantSetBit(chain);
|
||||
chain &= chain - 1;
|
||||
g_threadManager.EnqueueTaskOnThread(i, taskLists_[i].Next());
|
||||
}
|
||||
}
|
||||
|
||||
// The queue is full: with threads, wait for drawn items to free up, helping with the drawing.
|
||||
|
||||
@@ -264,6 +264,8 @@ private:
|
||||
int maxTasks_ = 1;
|
||||
BinTaskList taskLists_[MAX_POSSIBLE_TASKS];
|
||||
std::atomic<bool> taskStatus_[MAX_POSSIBLE_TASKS];
|
||||
// Threads whose tasks the first one woken enqueues: waking a thread is a system call, kept off this one.
|
||||
std::atomic<uint64_t> chainWake_{ 0 };
|
||||
|
||||
// With threads, queued items are binned into screen tiles. Any thread can take a tile with work and
|
||||
// draws its items in order; only one at a time, so each pixel still sees the primitives in order.
|
||||
@@ -359,6 +361,7 @@ private:
|
||||
bool NeedsOrder(const BinItem &item);
|
||||
void ReclaimItems();
|
||||
void WakeTasks();
|
||||
void WakeChained();
|
||||
bool ProcessTiles(int start);
|
||||
|
||||
friend class DrawBinItemsTask;
|
||||
|
||||
Reference in new issue
Block a user