sa2: remove multithreading.
Obsolete with recent Pi4 hardware, and even with Pi3, advantage is little.
This commit is contained in:
parent
a583fd75f3
commit
cbf9b078c6
4 changed files with 38 additions and 134 deletions
|
@ -93,9 +93,6 @@ namespace common2
|
|||
|
||||
po::options_description sdlDesc("SDL");
|
||||
sdlDesc.add_options()
|
||||
("multi-threaded,m", "Multi threaded")
|
||||
("timer-interval,i", po::value<int>()->default_value(options.timerInterval), "Timer interval in ms")
|
||||
("loose-mutex,l", "Loose mutex")
|
||||
("sdl-driver", po::value<int>()->default_value(options.sdlDriver), "SDL driver")
|
||||
("gl-swap", po::value<int>()->default_value(options.glSwapInterval), "SDL_GL_SwapInterval")
|
||||
("imgui", "Render with Dear ImGui")
|
||||
|
@ -123,9 +120,6 @@ namespace common2
|
|||
|
||||
options.saveConfigurationOnExit = vm.count("save-conf");
|
||||
options.useQtIni = vm.count("qt-ini");
|
||||
options.multiThreaded = vm.count("multi-threaded");
|
||||
options.looseMutex = vm.count("loose-mutex");
|
||||
options.timerInterval = vm["timer-interval"].as<int>();
|
||||
options.sdlDriver = vm["sdl-driver"].as<int>();
|
||||
options.glSwapInterval = vm["gl-swap"].as<int>();
|
||||
options.imgui = vm.count("imgui");
|
||||
|
|
|
@ -41,9 +41,6 @@ namespace common2
|
|||
|
||||
bool run = true; // false if options include "-h"
|
||||
|
||||
bool multiThreaded = false;
|
||||
bool looseMutex = false; // whether SDL_UpdateTexture is mutex protected (from CPU)
|
||||
int timerInterval = 16; // only when multithreaded
|
||||
bool fixedSpeed = false; // default adaptive
|
||||
|
||||
int sdlDriver = -1; // default = -1 to let SDL choose
|
||||
|
|
|
@ -15,10 +15,6 @@ If you have a modern gamepad where the axes (``LEFTX`` and ``LEFTY``) move in a
|
|||
|
||||
On a Raspberry Pi, one needs the KMS (fake or not). Better performance has been observed with the ``opengles2`` driver (use ``sa2 --sdl-driver 1``).
|
||||
|
||||
It is possible to run the CPU in a separate thread to keep the emulator running in real time (necessary for slower Pis, with some Apple video types and bigger window sizes):
|
||||
- ``sa2 -m``
|
||||
- optionally add ``-l``
|
||||
|
||||
## Dear ImGui
|
||||
|
||||
With the flag ``--imgui``, the rendering if performned with [Dear ImGui](https://github.com/ocornut/imgui). In this case ``--sdl-driver`` is ignored and a OpenGL ES2.0 implementation is required.
|
||||
|
@ -52,22 +48,18 @@ Speech / phonemes are known to hang the emulator.
|
|||
At the end of the run, it will print stats about timings:
|
||||
```
|
||||
Video refresh rate: 60 Hz, 16.67 ms
|
||||
Global: [. .], total = 7789.16 ms, mean = 7789.16 ms, std = 0.00 ms, n = 1
|
||||
Events: [0 M], total = 22.42 ms, mean = 0.05 ms, std = 0.17 ms, n = 471
|
||||
Texture: [0 M], total = 113.32 ms, mean = 0.24 ms, std = 0.06 ms, n = 471
|
||||
Screen: [0 .], total = 7624.87 ms, mean = 16.19 ms, std = 1.66 ms, n = 471
|
||||
CPU: [1 M], total = 647.21 ms, mean = 1.34 ms, std = 0.48 ms, n = 484
|
||||
Global: total = 7789.16 ms, mean = 7789.16 ms, std = 0.00 ms, n = 1
|
||||
Events: total = 22.42 ms, mean = 0.05 ms, std = 0.17 ms, n = 471
|
||||
Texture: total = 113.32 ms, mean = 0.24 ms, std = 0.06 ms, n = 471
|
||||
Screen: total = 7624.87 ms, mean = 16.19 ms, std = 1.66 ms, n = 471
|
||||
CPU: total = 647.21 ms, mean = 1.34 ms, std = 0.48 ms, n = 484
|
||||
Expected clock: 1020484.45 Hz, 7.74 s
|
||||
Actual clock: 1014560.11 Hz, 7.79 s
|
||||
```
|
||||
|
||||
The meaning of ``[0 M]`` is: 0/1 which thread and ``M`` if it is in the mutex protected area.
|
||||
|
||||
- ``events``: SDL events and audio
|
||||
- ``texture``: ``SDL_UpdateTexture``
|
||||
- ``screen``: ``SDL_RenderCopyEx`` and ``SDL_RenderPresent`` (this includes ``vsync``)
|
||||
- ``cpu``: AW's code
|
||||
|
||||
They do not include time spent in locking.
|
||||
|
||||
The clock shows expected vs actual speed.
|
||||
|
|
|
@ -160,124 +160,45 @@ void run_sdl(int argc, const char * argv [])
|
|||
const std::string globalTag = ". .";
|
||||
std::string updateTextureTimerTag, refreshScreenTimerTag, cpuTimerTag, eventTimerTag;
|
||||
|
||||
if (options.multiThreaded)
|
||||
// it does not need to be exact
|
||||
const size_t oneFrame = 1000 / fps;
|
||||
|
||||
bool quit = false;
|
||||
|
||||
do
|
||||
{
|
||||
refreshScreenTimerTag = "0 .";
|
||||
cpuTimerTag = "1 M";
|
||||
eventTimerTag = "0 M";
|
||||
if (options.looseMutex)
|
||||
frameTimer.tic();
|
||||
|
||||
eventTimer.tic();
|
||||
sa2::writeAudio();
|
||||
frame->ProcessEvents(quit);
|
||||
eventTimer.toc();
|
||||
|
||||
cpuTimer.tic();
|
||||
frame->ExecuteOneFrame(oneFrame);
|
||||
cpuTimer.toc();
|
||||
|
||||
updateTextureTimer.tic();
|
||||
frame->UpdateTexture();
|
||||
updateTextureTimer.toc();
|
||||
|
||||
if (!options.headless)
|
||||
{
|
||||
updateTextureTimerTag = "0 .";
|
||||
refreshScreenTimer.tic();
|
||||
frame->RenderPresent();
|
||||
refreshScreenTimer.toc();
|
||||
}
|
||||
else
|
||||
{
|
||||
updateTextureTimerTag = "0 M";
|
||||
}
|
||||
|
||||
std::shared_ptr<SDL_mutex> mutex(SDL_CreateMutex(), SDL_DestroyMutex);
|
||||
|
||||
Data data;
|
||||
data.mutex = mutex.get();
|
||||
data.timer = &cpuTimer;
|
||||
data.frame = frame.get();
|
||||
|
||||
const SDL_TimerID timer = SDL_AddTimer(options.timerInterval, emulator_callback, &data);
|
||||
|
||||
bool quit = false;
|
||||
do
|
||||
{
|
||||
frameTimer.tic();
|
||||
SDL_LockMutex(data.mutex);
|
||||
|
||||
eventTimer.tic();
|
||||
sa2::writeAudio();
|
||||
frame->ProcessEvents(quit);
|
||||
eventTimer.toc();
|
||||
|
||||
if (options.looseMutex)
|
||||
{
|
||||
// loose mutex
|
||||
// unlock early and let CPU run again in the timer callback
|
||||
SDL_UnlockMutex(data.mutex);
|
||||
// but the texture will be updated concurrently with the CPU updating the video buffer
|
||||
// pixels are not atomic, so a pixel error could happen (if pixel changes while being read)
|
||||
// on the positive side this will release pressure from CPU and allow for more parallelism
|
||||
}
|
||||
|
||||
updateTextureTimer.tic();
|
||||
frame->UpdateTexture();
|
||||
updateTextureTimer.toc();
|
||||
|
||||
if (!options.looseMutex)
|
||||
{
|
||||
// safe mutex, only unlock after texture has been updated
|
||||
// this will stop the CPU for longer
|
||||
SDL_UnlockMutex(data.mutex);
|
||||
}
|
||||
|
||||
if (!options.headless)
|
||||
{
|
||||
refreshScreenTimer.tic();
|
||||
frame->RenderPresent();
|
||||
refreshScreenTimer.toc();
|
||||
}
|
||||
frameTimer.toc();
|
||||
} while (!quit);
|
||||
|
||||
SDL_RemoveTimer(timer);
|
||||
// if the following enough to make sure the timer has finished
|
||||
// and wont be called again?
|
||||
SDL_LockMutex(data.mutex);
|
||||
SDL_UnlockMutex(data.mutex);
|
||||
}
|
||||
else
|
||||
{
|
||||
refreshScreenTimerTag = "0 .";
|
||||
cpuTimerTag = "0 .";
|
||||
eventTimerTag = "0 .";
|
||||
updateTextureTimerTag = "0 .";
|
||||
|
||||
bool quit = false;
|
||||
|
||||
// it does not need to be exact
|
||||
const size_t oneFrame = 1000 / fps;
|
||||
|
||||
do
|
||||
{
|
||||
frameTimer.tic();
|
||||
|
||||
eventTimer.tic();
|
||||
sa2::writeAudio();
|
||||
frame->ProcessEvents(quit);
|
||||
eventTimer.toc();
|
||||
|
||||
cpuTimer.tic();
|
||||
frame->ExecuteOneFrame(oneFrame);
|
||||
cpuTimer.toc();
|
||||
|
||||
updateTextureTimer.tic();
|
||||
frame->UpdateTexture();
|
||||
updateTextureTimer.toc();
|
||||
|
||||
if (!options.headless)
|
||||
{
|
||||
refreshScreenTimer.tic();
|
||||
frame->RenderPresent();
|
||||
refreshScreenTimer.toc();
|
||||
}
|
||||
frameTimer.toc();
|
||||
} while (!quit);
|
||||
}
|
||||
frameTimer.toc();
|
||||
} while (!quit);
|
||||
|
||||
global.toc();
|
||||
|
||||
const char sep[] = "], ";
|
||||
std::cerr << "Global: [" << globalTag << sep << global << std::endl;
|
||||
std::cerr << "Frame: [" << globalTag << sep << frameTimer << std::endl;
|
||||
std::cerr << "Screen: [" << refreshScreenTimerTag << sep << refreshScreenTimer << std::endl;
|
||||
std::cerr << "Texture: [" << updateTextureTimerTag << sep << updateTextureTimer << std::endl;
|
||||
std::cerr << "Events: [" << eventTimerTag << sep << eventTimer << std::endl;
|
||||
std::cerr << "CPU: [" << cpuTimerTag << sep << cpuTimer << std::endl;
|
||||
std::cerr << "Global: " << global << std::endl;
|
||||
std::cerr << "Frame: " << frameTimer << std::endl;
|
||||
std::cerr << "Screen: " << refreshScreenTimer << std::endl;
|
||||
std::cerr << "Texture: " << updateTextureTimer << std::endl;
|
||||
std::cerr << "Events: " << eventTimer << std::endl;
|
||||
std::cerr << "CPU: " << cpuTimer << std::endl;
|
||||
|
||||
const double timeInSeconds = global.getTimeInSeconds();
|
||||
const double actualClock = g_nCumulativeCycles / timeInSeconds;
|
||||
|
|
Loading…
Add table
Reference in a new issue