mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-09-30 14:04:55 -07:00
d51de976b1
## Link to GitHub Issue or related Pull Request, if one exists #0 ## Description of change Moves the API capture readback off the game's Present thread while a video stream client is connected. The readback is a `LockRect` plus a memcpy of the whole back buffer, roughly 635us at 720p and 1270us at 1080p. On the Present thread that comes out of the game's frame budget: TDJ (at 120Hz) dropped to 117fps with a 60fps stream running, and reading on a pool thread instead gave the full 120 back. Only streaming takes the off-thread path, gated on a new `capture_pump::screen_claimed()`. Screenshots, one-off API captures, and `THREAD_BAN` games all keep the existing inline read for compat reasons. A pool thread in `LockRect` while the Present thread sat inside `GetRenderTargetData` deadlocks DDR X2 for example. `CLAIMED[]` becomes `std::atomic<bool>` so the capture path does not take a lock on the Present thread. The read pool has a single worker so frames cannot be enqueued out of order, and both capture pools are never destroyed so a late read cannot queue onto a torn-down pool. The capture pipeline itself is unchanged: `GetRenderTargetData` is still synchronous on the Present thread. ## Testing DDR X2 World IIDX TDJ SDVX VM
60 lines
1.4 KiB
C++
60 lines
1.4 KiB
C++
#include "capture_pump.h"
|
|
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <mutex>
|
|
|
|
#include "hooks/graphics/graphics.h"
|
|
|
|
namespace api::capture_pump {
|
|
|
|
namespace {
|
|
|
|
std::array<std::mutex, GRAPHICS_CAPTURE_SCREEN_NO> CONSUMER_M;
|
|
|
|
// read once per capture from the present thread, so it stays lock free
|
|
std::atomic<bool> CLAIMED[GRAPHICS_CAPTURE_SCREEN_NO] {};
|
|
|
|
bool valid_screen(int screen) {
|
|
return 0 <= screen && screen < static_cast<int>(GRAPHICS_CAPTURE_SCREEN_NO);
|
|
}
|
|
}
|
|
|
|
bool capture_direct(int screen, std::shared_ptr<uint8_t[]> &out, int divide,
|
|
uint64_t *timestamp, int *width, int *height) {
|
|
|
|
if (!valid_screen(screen)) {
|
|
return false;
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(CONSUMER_M[screen]);
|
|
graphics_capture_trigger(screen);
|
|
return graphics_capture_receive_raw(
|
|
screen, out, divide, timestamp, width, height);
|
|
}
|
|
|
|
bool claim_screen(int screen) {
|
|
if (!valid_screen(screen)) {
|
|
return false;
|
|
}
|
|
|
|
return !CLAIMED[screen].exchange(true);
|
|
}
|
|
|
|
void release_screen(int screen) {
|
|
if (!valid_screen(screen)) {
|
|
return;
|
|
}
|
|
|
|
CLAIMED[screen] = false;
|
|
}
|
|
|
|
bool screen_claimed(int screen) {
|
|
if (!valid_screen(screen)) {
|
|
return false;
|
|
}
|
|
|
|
return CLAIMED[screen];
|
|
}
|
|
}
|