mirror of https://github.com/snes9xgit/snes9x.git
Threading fixes.
This commit is contained in:
parent
a438d3fa42
commit
e42dd27cd1
|
@ -1 +1 @@
|
||||||
Subproject commit 9c7fd1a33e5cecbe465e1cd70170167d5e40d398
|
Subproject commit 3ebb72cc7429f0ab8218104dc3687c659c0f364d
|
|
@ -2,6 +2,7 @@
|
||||||
#include "common/audio/s9x_sound_driver_sdl.hpp"
|
#include "common/audio/s9x_sound_driver_sdl.hpp"
|
||||||
#include "common/audio/s9x_sound_driver_cubeb.hpp"
|
#include "common/audio/s9x_sound_driver_cubeb.hpp"
|
||||||
#include <qlabel.h>
|
#include <qlabel.h>
|
||||||
|
#include <qnamespace.h>
|
||||||
#ifdef USE_PULSEAUDIO
|
#ifdef USE_PULSEAUDIO
|
||||||
#include "common/audio/s9x_sound_driver_pulse.hpp"
|
#include "common/audio/s9x_sound_driver_pulse.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
@ -169,9 +170,7 @@ void EmuApplication::suspendThread()
|
||||||
|
|
||||||
if (suspend_count > 0)
|
if (suspend_count > 0)
|
||||||
{
|
{
|
||||||
printf("Suspend %d\n", suspend_count);
|
emu_thread->runOnThread([&] { emu_thread->setStatusBits(EmuThread::eSuspended); }, true);
|
||||||
emu_thread->runOnThread([&] { emu_thread->setStatusBits(EmuThread::eSuspended); });
|
|
||||||
emu_thread->waitForStatusBit(EmuThread::eSuspended);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,12 +182,9 @@ void EmuApplication::unsuspendThread()
|
||||||
if (!emu_thread)
|
if (!emu_thread)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
printf("Un Suspend %d\n", suspend_count);
|
|
||||||
|
|
||||||
if (suspend_count == 0)
|
if (suspend_count == 0)
|
||||||
{
|
{
|
||||||
emu_thread->runOnThread([&] { emu_thread->unsetStatusBits(EmuThread::eSuspended); });
|
emu_thread->runOnThread([&] { emu_thread->unsetStatusBits(EmuThread::eSuspended); }, true);
|
||||||
emu_thread->waitForStatusBitCleared(EmuThread::eSuspended);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,7 +256,6 @@ void EmuApplication::mainLoop()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Here\n");
|
|
||||||
core->mainLoop();
|
core->mainLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -522,11 +517,15 @@ bool EmuApplication::isCoreActive()
|
||||||
return core->active;
|
return core->active;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::runOnThread(std::function<void()> func)
|
void EmuThread::runOnThread(std::function<void()> func, bool blocking)
|
||||||
{
|
{
|
||||||
if (QThread::currentThread() != this)
|
if (QThread::currentThread() != this)
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(this, "runOnThread", Q_ARG(std::function<void()>, func));
|
QMetaObject::invokeMethod(this,
|
||||||
|
"runOnThread",
|
||||||
|
blocking ? Qt::BlockingQueuedConnection : Qt::QueuedConnection,
|
||||||
|
Q_ARG(std::function<void()>, func),
|
||||||
|
Q_ARG(bool, blocking));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,20 +540,12 @@ EmuThread::EmuThread(QThread *main_thread_)
|
||||||
|
|
||||||
void EmuThread::setStatusBits(int new_status)
|
void EmuThread::setStatusBits(int new_status)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(status_mutex);
|
|
||||||
status |= new_status;
|
status |= new_status;
|
||||||
lock.unlock();
|
|
||||||
status_cond.notify_all();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::unsetStatusBits(int new_status)
|
void EmuThread::unsetStatusBits(int new_status)
|
||||||
{
|
{
|
||||||
printf("Old: %08x, new: %08x\n", status, new_status);
|
|
||||||
std::unique_lock lock(status_mutex);
|
|
||||||
status &= ~new_status;
|
status &= ~new_status;
|
||||||
printf("Final: %08x\n", status);
|
|
||||||
lock.unlock();
|
|
||||||
status_cond.notify_all();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::waitForStatusBit(int new_status)
|
void EmuThread::waitForStatusBit(int new_status)
|
||||||
|
@ -564,8 +555,7 @@ void EmuThread::waitForStatusBit(int new_status)
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(status_mutex);
|
QThread::yieldCurrentThread();
|
||||||
status_cond.wait_for(lock, std::chrono::milliseconds(500));
|
|
||||||
if (status & new_status)
|
if (status & new_status)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -578,8 +568,7 @@ void EmuThread::waitForStatusBitCleared(int new_status)
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(status_mutex);
|
QThread::yieldCurrentThread();
|
||||||
status_cond.wait_for(lock, std::chrono::milliseconds(500));
|
|
||||||
if (!(status & new_status))
|
if (!(status & new_status))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -587,14 +576,12 @@ void EmuThread::waitForStatusBitCleared(int new_status)
|
||||||
|
|
||||||
void EmuThread::pause()
|
void EmuThread::pause()
|
||||||
{
|
{
|
||||||
runOnThread([&] { setStatusBits(ePaused); });
|
runOnThread([&] { setStatusBits(ePaused); }, true);
|
||||||
waitForStatusBit(ePaused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::unpause()
|
void EmuThread::unpause()
|
||||||
{
|
{
|
||||||
runOnThread([&] { unsetStatusBits(ePaused); });
|
runOnThread([&] { unsetStatusBits(ePaused); }, true);
|
||||||
waitForStatusBitCleared(ePaused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::run()
|
void EmuThread::run()
|
||||||
|
@ -612,12 +599,10 @@ void EmuThread::run()
|
||||||
|
|
||||||
if (status & (ePaused | eSuspended))
|
if (status & (ePaused | eSuspended))
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for(2ms);
|
QThread::usleep(2000);
|
||||||
printf("Paused: %08x\n", status);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Loop\n");
|
|
||||||
if (main_loop)
|
if (main_loop)
|
||||||
main_loop();
|
main_loop();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,9 @@ Q_OBJECT
|
||||||
};
|
};
|
||||||
|
|
||||||
int status = eDead;
|
int status = eDead;
|
||||||
std::mutex status_mutex;
|
|
||||||
std::condition_variable status_cond;
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void runOnThread(std::function<void()> func);
|
void runOnThread(std::function<void()> func, bool blocking = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EmuApplication
|
struct EmuApplication
|
||||||
|
|
|
@ -12,6 +12,8 @@ class EmuCanvas : public QWidget
|
||||||
|
|
||||||
virtual void deinit() = 0;
|
virtual void deinit() = 0;
|
||||||
virtual void draw() = 0;
|
virtual void draw() = 0;
|
||||||
|
void paintEvent(QPaintEvent *) override = 0;
|
||||||
|
virtual void createContext() {}
|
||||||
void output(uint8_t *buffer, int width, int height, QImage::Format format, int bytes_per_line, double frame_rate);
|
void output(uint8_t *buffer, int width, int height, QImage::Format format, int bytes_per_line, double frame_rate);
|
||||||
void throttle();
|
void throttle();
|
||||||
void resizeEvent(QResizeEvent *event) override = 0;
|
void resizeEvent(QResizeEvent *event) override = 0;
|
||||||
|
|
|
@ -57,11 +57,6 @@ EmuCanvasOpenGL::EmuCanvasOpenGL(EmuConfig *config, QWidget *parent, QWidget *ma
|
||||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
|
|
||||||
createWinId();
|
createWinId();
|
||||||
|
|
||||||
auto timer = new QTimer(this);
|
|
||||||
timer->setSingleShot(true);
|
|
||||||
timer->callOnTimeout([&]{ createContext(); });
|
|
||||||
timer->start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EmuCanvasOpenGL::~EmuCanvasOpenGL()
|
EmuCanvasOpenGL::~EmuCanvasOpenGL()
|
||||||
|
@ -251,6 +246,7 @@ void EmuCanvasOpenGL::draw()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
context->make_current();
|
context->make_current();
|
||||||
|
gladLoaderLoadGL();
|
||||||
|
|
||||||
uploadTexture();
|
uploadTexture();
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
@ -298,7 +294,8 @@ void EmuCanvasOpenGL::resizeEvent(QResizeEvent *event)
|
||||||
{
|
{
|
||||||
QWidget::resizeEvent(event);
|
QWidget::resizeEvent(event);
|
||||||
|
|
||||||
if (!context) return;
|
if (!context)
|
||||||
|
return;
|
||||||
|
|
||||||
auto g = parent->geometry();
|
auto g = parent->geometry();
|
||||||
int s = devicePixelRatio();
|
int s = devicePixelRatio();
|
||||||
|
@ -308,6 +305,8 @@ void EmuCanvasOpenGL::resizeEvent(QResizeEvent *event)
|
||||||
((WaylandEGLContext *)context.get())->resize({ g.x(), g.y(), g.width(), g.height(), s });
|
((WaylandEGLContext *)context.get())->resize({ g.x(), g.y(), g.width(), g.height(), s });
|
||||||
else if (platform == "xcb")
|
else if (platform == "xcb")
|
||||||
((GTKGLXContext *)context.get())->resize();
|
((GTKGLXContext *)context.get())->resize();
|
||||||
|
#else
|
||||||
|
((WGLContext *)context.get())->resize();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,6 +323,9 @@ void EmuCanvasOpenGL::paintEvent(QPaintEvent *event)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
((WGLContext *)context.get())->resize();
|
||||||
|
#endif
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
context->swap_buffers();
|
context->swap_buffers();
|
||||||
|
|
|
@ -14,6 +14,7 @@ class EmuCanvasOpenGL : public EmuCanvas
|
||||||
EmuCanvasOpenGL(EmuConfig *config, QWidget *parent, QWidget *main_window);
|
EmuCanvasOpenGL(EmuConfig *config, QWidget *parent, QWidget *main_window);
|
||||||
~EmuCanvasOpenGL();
|
~EmuCanvasOpenGL();
|
||||||
|
|
||||||
|
void createContext() override;
|
||||||
void deinit() override;
|
void deinit() override;
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
@ -26,7 +27,6 @@ class EmuCanvasOpenGL : public EmuCanvas
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void resizeTexture(int width, int height);
|
void resizeTexture(int width, int height);
|
||||||
void createContext();
|
|
||||||
void createStockShaders();
|
void createStockShaders();
|
||||||
void stockShaderDraw();
|
void stockShaderDraw();
|
||||||
void customShaderDraw();
|
void customShaderDraw();
|
||||||
|
|
|
@ -25,11 +25,6 @@ EmuCanvasVulkan::EmuCanvasVulkan(EmuConfig *config, QWidget *parent, QWidget *ma
|
||||||
|
|
||||||
createWinId();
|
createWinId();
|
||||||
window = windowHandle();
|
window = windowHandle();
|
||||||
|
|
||||||
auto timer = new QTimer(this);
|
|
||||||
timer->setSingleShot(true);
|
|
||||||
timer->callOnTimeout([&]{ createContext(); });
|
|
||||||
timer->start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EmuCanvasVulkan::~EmuCanvasVulkan()
|
EmuCanvasVulkan::~EmuCanvasVulkan()
|
||||||
|
|
|
@ -16,6 +16,7 @@ class EmuCanvasVulkan : public EmuCanvas
|
||||||
EmuCanvasVulkan(EmuConfig *config, QWidget *parent, QWidget *main_window);
|
EmuCanvasVulkan(EmuConfig *config, QWidget *parent, QWidget *main_window);
|
||||||
~EmuCanvasVulkan();
|
~EmuCanvasVulkan();
|
||||||
|
|
||||||
|
void createContext();
|
||||||
void deinit() override;
|
void deinit() override;
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
@ -37,7 +38,6 @@ class EmuCanvasVulkan : public EmuCanvas
|
||||||
std::unique_ptr<Vulkan::ShaderChain> shader_chain;
|
std::unique_ptr<Vulkan::ShaderChain> shader_chain;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createContext();
|
|
||||||
void tryLoadShader();
|
void tryLoadShader();
|
||||||
std::string current_shader;
|
std::string current_shader;
|
||||||
QWindow *window = nullptr;
|
QWindow *window = nullptr;
|
||||||
|
|
|
@ -84,22 +84,40 @@ void EmuMainWindow::createCanvas()
|
||||||
auto central_widget = new QStackedWidget();
|
auto central_widget = new QStackedWidget();
|
||||||
|
|
||||||
if (app->config->display_driver == "vulkan")
|
if (app->config->display_driver == "vulkan")
|
||||||
|
{
|
||||||
canvas = new EmuCanvasVulkan(app->config.get(), central_widget, this);
|
canvas = new EmuCanvasVulkan(app->config.get(), central_widget, this);
|
||||||
|
QGuiApplication::processEvents();
|
||||||
|
canvas->createContext();
|
||||||
|
}
|
||||||
else if (app->config->display_driver == "opengl")
|
else if (app->config->display_driver == "opengl")
|
||||||
|
{
|
||||||
canvas = new EmuCanvasOpenGL(app->config.get(), central_widget, this);
|
canvas = new EmuCanvasOpenGL(app->config.get(), central_widget, this);
|
||||||
|
QGuiApplication::processEvents();
|
||||||
|
app->emu_thread->runOnThread([&] { canvas->createContext(); }, true);
|
||||||
|
}
|
||||||
|
|
||||||
central_widget->addWidget(canvas);
|
central_widget->addWidget(canvas);
|
||||||
central_widget->setCurrentWidget(canvas);
|
central_widget->setCurrentWidget(canvas);
|
||||||
setCentralWidget(central_widget);
|
setCentralWidget(central_widget);
|
||||||
using_stacked_widget = true;
|
using_stacked_widget = true;
|
||||||
|
QGuiApplication::processEvents();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (app->config->display_driver == "vulkan")
|
if (app->config->display_driver == "vulkan")
|
||||||
|
{
|
||||||
canvas = new EmuCanvasVulkan(app->config.get(), this, this);
|
canvas = new EmuCanvasVulkan(app->config.get(), this, this);
|
||||||
|
QGuiApplication::processEvents();
|
||||||
|
canvas->createContext();
|
||||||
|
}
|
||||||
else if (app->config->display_driver == "opengl")
|
else if (app->config->display_driver == "opengl")
|
||||||
|
{
|
||||||
canvas = new EmuCanvasOpenGL(app->config.get(), this, this);
|
canvas = new EmuCanvasOpenGL(app->config.get(), this, this);
|
||||||
|
QGuiApplication::processEvents();
|
||||||
|
app->emu_thread->runOnThread([&] { canvas->createContext(); }, true);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
canvas = new EmuCanvasQt(app->config.get(), this, this);
|
canvas = new EmuCanvasQt(app->config.get(), this, this);
|
||||||
|
|
||||||
|
@ -447,6 +465,7 @@ bool EmuMainWindow::event(QEvent *event)
|
||||||
{
|
{
|
||||||
toggleFullscreen();
|
toggleFullscreen();
|
||||||
}
|
}
|
||||||
|
app->stopThread();
|
||||||
if (canvas)
|
if (canvas)
|
||||||
canvas->deinit();
|
canvas->deinit();
|
||||||
QGuiApplication::sync();
|
QGuiApplication::sync();
|
||||||
|
@ -530,13 +549,24 @@ void EmuMainWindow::toggleFullscreen()
|
||||||
|
|
||||||
bool EmuMainWindow::eventFilter(QObject *watched, QEvent *event)
|
bool EmuMainWindow::eventFilter(QObject *watched, QEvent *event)
|
||||||
{
|
{
|
||||||
if (watched == canvas && event->type() == QEvent::Resize)
|
if (watched == canvas)
|
||||||
{
|
{
|
||||||
app->suspendThread();
|
if (event->type() == QEvent::Resize)
|
||||||
canvas->resizeEvent((QResizeEvent *)event);
|
{
|
||||||
event->accept();
|
app->suspendThread();
|
||||||
app->unsuspendThread();
|
canvas->resizeEvent((QResizeEvent *)event);
|
||||||
return true;
|
event->accept();
|
||||||
|
app->unsuspendThread();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (event->type() == QEvent::Paint)
|
||||||
|
{
|
||||||
|
app->emu_thread->runOnThread([&] {
|
||||||
|
canvas->paintEvent((QPaintEvent *)event);
|
||||||
|
}, true);
|
||||||
|
event->accept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->type() != QEvent::KeyPress && event->type() != QEvent::KeyRelease)
|
if (event->type() != QEvent::KeyPress && event->type() != QEvent::KeyRelease)
|
||||||
|
|
|
@ -17,6 +17,8 @@ int main(int argc, char *argv[])
|
||||||
signal(s, quit_handler);
|
signal(s, quit_handler);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
emu.startThread();
|
||||||
|
|
||||||
emu.config = std::make_unique<EmuConfig>();
|
emu.config = std::make_unique<EmuConfig>();
|
||||||
emu.config->setDefaults();
|
emu.config->setDefaults();
|
||||||
emu.config->loadFile(EmuConfig::findConfigFile());
|
emu.config->loadFile(EmuConfig::findConfigFile());
|
||||||
|
@ -27,7 +29,6 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
emu.updateBindings();
|
emu.updateBindings();
|
||||||
emu.startInputTimer();
|
emu.startInputTimer();
|
||||||
emu.startThread();
|
|
||||||
emu.qtapp->exec();
|
emu.qtapp->exec();
|
||||||
|
|
||||||
emu.stopThread();
|
emu.stopThread();
|
||||||
|
|
Loading…
Reference in New Issue