Index: visualc_net/dosbox.vcproj
===================================================================
--- visualc_net/dosbox.vcproj (revision 3827)
+++ visualc_net/dosbox.vcproj (working copy)
@@ -635,6 +635,12 @@
RelativePath="..\src\gui\render_templates.h">
+
+
+
+
w*sdl.blit.surface->format->BytesPerPixel;
+ } else {
+ // FIXME: Too much RAM due to the width (lots of unused space),
+ // but simpler to handle for now...
+ // (Furthermore this is currently not useful without VSync)
+ sdl.threading.pitch = sdl.surface->w*sdl.surface->format->BytesPerPixel;
+ }
+ }
break;
#if (HAVE_DDRAW_H) && defined(WIN32)
case SCREEN_SURFACE_DDRAW:
if (flags & GFX_CAN_15) bpp=15;
if (flags & GFX_CAN_16) bpp=16;
if (flags & GFX_CAN_32) bpp=32;
- if (!GFX_SetupSurfaceScaled((sdl.desktop.doublebuf && sdl.desktop.fullscreen) ? SDL_DOUBLEBUF : 0,bpp)) goto dosurface;
+ if (!GFX_SetupSurfaceScaled((sdl.desktop.vsync && sdl.desktop.fullscreen) ? SDL_DOUBLEBUF : 0,bpp)) goto dosurface;
sdl.blit.rect.top=sdl.clip.y;
sdl.blit.rect.left=sdl.clip.x;
sdl.blit.rect.right=sdl.clip.x+sdl.clip.w;
@@ -586,6 +616,9 @@
break;
}
sdl.desktop.type=SCREEN_SURFACE_DDRAW;
+ if (sdl.threading.isEnabled) {
+ sdl.threading.pitch = width*sdl.surface->format->BytesPerPixel;
+ }
break;
#endif
case SCREEN_OVERLAY:
@@ -602,6 +635,9 @@
}
sdl.desktop.type=SCREEN_OVERLAY;
retFlags = GFX_CAN_32 | GFX_SCALING | GFX_HARDWARE;
+ if (sdl.threading.isEnabled) {
+ sdl.threading.pitch = width*4;
+ }
break;
#if C_OPENGL
case SCREEN_OPENGL:
@@ -620,8 +656,8 @@
goto dosurface;
}
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
-#if defined (WIN32) && SDL_VERSION_ATLEAST(1, 2, 11)
- SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 0 );
+#if SDL_VERSION_ATLEAST(1, 2, 11)
+ SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, sdl.desktop.vsync ? 1 : 0 );
#endif
GFX_SetupSurfaceScaled(SDL_OPENGL,0);
if (!sdl.surface || sdl.surface->format->BitsPerPixel<15) {
@@ -690,6 +726,9 @@
retFlags = GFX_CAN_32 | GFX_SCALING;
if (sdl.opengl.pixel_buffer_object)
retFlags |= GFX_HARDWARE;
+ if (sdl.threading.isEnabled) {
+ sdl.threading.pitch = sdl.opengl.pitch;
+ }
break;
}//OPENGL
#endif //C_OPENGL
@@ -697,8 +736,19 @@
goto dosurface;
break;
}//CASE
- if (retFlags)
+ if (retFlags) {
+ if (sdl.threading.isEnabled) {
+ // sdl.threading.pitch is now set!
+ sdl.threading.sharedBufferSize = height*sdl.threading.pitch;
+ free(sdl.threading.sharedBuffer); // May be 0
+ sdl.threading.sharedBuffer = (Bit8u *)malloc(2*sdl.threading.sharedBufferSize);
+ if (sdl.threading.sharedBuffer == NULL)
+ E_Exit("Could not create shared graphics buffer");
+ sdl.threading.pixels = sdl.threading.sharedBuffer + sdl.threading.sharedBufferSize;
+ sdl.threading.isSharedBufferUpdated = false;
+ }
GFX_Start();
+ }
if (!sdl.mouse.autoenable) SDL_ShowCursor(sdl.mouse.autolock?SDL_DISABLE:SDL_ENABLE);
return retFlags;
}
@@ -706,25 +756,25 @@
void GFX_CaptureMouse(void) {
sdl.mouse.locked=!sdl.mouse.locked;
if (sdl.mouse.locked) {
- SDL_WM_GrabInput(SDL_GRAB_ON);
- SDL_ShowCursor(SDL_DISABLE);
+ threading.func.async.SDL_WM_GrabInput_ptr(SDL_GRAB_ON);
+ threading.func.async.SDL_ShowCursor_ptr(SDL_DISABLE);
} else {
- SDL_WM_GrabInput(SDL_GRAB_OFF);
- if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE);
+ threading.func.async.SDL_WM_GrabInput_ptr(SDL_GRAB_OFF);
+ if (sdl.mouse.autoenable || !sdl.mouse.autolock) threading.func.async.SDL_ShowCursor_ptr(SDL_ENABLE);
}
mouselocked=sdl.mouse.locked;
}
void GFX_UpdateSDLCaptureState(void) {
if (sdl.mouse.locked) {
- SDL_WM_GrabInput(SDL_GRAB_ON);
- SDL_ShowCursor(SDL_DISABLE);
+ threading.func.async.SDL_WM_GrabInput_ptr(SDL_GRAB_ON);
+ threading.func.async.SDL_ShowCursor_ptr(SDL_DISABLE);
} else {
- SDL_WM_GrabInput(SDL_GRAB_OFF);
- if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE);
+ threading.func.async.SDL_WM_GrabInput_ptr(SDL_GRAB_OFF);
+ if (sdl.mouse.autoenable || !sdl.mouse.autolock) threading.func.async.SDL_ShowCursor_ptr(SDL_ENABLE);
}
CPU_Reset_AutoAdjust();
- GFX_SetTitle(-1,-1,false);
+ threading.func.sync.voidFun.GFX_SetTitle_ptr(-1,-1,false);
}
bool mouselocked; //Global variable for mapper
@@ -799,14 +849,28 @@
}
void GFX_RestoreMode(void) {
- GFX_SetSize(sdl.draw.width,sdl.draw.height,sdl.draw.flags,sdl.draw.scalex,sdl.draw.scaley,sdl.draw.callback);
+ threading.func.sync.nonVoidFun.GFX_SetSize_ptr(sdl.draw.width,sdl.draw.height,sdl.draw.flags,sdl.draw.scalex,sdl.draw.scaley,sdl.draw.callback);
GFX_UpdateSDLCaptureState();
}
+bool GFX_PrepareOutput(Bit8u * & pixels,Bitu & pitch);
+
bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) {
if (!sdl.active || sdl.updating)
return false;
+ if (sdl.threading.isEnabled) {
+ pixels=sdl.threading.pixels;
+ pitch=sdl.threading.pitch;
+ sdl.updating=true;
+ } else {
+ sdl.updating=GFX_PrepareOutput(pixels, pitch);
+ }
+ return sdl.updating;
+}
+
+
+bool GFX_PrepareOutput(Bit8u * & pixels,Bitu & pitch) {
switch (sdl.desktop.type) {
case SCREEN_SURFACE:
if (sdl.blit.surface) {
@@ -822,7 +886,7 @@
pixels+=sdl.clip.x*sdl.surface->format->BytesPerPixel;
pitch=sdl.surface->pitch;
}
- sdl.updating=true;
+ //sdl.updating=true;
return true;
#if (HAVE_DDRAW_H) && defined(WIN32)
case SCREEN_SURFACE_DDRAW:
@@ -832,14 +896,14 @@
}
pixels=(Bit8u *)sdl.blit.surface->pixels;
pitch=sdl.blit.surface->pitch;
- sdl.updating=true;
+ //sdl.updating=true;
return true;
#endif
case SCREEN_OVERLAY:
if (SDL_LockYUVOverlay(sdl.overlay)) return false;
pixels=(Bit8u *)*(sdl.overlay->pixels);
pitch=*(sdl.overlay->pitches);
- sdl.updating=true;
+ //sdl.updating=true;
return true;
#if C_OPENGL
case SCREEN_OPENGL:
@@ -849,7 +913,7 @@
} else
pixels=(Bit8u *)sdl.opengl.framebuf;
pitch=sdl.opengl.pitch;
- sdl.updating=true;
+ //sdl.updating=true;
return true;
#endif
default:
@@ -859,13 +923,30 @@
}
+void GFX_DoOutput( const Bit16u *changedLines );
+
void GFX_EndUpdate( const Bit16u *changedLines ) {
+ if (!sdl.updating)
+ return;
+ sdl.updating=false;
+ if (sdl.threading.isEnabled) {
+ // There ARE chances we reach this stage
+ // from the main thread (e.g. on shutdown)
+ if (threading.mainThreadID == SDL_ThreadID())
+ return;
+ THREADED_MutexLocker locker(sdl.threading.sharedBufferMutex);
+ memcpy(sdl.threading.sharedBuffer, sdl.threading.pixels, sdl.threading.sharedBufferSize);
+ sdl.threading.isSharedBufferUpdated = true;
+ } else {
+ GFX_DoOutput(changedLines);
+ }
+}
+
+
+void GFX_DoOutput( const Bit16u *changedLines ) {
#if (HAVE_DDRAW_H) && defined(WIN32)
int ret;
#endif
- if (!sdl.updating)
- return;
- sdl.updating=false;
switch (sdl.desktop.type) {
case SCREEN_SURFACE:
if (SDL_MUSTLOCK(sdl.surface)) {
@@ -877,6 +958,8 @@
SDL_UnlockSurface(sdl.surface);
}
SDL_Flip(sdl.surface);
+ } else if (sdl.threading.isEnabled) {
+ SDL_Flip(sdl.surface);
} else if (changedLines) {
Bitu y = 0, index = 0, rectCount = 0;
while (y < sdl.draw.height) {
@@ -936,6 +1019,13 @@
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
glCallList(sdl.opengl.displaylist);
SDL_GL_SwapBuffers();
+ } else if (sdl.threading.isEnabled) {
+ glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
+ sdl.draw.width, sdl.draw.height, GL_BGRA_EXT,
+ GL_UNSIGNED_INT_8_8_8_8_REV, sdl.opengl.framebuf);
+ glCallList(sdl.opengl.displaylist);
+ SDL_GL_SwapBuffers();
} else if (changedLines) {
Bitu y = 0, index = 0;
glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture);
@@ -966,11 +1056,11 @@
void GFX_SetPalette(Bitu start,Bitu count,GFX_PalEntry * entries) {
/* I should probably not change the GFX_PalEntry :) */
if (sdl.surface->flags & SDL_HWPALETTE) {
- if (!SDL_SetPalette(sdl.surface,SDL_PHYSPAL,(SDL_Color *)entries,start,count)) {
+ if (!threading.func.sync.nonVoidFun.SDL_SetPalette_ptr(sdl.surface,SDL_PHYSPAL,(SDL_Color *)entries,start,count)) {
E_Exit("SDL:Can't set palette");
}
} else {
- if (!SDL_SetPalette(sdl.surface,SDL_LOGPAL,(SDL_Color *)entries,start,count)) {
+ if (!threading.func.sync.nonVoidFun.SDL_SetPalette_ptr(sdl.surface,SDL_LOGPAL,(SDL_Color *)entries,start,count)) {
E_Exit("SDL:Can't set palette");
}
}
@@ -1172,7 +1262,13 @@
}
}
}
- sdl.desktop.doublebuf=section->Get_bool("fulldouble");
+ sdl.desktop.vsync=section->Get_bool("vsync");
+ const char *threadingsetting=section->Get_string("threading");
+ if (!strcmp(threadingsetting, "auto")) {
+ sdl.threading.isEnabled = sdl.desktop.vsync;
+ } else {
+ sdl.threading.isEnabled = (!strcmp(threadingsetting, "on"));
+ }
#if SDL_VERSION_ATLEAST(1, 2, 10)
if (!sdl.desktop.full.width || !sdl.desktop.full.height){
//Can only be done on the very first call! Not restartable.
@@ -1364,7 +1460,7 @@
sdl.mouse.autolock=enable;
if (sdl.mouse.autoenable) sdl.mouse.requestlock=enable;
else {
- SDL_ShowCursor(enable?SDL_DISABLE:SDL_ENABLE);
+ threading.func.async.SDL_ShowCursor_ptr(enable?SDL_DISABLE:SDL_ENABLE);
sdl.mouse.requestlock=false;
}
}
@@ -1439,7 +1535,13 @@
MAPPER_UpdateJoysticks();
}
#endif
- while (SDL_PollEvent(&event)) {
+ if (sdl.threading.isEnabled) {
+ THREADED_MutexLocker locker(threading.quit_mutex);
+ if (threading.quit) {
+ throw 0;
+ }
+ }
+ while (threading.func.special.SDL_PollEvent_ptr(&event)) {
switch (event.type) {
case SDL_ACTIVEEVENT:
if (event.active.state & SDL_APPINPUTFOCUS) {
@@ -1477,16 +1579,14 @@
bool paused = true;
SDL_Event ev;
- GFX_SetTitle(-1,-1,true);
+ threading.func.sync.voidFun.GFX_SetTitle_ptr(-1,-1,true);
KEYBOARD_ClrBuffer();
// SDL_Delay(500);
-// while (SDL_PollEvent(&ev)) {
- // flush event queue.
-// }
+// threading.func.special.Flush_SDL_Event_Queue_ptr();
while (paused) {
// WaitEvent waits for an event rather than polling, so CPU usage drops to zero
- SDL_WaitEvent(&ev);
+ threading.func.special.SDL_WaitEvent_ptr(&ev);
switch (ev.type) {
case SDL_QUIT: throw(0); break; // a bit redundant at linux at least as the active events gets before the quit event.
@@ -1495,7 +1595,7 @@
// We've got focus back, so unpause and break out of the loop
if (ev.active.gain) {
paused = false;
- GFX_SetTitle(-1,-1,false);
+ threading.func.sync.voidFun.GFX_SetTitle_ptr(-1,-1,false);
}
/* Now poke a "release ALT" command into the keyboard buffer
@@ -1594,9 +1694,19 @@
Pbool = sdl_sec->Add_bool("fullscreen",Property::Changeable::Always,false);
Pbool->Set_help("Start dosbox directly in fullscreen. (Press ALT-Enter to go back)");
- Pbool = sdl_sec->Add_bool("fulldouble",Property::Changeable::Always,false);
- Pbool->Set_help("Use double buffering in fullscreen. It can reduce screen flickering, but it can also result in a slow DOSBox.");
+ Pbool = sdl_sec->Add_bool("vsync",Property::Changeable::Always,false);
+ Pbool->Set_help("Sync to VBlank IF supported by the output device and current setup.\n"
+ "It can reduce screen flickering, but it can also result in a slow DOSBox,\n"
+ "especially with threading=off.");
+ const char *threadingToggles[] = { "off", "auto", "on", 0 };
+
+ Pstring = sdl_sec->Add_string("threading",Property::Changeable::Always,"auto");
+ Pstring->Set_help("Update display contents in a dedicated thread. Recommended ONLY when\n"
+ "vertical synchronization is in effect, e.g. with vsync=true on a supported setup.\n"
+ "It may be less stable, so use threading=off in case of problems.");
+ Pstring->Set_values(threadingToggles);
+
Pstring = sdl_sec->Add_string("fullresolution",Property::Changeable::Always,"original");
Pstring->Set_help("What resolution to use for fullscreen: original, desktop or a fixed size (e.g. 1024x768).\n"
" Using your monitor's native resolution with aspect=true might give the best results.\n"
@@ -1717,6 +1827,8 @@
extern void DEBUG_ShutDown(Section * /*sec*/);
#endif
+static void mainThreadLoopEnd();
+
void restart_program(std::vector & parameters) {
char** newargs = new char* [parameters.size()+1];
// parameter 0 is the executable path
@@ -1736,6 +1848,11 @@
free(newargs);
}
void Restart(bool pressed) { // mapper handler
+ threading.func.special.GFX_Restart_ptr();
+}
+
+/*** Note: This always runs in the main thread ***/
+void GFX_Restart() {
restart_program(control->startup_params);
}
@@ -1818,6 +1935,232 @@
}
+static void mainThreadLoopEnd() {
+ THREADED_Clear();
+ SDL_DestroyMutex(sdl.threading.sharedBufferMutex);
+}
+
+void mainThreadHandleAsyncMethods() {
+ bool query;
+ THREADED_Void_SDL_WM_GrabInput SDL_WM_GrabInput_DelayedCall;
+ THREADED_Void_SDL_ShowCursor SDL_ShowCursor_DelayedCall;
+
+ // Asynchronous SDL_WM_GrabInput calling
+ {
+ THREADED_MutexLocker locker(threading.SDL_WM_GrabInput_call_mutex);
+ query = threading.SDL_WM_GrabInput_call_is_ready;
+ if (query) {
+ threading.SDL_WM_GrabInput_call_is_ready = false;
+ SDL_WM_GrabInput_DelayedCall = threading.SDL_WM_GrabInput_call;
+ }
+ } // Mutex is unlocked now!
+ if (query) {
+ SDL_WM_GrabInput_DelayedCall.run();
+ }
+
+ // Asynchronous SDL_ShowCursor calling
+ {
+ THREADED_MutexLocker locker(threading.SDL_ShowCursor_call_mutex);
+ query = threading.SDL_ShowCursor_call_is_ready;
+ if (query) {
+ threading.SDL_ShowCursor_call_is_ready = false;
+ SDL_ShowCursor_DelayedCall = threading.SDL_ShowCursor_call;
+ }
+ } // Mutex is unlocked now!
+ if (query) {
+ SDL_ShowCursor_DelayedCall.run();
+ }
+}
+
+static void mainThreadLoop() {
+ Bit8u *pixels;
+ Bitu pitch;
+ SDL_Event event;
+ bool query;
+
+ sdl.threading.exceptionType = SDL_Block::Threading_Block::THREAD_EXCEPTION_NONE;
+ sdl.threading.sharedBuffer = 0;
+ sdl.threading.isSharedBufferUpdated = false;
+ sdl.threading.sharedBufferMutex = SDL_CreateMutex();
+
+ /********* WARNING WARNING WARNING WARNING WARNING WARNING *********/
+ /* We *must* catch exceptions coming from the main thread here,
+ * not just in the main function. That's because some things
+ * are locally allocated on the STACK from within the main function!
+ */
+ try {
+ THREADED_Start();
+ do {
+ { // Is the secondary thread done? Maybe a restart is desired?
+ THREADED_MutexLocker locker(threading.quit_mutex);
+ // We don't yet check for a restart: When it is
+ // actually done, the mutex should be UNLOCKED.
+ query = threading.restart;
+ if (threading.quit) {
+ break;
+ }
+ } // Mutex is unlocked now!
+
+ if (query) {
+ // At this stage the secondary thread's
+ // execution should be done, or at least it's
+ // going to be.
+ mainThreadLoopEnd();
+ // Do restart!
+ GFX_Restart();
+ }
+
+ /* NOTE: Before handling ASYNC function calls, we first
+ * lock the mutex for the SYNC ones. Why? Well:
+ * - We want to ensure that an earlier ASYNC call is
+ * processed before any later SYNC call is.
+ * - IF the secondary thread tries to lock
+ * sync_func_execution_mutex, it is going to wait for
+ * the main thread anyway.
+ * - If it has already locked the mutex for such a
+ * call, it is also going to temporarily release the
+ * mutex for us, using a condition variable.
+ *
+ * An example for the former point:
+ * 1. An asynchronous call to SDL_ShowCursor is added
+ * by the secondary thread, but not yet processed.
+ * 2. Right afterwards the secondary thread wants to
+ * make a synchronous call to MAPPER_RunInternal.
+ * 3. MAPPER_RunInternal itself uses a simple
+ * (non-threaded) call to SDL_ShowCursor as a QUERY,
+ * checking if the cursor is shown or not.
+ * 4. Hence, the earlier (threaded, non-query) call to
+ * SDL_ShowCursor should be done first.
+ * 5. In practice, this specific example may be less
+ * relevant, but another one might always pop up.
+ */
+
+ {
+ THREADED_MutexLocker locker(threading.sync_func_execution_mutex);
+ // Asynchronous function calling
+ mainThreadHandleAsyncMethods();
+ // Synchronous function calling
+ if (threading.isVoidFunctionReady) {
+ threading.isVoidFunctionReady = false;
+ threading.voidFunction->run();
+ SDL_CondSignal(threading.sync_func_execution_cond_var);
+ } else if (threading.isNonVoidFunctionReady) {
+ threading.isNonVoidFunctionReady = false;
+ threading.retValPtr = threading.nonVoidFunction->run();
+ SDL_CondSignal(threading.sync_func_execution_cond_var);
+ }
+ } // Mutex is unlocked now!
+
+ while (SDL_PollEvent(&event)) {
+ THREADED_MutexLocker locker(threading.sdl_events_queue_mutex);
+ threading.sdl_events_queue.push(event);
+ }
+
+ // Now updating physical display, mimicking
+ // single-threaded GFX_StartUpdate behaviors first
+ {
+ THREADED_MutexLocker locker(sdl.threading.sharedBufferMutex);
+ /* If there is no new updated frame, do NOT
+ * draw. Otherwise stutters are a possibility,
+ * along with an added output latency.
+ * As a side effect, it also prevents the
+ * display of "garbage" contents on video mode
+ * changes.
+ */
+ query = sdl.threading.isSharedBufferUpdated;
+ if (query) {
+ sdl.threading.isSharedBufferUpdated = false;
+ }
+ } // Mutex is unlocked now!
+
+ if (!query) {
+ /* NOTE: With any sleep there is the possible
+ * danger of visual stutters. However:
+ * - With no sleep, the following supposedly
+ * strange phenomenon occurs: The LESS frames
+ * there are to actually update on screen,
+ * the MORE CPU cycles are wasted in a loop.
+ * - This is particularly noticeable when there
+ * is no update AT ALL.
+ * - This is reproduced while the DOSBox
+ * debugger is used, too.
+ *
+ * So, at least have at attempt with a 1ms
+ * sleep. It may actually take longer, but if
+ * it is never more than 10ms then this is less
+ * than the refresh time within the emulated
+ * machine, which should hopefully be OK.
+ */
+ SDL_Delay(1);
+ } else {
+ /* This mimicks single-threaded
+ * GFX_StartUpdate behaviors.
+ *
+ * WARNING: If some mutex were LOCKED at this
+ * stage, it could result in BOTH threads
+ * waiting for vertical sync! (Yes, here too!)
+ */
+ if (!GFX_PrepareOutput(pixels, pitch))
+ continue;
+ {
+ THREADED_MutexLocker locker(sdl.threading.sharedBufferMutex);
+ memcpy(pixels, sdl.threading.sharedBuffer, sdl.threading.sharedBufferSize);
+ } // Mutex is unlocked now!
+
+ // Finally mimicking single-threaded
+ // GFX_EndUpdate behaviors
+ GFX_DoOutput(NULL);
+ }
+ } while (true);
+ } catch (char *error) {
+ mainThreadLoopEnd();
+ throw error;
+ } catch (int num){
+ mainThreadLoopEnd();
+ throw num;
+ } catch(...) {
+ mainThreadLoopEnd();
+ throw;
+ }
+ mainThreadLoopEnd();
+ switch (sdl.threading.exceptionType) {
+ case SDL_Block::Threading_Block::THREAD_EXCEPTION_STRING: throw sdl.threading.secondaryThreadException.str.c_str();
+ case SDL_Block::Threading_Block::THREAD_EXCEPTION_INT : throw sdl.threading.secondaryThreadException.num;
+ case SDL_Block::Threading_Block::THREAD_EXCEPTION_OTHER : throw;
+ default: ;
+ }
+}
+
+
+int GFX_secondaryThreadStartPoint(void *unused) {
+ // NOTE: There is no need to lock access to exception data, since the
+ // only time the main thread can check this is after the secondary
+ // thread shut downs.
+ try {
+ /* Start up main machine */
+ control->StartUp();
+ } catch (char * error) {
+ // The contents of the string may be erased, so use a container
+ sdl.threading.secondaryThreadException.str = std::string(error);
+ sdl.threading.exceptionType = SDL_Block::Threading_Block::THREAD_EXCEPTION_STRING;
+ } catch (int num){
+ sdl.threading.secondaryThreadException.num = num;
+ sdl.threading.exceptionType = SDL_Block::Threading_Block::THREAD_EXCEPTION_INT;
+ } catch(...){
+ sdl.threading.exceptionType = SDL_Block::Threading_Block::THREAD_EXCEPTION_OTHER;
+ }
+
+ // threading.restart is never changed by the
+ // main thread, so no locking is done, yet.
+ if (!threading.restart) {
+ // Signal quit to other thread
+ THREADED_MutexLocker locker(threading.quit_mutex);
+ threading.quit = true;
+ }
+ return 0;
+}
+
+
//extern void UI_Init(void);
int main(int argc, char* argv[]) {
try {
@@ -2021,8 +2364,16 @@
/* Init the keyMapper */
MAPPER_Init();
if (control->cmdline->FindExist("-startmapper")) MAPPER_RunInternal();
- /* Start up main machine */
- control->StartUp();
+ if (sdl.threading.isEnabled) {
+ /* Prepare the secondary thread, which is going to
+ * start up main machine. The main thread is
+ * responsible for display updates and some more.
+ */
+ mainThreadLoop();
+ } else {
+ /* Start up main machine */
+ control->StartUp();
+ }
/* Shutdown everything */
} catch (char * error) {
#if defined (WIN32)
Index: src/gui/Makefile.am
===================================================================
--- src/gui/Makefile.am (revision 3827)
+++ src/gui/Makefile.am (working copy)
@@ -6,6 +6,7 @@
render_templates.h render_loops.h render_simple.h \
render_templates_sai.h render_templates_hq.h \
render_templates_hq2x.h render_templates_hq3x.h \
+ threading.cpp threading.h \
midi.cpp midi_win32.h midi_oss.h midi_coreaudio.h midi_alsa.h \
midi_coremidi.h sdl_gui.cpp dosbox_splash.h
Index: src/gui/threading.cpp
===================================================================
--- src/gui/threading.cpp (revision 0)
+++ src/gui/threading.cpp (revision 0)
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2002-2013 The DOSBox Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "threading.h"
+
+const THREADED_GFX_SetTitle_T& GFX_SetTitle_ptr = threading.func.sync.voidFun.GFX_SetTitle_ptr;
+struct THREADING_BLOCK threading;
+
+extern void GFX_SetTitle(Bit32s cycles, Bits frameskip, bool paused);
+extern void GFX_Restart();
+extern void MAPPER_RunInternal();
+
+// Obtains a pointer to an SDL_mutex and locks it
+THREADED_MutexLocker::THREADED_MutexLocker(SDL_mutex *mutex) {
+ this->mutex = mutex;
+ SDL_mutexP(mutex);
+}
+
+// Unlocks the mutex before clearing locker's resources (not the mutex')
+THREADED_MutexLocker::~THREADED_MutexLocker() {
+ SDL_mutexV(mutex);
+}
+
+/******************************************************
+Implementations of functions handled in an asynchronous
+manner, with classes declared in the header.
+******************************************************/
+THREADED_Void_SDL_WM_GrabInput::THREADED_Void_SDL_WM_GrabInput() {} // Exists for technical reasons
+THREADED_Void_SDL_WM_GrabInput::THREADED_Void_SDL_WM_GrabInput(SDL_GrabMode mode): mode(mode) {}
+void THREADED_Void_SDL_WM_GrabInput::run() {
+ SDL_WM_GrabInput(mode);
+}
+
+THREADED_Void_SDL_ShowCursor::THREADED_Void_SDL_ShowCursor() {} // Exists for technical reasons
+THREADED_Void_SDL_ShowCursor::THREADED_Void_SDL_ShowCursor(int toggle): toggle(toggle) {}
+void THREADED_Void_SDL_ShowCursor::run() {
+ SDL_ShowCursor(toggle);
+}
+
+/**********************************************
+Internal class declarations and implementations
+**********************************************/
+
+class THREADED_NonVoid_SDL_SetPalette : public THREADED_NonVoid_Function {
+public:
+ THREADED_NonVoid_SDL_SetPalette(SDL_Surface *surface, int flags, SDL_Color *colors, int firstcolor, int ncolors):
+ surface(surface), flags(flags), colors(colors), firstcolor(firstcolor), ncolors(ncolors) {}
+ void * run() {
+ retVal = SDL_SetPalette(surface, flags, colors, firstcolor, ncolors);
+ return &retVal;
+ }
+ SDL_Surface *surface;
+ int flags;
+ SDL_Color *colors;
+ int firstcolor;
+ int ncolors;
+
+ int retVal;
+};
+
+class THREADED_Void_MAPPER_RunInternal : public THREADED_Void_Function {
+// THREADED_Void_MAPPER_RunInternal(void) {}
+ void run () {
+ MAPPER_RunInternal();
+ }
+};
+
+class THREADED_NonVoid_SDL_VideoModeOK : public THREADED_NonVoid_Function {
+public:
+ THREADED_NonVoid_SDL_VideoModeOK(int width, int height, int bpp, Uint32 flags):
+ width(width), height(height), bpp(bpp), flags(flags) {}
+ void * run() {
+ retVal = SDL_VideoModeOK(width, height, bpp, flags);
+ return &retVal;
+ }
+ int width, height, bpp;
+ Uint32 flags;
+
+ int retVal;
+};
+
+class THREADED_NonVoid_SDL_ShowCursor : public THREADED_NonVoid_Function {
+public:
+ THREADED_NonVoid_SDL_ShowCursor(int toggle): toggle(toggle) {}
+ void * run() {
+ retVal = SDL_ShowCursor(toggle);
+ return &retVal;
+ }
+ int toggle;
+
+ int retVal;
+};
+
+class THREADED_NonVoid_GFX_SetSize : public THREADED_NonVoid_Function {
+public:
+ THREADED_NonVoid_GFX_SetSize(Bitu width, Bitu height, Bitu flags, double scalex, double scaley, GFX_CallBack_t callback):
+ width(width), height(height), flags(flags), scalex(scalex), scaley(scaley), callback(callback) {}
+ void * run() {
+ retVal = GFX_SetSize(width, height, flags, scalex, scaley, callback);
+ return &retVal;
+ }
+ Bitu width, height;
+ Bitu flags;
+ double scalex, scaley;
+ GFX_CallBack_t callback;
+
+ Bitu retVal;
+};
+
+class THREADED_Void_GFX_SetTitle : public THREADED_Void_Function {
+public:
+ //THREADED_Void_GFX_SetTitle() {}
+ THREADED_Void_GFX_SetTitle(Bit32s cycles, Bits frameskip, bool paused):
+ cycles(cycles), frameskip(frameskip), paused(paused) {}
+ void run() {
+ GFX_SetTitle(cycles, frameskip, paused);
+ }
+ Bit32s cycles;
+ Bits frameskip;
+ bool paused;
+};
+
+/****************************
+Classes for special functions
+****************************/
+
+class THREADED_Special_Void_Flush_SDL_Event_Queue : public THREADED_Void_Function {
+public:
+// THREADED_Special_Void_Flush_SDL_Event_Queue(void) {}
+ void run() {
+ Flush_SDL_Event_Queue();
+ // Clear our internal queue, so we don't leave
+ // old events to be processed, say, after a pause!!
+ THREADED_MutexLocker locker(threading.sdl_events_queue_mutex);
+ threading.sdl_events_queue = std::queue();
+ }
+};
+
+class THREADED_Special_Void_SDL_WaitEvent : public THREADED_Void_Function {
+public:
+ THREADED_Special_Void_SDL_WaitEvent(SDL_Event *event): event(event) {}
+ void run() {
+ bool isQueueEmpty;
+ {
+ THREADED_MutexLocker locker(threading.sdl_events_queue_mutex);
+ isQueueEmpty = threading.sdl_events_queue.empty();
+ if ((!isQueueEmpty) && event) {
+ *event = threading.sdl_events_queue.front();
+ threading.sdl_events_queue.pop();
+ }
+ }
+ if (isQueueEmpty) // Safer to do so AFTER unlocking the mutex
+ SDL_WaitEvent(event);
+ }
+ SDL_Event *event;
+};
+
+/******************************
+General call handling functions
+******************************/
+
+void THREADED_Sync_Void_HandleCall(THREADED_Void_Function *delayedCall) {
+ THREADED_MutexLocker locker(threading.sync_func_execution_mutex);
+ threading.voidFunction = delayedCall;
+ threading.isVoidFunctionReady = true;
+ // Mutex is locked now, soon to be get unlocked before waiting
+ SDL_CondWait(threading.sync_func_execution_cond_var, threading.sync_func_execution_mutex);
+ // It then gets lock again, and finally unlocked - on locker destruction
+}
+
+void * THREADED_Sync_NonVoid_HandleCall(THREADED_NonVoid_Function *delayedCall) {
+ THREADED_MutexLocker locker(threading.sync_func_execution_mutex);
+ threading.nonVoidFunction = delayedCall;
+ threading.isNonVoidFunctionReady = true;
+ // Mutex is locked now, soon to be get unlocked before waiting
+ SDL_CondWait(threading.sync_func_execution_cond_var, threading.sync_func_execution_mutex);
+ // It then gets lock again
+ return threading.retValPtr;
+ // Finally it gets unlocked as the locker gets destructed
+}
+
+/*******************************************************************
+More specific functions for handling calls (usually) using the above
+*******************************************************************/
+
+int THREADED_Sync_NonVoid_SDL_SetPalette(SDL_Surface *surface, int flags, SDL_Color *colors, int firstcolor, int ncolors) {
+ THREADED_NonVoid_SDL_SetPalette delayedCall(surface, flags, colors, firstcolor, ncolors);
+ return *(int *)THREADED_Sync_NonVoid_HandleCall(&delayedCall);
+}
+
+void THREADED_Sync_Void_MAPPER_RunInternal() {
+ THREADED_Void_MAPPER_RunInternal delayedCall;
+ THREADED_Sync_Void_HandleCall(&delayedCall);
+}
+
+int THREADED_Sync_NonVoid_SDL_VideoModeOK(int width, int height, int bpp, Uint32 flags) {
+ THREADED_NonVoid_SDL_VideoModeOK delayedCall(width, height, bpp, flags);
+ return *(int *)THREADED_Sync_NonVoid_HandleCall(&delayedCall);
+}
+
+int THREADED_Sync_NonVoid_SDL_ShowCursor(int toggle) {
+ THREADED_NonVoid_SDL_ShowCursor delayedCall(toggle);
+ return *(int *)THREADED_Sync_NonVoid_HandleCall(&delayedCall);
+}
+
+Bitu THREADED_Sync_NonVoid_GFX_SetSize(Bitu width, Bitu height, Bitu flags, double scalex, double scaley, GFX_CallBack_t callback) {
+ THREADED_NonVoid_GFX_SetSize delayedCall(width, height, flags, scalex, scaley, callback);
+ return *(Bitu *)THREADED_Sync_NonVoid_HandleCall(&delayedCall);
+}
+
+void THREADED_Sync_Void_GFX_SetTitle(Bit32s cycles, Bits frameskip, bool paused) {
+ THREADED_Void_GFX_SetTitle delayedCall(cycles, frameskip, paused);
+ THREADED_Sync_Void_HandleCall(&delayedCall);
+}
+
+/***********************************************************************
+We don't have much async functions, so deal with each of them separately
+***********************************************************************/
+
+SDL_GrabMode THREADED_Async_SDL_WM_GrabInput(SDL_GrabMode mode){
+ THREADED_MutexLocker locker(threading.SDL_WM_GrabInput_call_mutex);
+ threading.SDL_WM_GrabInput_call = THREADED_Void_SDL_WM_GrabInput(mode);
+ threading.SDL_WM_GrabInput_call_is_ready = true;
+ // We need to return some value to be type compatible, so be unique...
+ return SDL_GRAB_QUERY;
+}
+
+int THREADED_Async_SDL_ShowCursor(int toggle){
+ THREADED_MutexLocker locker(threading.SDL_ShowCursor_call_mutex);
+ threading.SDL_ShowCursor_call = THREADED_Void_SDL_ShowCursor(toggle);
+ threading.SDL_ShowCursor_call_is_ready = true;
+ // We need to return some value to be type compatible, so be unique...
+ return 2;
+}
+
+/****************
+Special functions
+****************/
+
+int THREADED_Special_SDL_WaitEvent(SDL_Event *event) {
+ THREADED_Special_Void_SDL_WaitEvent delayedCall(event);
+ THREADED_Sync_Void_HandleCall(&delayedCall);
+ // We need to return some value to be type compatible
+ return 0;
+}
+
+int THREADED_Special_SDL_PollEvent(SDL_Event *event) {
+ THREADED_MutexLocker locker(threading.sdl_events_queue_mutex);
+ if (threading.sdl_events_queue.empty())
+ return 0;
+ if (event) {
+ *event = threading.sdl_events_queue.front();
+ threading.sdl_events_queue.pop();
+ }
+ return 1;
+}
+
+void THREADED_Special_GFX_Restart() {
+ // Signal to other thread - using an existing mutex for saving resources...
+ THREADED_MutexLocker locker(threading.quit_mutex);
+ threading.restart = true;
+ // Eventually the main thread catches this, clears secondary thread
+ // resources (and possibly more) and then restarts.
+ throw 0;
+}
+
+void THREADED_Special_Flush_SDL_Event_Queue() {
+ THREADED_Special_Void_Flush_SDL_Event_Queue delayedCall;
+ THREADED_Sync_Void_HandleCall(&delayedCall);
+}
+
+void Flush_SDL_Event_Queue() {
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ // flush event queue.
+ }
+}
+
+/***********************************************************************
+End of functions lists (as threaded replacements and classes altogether)
+***********************************************************************/
+
+int GFX_secondaryThreadStartPoint(void *unused);
+
+// Prepares function pointers and more resources, and starts secondary thread.
+void THREADED_Start() {
+ threading.SDL_WM_GrabInput_call_is_ready = false;
+ threading.SDL_ShowCursor_call_is_ready = false;
+ threading.SDL_WM_GrabInput_call_mutex = SDL_CreateMutex();
+ threading.SDL_ShowCursor_call_mutex = SDL_CreateMutex();
+
+ threading.sync_func_execution_mutex = SDL_CreateMutex();
+ threading.sync_func_execution_cond_var = SDL_CreateCond();
+ threading.sdl_events_queue_mutex = SDL_CreateMutex();
+
+ threading.quit = false;
+ threading.restart = false;
+ threading.quit_mutex = SDL_CreateMutex();
+
+ threading.func.sync.voidFun.MAPPER_RunInternal_ptr = THREADED_Sync_Void_MAPPER_RunInternal;
+ threading.func.sync.voidFun.GFX_SetTitle_ptr = THREADED_Sync_Void_GFX_SetTitle;
+
+ threading.func.sync.nonVoidFun.SDL_SetPalette_ptr = THREADED_Sync_NonVoid_SDL_SetPalette;
+ threading.func.sync.nonVoidFun.SDL_VideoModeOK_ptr = THREADED_Sync_NonVoid_SDL_VideoModeOK;
+ threading.func.sync.nonVoidFun.SDL_ShowCursor_ptr = THREADED_Sync_NonVoid_SDL_ShowCursor;
+
+ threading.func.sync.nonVoidFun.GFX_SetSize_ptr = THREADED_Sync_NonVoid_GFX_SetSize;
+
+ threading.func.async.SDL_WM_GrabInput_ptr = THREADED_Async_SDL_WM_GrabInput;
+ threading.func.async.SDL_ShowCursor_ptr = THREADED_Async_SDL_ShowCursor;
+
+ threading.func.special.SDL_WaitEvent_ptr = THREADED_Special_SDL_WaitEvent;
+ threading.func.special.SDL_PollEvent_ptr = THREADED_Special_SDL_PollEvent;
+
+ threading.func.special.GFX_Restart_ptr = THREADED_Special_GFX_Restart;
+ threading.func.special.Flush_SDL_Event_Queue_ptr = THREADED_Special_Flush_SDL_Event_Queue;
+
+ threading.mainThreadID = SDL_ThreadID();
+ // BETTER BE THE LAST STEP TO APPLY!
+ threading.secondaryThread = SDL_CreateThread(GFX_secondaryThreadStartPoint, 0);
+ if (!threading.secondaryThread) {
+ threading.resetFunctionPointers(); // DON'T FORGET THIS!
+ throw "FATAL ERROR: Couldn't create secondary thread.\n";
+ }
+
+}
+
+void THREADING_BLOCK::resetFunctionPointers() {
+ func.sync.voidFun.MAPPER_RunInternal_ptr = MAPPER_RunInternal;
+ func.sync.voidFun.GFX_SetTitle_ptr = GFX_SetTitle;
+
+ func.sync.nonVoidFun.SDL_SetPalette_ptr = SDL_SetPalette;
+ func.sync.nonVoidFun.SDL_VideoModeOK_ptr = SDL_VideoModeOK;
+ func.sync.nonVoidFun.SDL_ShowCursor_ptr = SDL_ShowCursor;
+
+ func.sync.nonVoidFun.GFX_SetSize_ptr = GFX_SetSize;
+
+ func.async.SDL_WM_GrabInput_ptr = SDL_WM_GrabInput;
+ func.async.SDL_ShowCursor_ptr = SDL_ShowCursor;
+
+ func.special.SDL_WaitEvent_ptr = SDL_WaitEvent;
+ func.special.SDL_PollEvent_ptr = SDL_PollEvent;
+
+ func.special.GFX_Restart_ptr = GFX_Restart;
+ func.special.Flush_SDL_Event_Queue_ptr = Flush_SDL_Event_Queue;
+}
+
+// Shut downs secondary thread (if any is running) and clears resources
+void THREADED_Clear() {
+ if (!threading.secondaryThread)
+ return;
+ { // Signal to other thread
+ THREADED_MutexLocker locker(threading.quit_mutex);
+ threading.quit = true;
+ }
+ SDL_WaitThread(threading.secondaryThread, 0);
+ threading.secondaryThread = 0; // Just to be safer...
+
+ // Some things may still process even on shutdown!
+ threading.resetFunctionPointers();
+
+ // Do NOT do this, though - a call to GFX_EndUpdate may depend on it.
+ // Luckily it doesn't NEED to be false for something else...
+// sdl.threading.isEnabled = false;
+
+ SDL_DestroyMutex(threading.SDL_WM_GrabInput_call_mutex);
+ SDL_DestroyMutex(threading.SDL_ShowCursor_call_mutex);
+ SDL_DestroyMutex(threading.sync_func_execution_mutex);
+ SDL_DestroyCond(threading.sync_func_execution_cond_var);
+ SDL_DestroyMutex(threading.sdl_events_queue_mutex);
+
+ SDL_DestroyMutex(threading.quit_mutex);
+}
Index: src/gui/threading.h
===================================================================
--- src/gui/threading.h (revision 0)
+++ src/gui/threading.h (revision 0)
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2002-2013 The DOSBox Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __DOSBOX_THREADED_OUTPUT__
+#define __DOSBOX_THREADED_OUTPUT__
+
+#include
+
+#include "cross.h"
+#include "SDL.h"
+
+#include "dosbox.h"
+#include "video.h"
+
+/*************************************************************************
+A (very short introduction): There are basically four kinds of function
+calls that the secondary thread may pass to the main thread:
+- Synchronous void functions: These are functions like MAPPER_RunInternal,
+which don't return any value and the secondary thread is waiting for the
+main thread to let them complete.
+- Synchronous non-void functions: These functions return some values.
+As a side effect, the secondary thread *has* to wait for completion.
+- Asynchronous (void) functions that CAN BE OVERRIDDEN: Once the secondary
+thread schedules such a function for the main thread, it returns
+immediately and basically forgets about the function call.
+Furthermore, a later scheduled call of a specific function of that kind
+REPLACES any such call to the same function scheduled earlier.
+Example: SDL_ShowCursor (if not checking the returned value)
+- Special functions with unique handlings.
+*************************************************************************/
+
+typedef int (* THREADED_SDL_SetPalette_T) (SDL_Surface *surface, int flags, SDL_Color *colors, int firstcolor, int ncolors);
+
+// The only non-SDL function for the mapper
+typedef void (* THREADED_MAPPER_RunInternal_T) ();
+
+typedef int (* THREADED_SDL_PollEvent_T) (SDL_Event *event);
+typedef int (* THREADED_SDL_WaitEvent_T) (SDL_Event *event);
+
+typedef int (* THREADED_SDL_VideoModeOK_T) (int width, int height, int bpp, Uint32 flags);
+
+typedef SDL_GrabMode (* THREADED_SDL_WM_GrabInput_T) (SDL_GrabMode mode);
+typedef int (* THREADED_SDL_ShowCursor_T) (int toggle);
+
+// Non-SDL functions for sdlmain
+typedef Bitu (* THREADED_GFX_SetSize_T) (Bitu width, Bitu height, Bitu flags, double scalex, double scaley, GFX_CallBack_t callback);
+typedef void (* THREADED_GFX_SetTitle_T)(Bit32s cycles, Bits frameskip, bool paused);
+typedef void (* THREADED_GFX_Restart_T)();
+
+// A new special function for flushing the SDL event queue (used for pauses)
+typedef void (* THREADED_Flush_SDL_Event_Queue_T) ();
+void Flush_SDL_Event_Queue();
+
+
+class THREADED_Void_Function {
+public:
+ virtual void run() = 0;
+};
+
+class THREADED_NonVoid_Function {
+public:
+ virtual void * run() = 0;
+};
+
+
+/* A safe-to-use mutex locker.
+ * WARNINGS:
+ * 1. Currently, it does *not* check the return values of SDL calls!!
+ * 2. For simplicity it accepts a pointer, rather than a C++ style reference.
+ * However, it should NEVER be a null pointer!
+ */
+class THREADED_MutexLocker {
+public:
+ // Obtains a pointer to an SDL_mutex and locks it
+ THREADED_MutexLocker(SDL_mutex *mutex);
+ // Unlocks the mutex before clearing locker's resources (not the mutex')
+ ~THREADED_MutexLocker();
+private:
+ SDL_mutex *mutex;
+};
+
+
+// Asynchronous variation
+class THREADED_Void_SDL_WM_GrabInput : public THREADED_Void_Function {
+public:
+ THREADED_Void_SDL_WM_GrabInput(); // Exists for technical reasons
+ THREADED_Void_SDL_WM_GrabInput(SDL_GrabMode mode);
+ void run();
+
+ SDL_GrabMode mode;
+};
+
+// Asynchronous variation
+class THREADED_Void_SDL_ShowCursor : public THREADED_Void_Function {
+public:
+ THREADED_Void_SDL_ShowCursor(); // Exists for technical reasons
+ THREADED_Void_SDL_ShowCursor(int toggle);
+ void run();
+
+ int toggle;
+};
+
+// GFX_SetTitle is also required by supposedly-unrelated source files
+// (like cpu.cpp). So, better have a separate reference to the function pointer.
+
+extern const THREADED_GFX_SetTitle_T& GFX_SetTitle_ptr;
+
+struct THREADING_BLOCK {
+ SDL_Thread *secondaryThread;
+ Uint32 mainThreadID;
+
+ /* Sadly, for asynchronous calls, we cannot just allocate an instance
+ * of THREADED_Void_Function (well, a QUEUE of such) and use these.
+ * Luckily we do not have a lot of distinct async methods...
+ * (An alternative would be to dynamically allocate some memory,
+ * which would also be safer to do so in a container.)
+ */
+ THREADED_Void_SDL_WM_GrabInput SDL_WM_GrabInput_call;
+ bool SDL_WM_GrabInput_call_is_ready;
+ SDL_mutex *SDL_WM_GrabInput_call_mutex;
+ THREADED_Void_SDL_ShowCursor SDL_ShowCursor_call;
+ bool SDL_ShowCursor_call_is_ready;
+ SDL_mutex *SDL_ShowCursor_call_mutex;
+
+ // Signal to quit or restart
+ bool quit, restart;
+ SDL_mutex *quit_mutex;
+ /* General synchronized function pointers to temporarily store;
+ * Used only while the secondary thread is waiting for any of these
+ * to complete, so we can leave plain pointers.
+ */
+ THREADED_Void_Function *voidFunction;
+ THREADED_NonVoid_Function *nonVoidFunction;
+ bool isVoidFunctionReady, isNonVoidFunctionReady;
+ // Pointer to value returned by a non-void function (with sync)
+ void *retValPtr;
+ // Mutex to protect these, along with a condition variable
+ // for letting the secondary thread wait for the call to finish
+ SDL_mutex *sync_func_execution_mutex;
+ SDL_cond *sync_func_execution_cond_var;
+ // Queue of SDL events to pass to the secondary thread
+ std::queue sdl_events_queue;
+ SDL_mutex *sdl_events_queue_mutex;
+
+#ifdef C_DEBUG_NOTFORNOW
+ bool output_query;
+ SDL_mutex *output_query_mutex;
+#endif
+
+ THREADING_BLOCK(): secondaryThread(0) { resetFunctionPointers(); }
+ void resetFunctionPointers();
+
+ struct FUNCTIONS_BLOCK {
+ struct SYNC_FUNCTIONS_BLOCK {
+ // void functions that the secondary thread should
+ // wait before operations of them complete
+ struct SYNC_VOID_FUNCTIONS_BLOCK {
+ THREADED_MAPPER_RunInternal_T MAPPER_RunInternal_ptr;
+ THREADED_GFX_SetTitle_T GFX_SetTitle_ptr;
+ } voidFun;
+ // Same, but non-void
+ struct SYNC_NONVOID_FUNCTIONS_BLOCK {
+ THREADED_SDL_SetPalette_T SDL_SetPalette_ptr;
+ THREADED_SDL_VideoModeOK_T SDL_VideoModeOK_ptr;
+ THREADED_SDL_ShowCursor_T SDL_ShowCursor_ptr; // Non-void sync variant
+ THREADED_GFX_SetSize_T GFX_SetSize_ptr;
+ } nonVoidFun;
+ } sync;
+ struct ASYNC_FUNCTIONS_STRUCT {
+ // Functions that the secondary thread can push to the
+ // main thread and then finish before their operations
+ // complete
+ // (They may not be void, but they're used as such.)
+ THREADED_SDL_WM_GrabInput_T SDL_WM_GrabInput_ptr;
+ THREADED_SDL_ShowCursor_T SDL_ShowCursor_ptr;
+
+ } async;
+ struct SPECIAL_FUNCTIONS_STRUCT {
+ THREADED_SDL_WaitEvent_T SDL_WaitEvent_ptr;
+ THREADED_SDL_PollEvent_T SDL_PollEvent_ptr;
+ THREADED_GFX_Restart_T GFX_Restart_ptr;
+ THREADED_Flush_SDL_Event_Queue_T Flush_SDL_Event_Queue_ptr;
+ } special;
+ } func;
+};
+
+extern struct THREADING_BLOCK threading;
+
+
+// Prepares function pointers and more resources, and starts secondary thread.
+void THREADED_Start();
+
+// Shut downs secondary thread (if any is running) and clears resources
+void THREADED_Clear();
+
+#endif // __DOSBOX_THREADED_OUTPUT__
Index: src/gui/render.cpp
===================================================================
--- src/gui/render.cpp (revision 3827)
+++ src/gui/render.cpp (working copy)
@@ -30,6 +30,7 @@
#include "cross.h"
#include "hardware.h"
#include "support.h"
+#include "threading.h"
#include "render_scalers.h"
@@ -419,7 +420,7 @@
}
}
/* Setup the scaler variables */
- gfx_flags=GFX_SetSize(width,height,gfx_flags,gfx_scalew,gfx_scaleh,&RENDER_CallBack);
+ gfx_flags=threading.func.sync.nonVoidFun.GFX_SetSize_ptr(width,height,gfx_flags,gfx_scalew,gfx_scaleh,&RENDER_CallBack);
if (gfx_flags & GFX_CAN_8)
render.scale.outMode = scalerMode8;
else if (gfx_flags & GFX_CAN_15)
@@ -534,13 +535,13 @@
RENDER_Reset( );
}
-extern void GFX_SetTitle(Bit32s cycles, Bits frameskip,bool paused);
+//extern void GFX_SetTitle(Bit32s cycles, Bits frameskip,bool paused);
static void IncreaseFrameSkip(bool pressed) {
if (!pressed)
return;
if (render.frameskip.max<10) render.frameskip.max++;
LOG_MSG("Frame Skip at %d",render.frameskip.max);
- GFX_SetTitle(-1,render.frameskip.max,false);
+ threading.func.sync.voidFun.GFX_SetTitle_ptr(-1,render.frameskip.max,false);
}
static void DecreaseFrameSkip(bool pressed) {
@@ -548,7 +549,7 @@
return;
if (render.frameskip.max>0) render.frameskip.max--;
LOG_MSG("Frame Skip at %d",render.frameskip.max);
- GFX_SetTitle(-1,render.frameskip.max,false);
+ threading.func.sync.voidFun.GFX_SetTitle_ptr(-1,render.frameskip.max,false);
}
/* Disabled as I don't want to waste a keybind for that. Might be used in the future (Qbix)
static void ChangeScaler(bool pressed) {
@@ -628,6 +629,6 @@
MAPPER_AddHandler(DecreaseFrameSkip,MK_f7,MMOD1,"decfskip","Dec Fskip");
MAPPER_AddHandler(IncreaseFrameSkip,MK_f8,MMOD1,"incfskip","Inc Fskip");
- GFX_SetTitle(-1,render.frameskip.max,false);
+ threading.func.sync.voidFun.GFX_SetTitle_ptr(-1,render.frameskip.max,false);
}
Index: src/dos/dos_execute.cpp
===================================================================
--- src/dos/dos_execute.cpp (revision 3827)
+++ src/dos/dos_execute.cpp (working copy)
@@ -88,7 +88,10 @@
reg_sp+=18;
}
-extern void GFX_SetTitle(Bit32s cycles,Bits frameskip,bool paused);
+//extern void GFX_SetTitle(Bit32s cycles,Bits frameskip,bool paused);
+typedef void (* THREADED_GFX_SetTitle_T)(Bit32s cycles, Bits frameskip, bool paused);
+extern const THREADED_GFX_SetTitle_T& GFX_SetTitle_ptr;
+
void DOS_UpdatePSPName(void) {
DOS_MCB mcb(dos.psp()-1);
static char name[9];
@@ -100,7 +103,7 @@
if ( !isprint(*reinterpret_cast(&name[i])) ) name[i] = '?';
}
RunningProgram = name;
- GFX_SetTitle(-1,-1,false);
+ GFX_SetTitle_ptr(-1,-1,false);
}
void DOS_Terminate(Bit16u pspseg,bool tsr,Bit8u exitcode) {
@@ -144,9 +147,9 @@
CPU_CycleLeft=0;
CPU_Cycles=0;
CPU_CycleMax=CPU_OldCycleMax;
- GFX_SetTitle(CPU_OldCycleMax,-1,false);
+ GFX_SetTitle_ptr(CPU_OldCycleMax,-1,false);
} else {
- GFX_SetTitle(-1,-1,false);
+ GFX_SetTitle_ptr(-1,-1,false);
}
#if (C_DYNAMIC_X86) || (C_DYNREC)
if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CORE) {
Index: src/cpu/cpu.cpp
===================================================================
--- src/cpu/cpu.cpp (revision 3827)
+++ src/cpu/cpu.cpp (working copy)
@@ -32,7 +32,9 @@
#include "support.h"
Bitu DEBUG_EnableDebugger(void);
-extern void GFX_SetTitle(Bit32s cycles ,Bits frameskip,bool paused);
+typedef void (* THREADED_GFX_SetTitle_T)(Bit32s cycles, Bits frameskip, bool paused);
+//extern void GFX_SetTitle(Bit32s cycles ,Bits frameskip,bool paused);
+extern const THREADED_GFX_SetTitle_T& GFX_SetTitle_ptr;
#if 1
#undef LOG
@@ -1563,13 +1565,13 @@
CPU_CycleLeft=0;
CPU_Cycles=0;
CPU_OldCycleMax=CPU_CycleMax;
- GFX_SetTitle(CPU_CyclePercUsed,-1,false);
+ GFX_SetTitle_ptr(CPU_CyclePercUsed,-1,false);
if(!printed_cycles_auto_info) {
printed_cycles_auto_info = true;
LOG_MSG("DOSBox switched to max cycles, because of the setting: cycles=auto. If the game runs too fast try a fixed cycles amount in DOSBox's options.");
}
} else {
- GFX_SetTitle(-1,-1,false);
+ GFX_SetTitle_ptr(-1,-1,false);
}
#if (C_DYNAMIC_X86)
if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CORE) {
@@ -2104,7 +2106,7 @@
CPU_CyclePercUsed+=5;
if (CPU_CyclePercUsed>105) CPU_CyclePercUsed=105;
LOG_MSG("CPU speed: max %d percent.",CPU_CyclePercUsed);
- GFX_SetTitle(CPU_CyclePercUsed,-1,false);
+ GFX_SetTitle_ptr(CPU_CyclePercUsed,-1,false);
} else {
Bit32s old_cycles=CPU_CycleMax;
if (CPU_CycleUp < 100) {
@@ -2119,7 +2121,7 @@
LOG_MSG("CPU speed: fixed %d cycles. If you need more than 20000, try core=dynamic in DOSBox's options.",CPU_CycleMax);
else
LOG_MSG("CPU speed: fixed %d cycles.",CPU_CycleMax);
- GFX_SetTitle(CPU_CycleMax,-1,false);
+ GFX_SetTitle_ptr(CPU_CycleMax,-1,false);
}
}
@@ -2132,7 +2134,7 @@
LOG_MSG("CPU speed: max %d percent. If the game runs too fast, try a fixed cycles amount in DOSBox's options.",CPU_CyclePercUsed);
else
LOG_MSG("CPU speed: max %d percent.",CPU_CyclePercUsed);
- GFX_SetTitle(CPU_CyclePercUsed,-1,false);
+ GFX_SetTitle_ptr(CPU_CyclePercUsed,-1,false);
} else {
if (CPU_CycleDown < 100) {
CPU_CycleMax = (Bit32s)(CPU_CycleMax / (1 + (float)CPU_CycleDown / 100.0));
@@ -2142,7 +2144,7 @@
CPU_CycleLeft=0;CPU_Cycles=0;
if (CPU_CycleMax <= 0) CPU_CycleMax=1;
LOG_MSG("CPU speed: fixed %d cycles.",CPU_CycleMax);
- GFX_SetTitle(CPU_CycleMax,-1,false);
+ GFX_SetTitle_ptr(CPU_CycleMax,-1,false);
}
}
@@ -2398,8 +2400,8 @@
if(CPU_CycleMax <= 0) CPU_CycleMax = 3000;
if(CPU_CycleUp <= 0) CPU_CycleUp = 500;
if(CPU_CycleDown <= 0) CPU_CycleDown = 20;
- if (CPU_CycleAutoAdjust) GFX_SetTitle(CPU_CyclePercUsed,-1,false);
- else GFX_SetTitle(CPU_CycleMax,-1,false);
+ if (CPU_CycleAutoAdjust) GFX_SetTitle_ptr(CPU_CyclePercUsed,-1,false);
+ else GFX_SetTitle_ptr(CPU_CycleMax,-1,false);
return true;
}
~CPU(){ /* empty */};