From ae02bb54a10c650b0a0886adc05e4705924f7787 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 24 Mar 2013 23:44:13 +0100 Subject: [PATCH 1/9] Make it compile with SDL2 again For some reason SDL.h (or headers included by it) need some string functions (like strncmp) in inline-functions (that we don't even use). Str.h has #defines preventing their usage.. so #undef those in the (few) sourcefiles that need SDL headers --- neo/sys/sdl/sdl_cpu.cpp | 11 +++++++++-- neo/sys/sdl/sdl_events.cpp | 7 +++++++ neo/sys/sdl/sdl_glimp.cpp | 11 +++++++++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/neo/sys/sdl/sdl_cpu.cpp b/neo/sys/sdl/sdl_cpu.cpp index 0c893bd3..a64bb572 100644 --- a/neo/sys/sdl/sdl_cpu.cpp +++ b/neo/sys/sdl/sdl_cpu.cpp @@ -26,12 +26,19 @@ If you have questions concerning this license or the applicable additional terms =========================================================================== */ -#include #pragma hdrstop #include "../../idlib/precompiled.h" -//#include "win_local.h" +// DG: SDL_*.h somehow needs the following functions, so #undef those silly +// "don't use" #defines from Str.h +#undef strcasecmp +#undef strncmp +#undef vsnprintf +// DG end + +#include + #pragma warning(disable:4740) // warning C4740: flow in or out of inline asm code suppresses global optimization #pragma warning(disable:4731) // warning C4731: 'XXX' : frame pointer register 'ebx' modified by inline assembly code diff --git a/neo/sys/sdl/sdl_events.cpp b/neo/sys/sdl/sdl_events.cpp index 262b8bcb..bfd35ed3 100644 --- a/neo/sys/sdl/sdl_events.cpp +++ b/neo/sys/sdl/sdl_events.cpp @@ -31,6 +31,13 @@ If you have questions concerning this license or the applicable additional terms #include "../../idlib/precompiled.h" +// DG: SDL.h somehow needs the following functions, so #undef those silly +// "don't use" #defines from Str.h +#undef strncmp +#undef strcasecmp +#undef vsnprintf +// DG end + #include #include "renderer/tr_local.h" diff --git a/neo/sys/sdl/sdl_glimp.cpp b/neo/sys/sdl/sdl_glimp.cpp index b4323251..a0098126 100644 --- a/neo/sys/sdl/sdl_glimp.cpp +++ b/neo/sys/sdl/sdl_glimp.cpp @@ -29,11 +29,18 @@ If you have questions concerning this license or the applicable additional terms =========================================================================== */ +#include "../../idlib/precompiled.h" + +// DG: SDL.h somehow needs the following functions, so #undef those silly +// "don't use" #defines from Str.h +#undef strncmp +#undef strcasecmp +#undef vsnprintf +// DG end + #include #include -#include "../../idlib/precompiled.h" - #include "renderer/tr_local.h" #include "sdl_local.h" From fe18a493034fd4c76256b069ef85e89b991888dc Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Mon, 25 Mar 2013 00:38:54 +0100 Subject: [PATCH 2/9] Improve POSIX threading code * setting threadname is now done in a seperate function so it's a bit cleaner (it's different for every platform..) * replace/refactor signaling code (based on my SDL threading branch and the old pthread signaling code from RB): - The interface is like on win32 now (Sys_Signal* functions instead of overwriting idSysSignal class) - created a custom signalHandle_t struct for that, which contains all needed information - Mimic Windows functions used in win32 implementation more closely, e.g. signal all waiting threads on manualReset signalRaise, count waiting threads etc. I'm pretty sure the behavior on Win32 and POSIX now is identical (as far as possible). --- neo/idlib/Thread.h | 32 ---- neo/idlib/sys/posix/posix_thread.cpp | 267 +++++++++++++++++---------- neo/idlib/sys/sys_threading.h | 17 +- 3 files changed, 181 insertions(+), 135 deletions(-) diff --git a/neo/idlib/Thread.h b/neo/idlib/Thread.h index 7b1b2308..55295636 100644 --- a/neo/idlib/Thread.h +++ b/neo/idlib/Thread.h @@ -93,8 +93,6 @@ that a thread can wait on for it to be raised. It's used to indicate data is av a thread has reached a specific point. ================================================ */ -// RB begin -#if defined(_WIN32) class idSysSignal { public: @@ -132,36 +130,6 @@ private: idSysSignal( const idSysSignal& s ) {} void operator=( const idSysSignal& s ) {} }; -#else -class idSysSignal -{ -public: - static const int WAIT_INFINITE = -1; - - idSysSignal( bool manualReset = false ); - ~idSysSignal(); - - void Raise(); - - void Clear(); - - // Wait returns true if the object is in a signalled state and - // returns false if the wait timed out. Wait also clears the signalled - // state when the signalled state is reached within the time out period. - bool Wait( int timeout = WAIT_INFINITE ); - -private: - pthread_mutex_t mutex; - pthread_cond_t cond; - bool signaled; - int signalCounter; - bool waiting; - bool manualReset; - - idSysSignal( const idSysSignal& s ) {} - void operator=( const idSysSignal& s ) {} -}; -#endif /* ================================================ diff --git a/neo/idlib/sys/posix/posix_thread.cpp b/neo/idlib/sys/posix/posix_thread.cpp index ad48521d..32c6ad0c 100644 --- a/neo/idlib/sys/posix/posix_thread.cpp +++ b/neo/idlib/sys/posix/posix_thread.cpp @@ -4,6 +4,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. Copyright (C) 2012 Robert Beckebans +Copyright (C) 2013 Daniel Gibson This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -29,11 +30,50 @@ If you have questions concerning this license or the applicable additional terms #pragma hdrstop #include "../../precompiled.h" +#ifdef __FreeBSD__ +#include // for pthread_set_name_np +#endif //#define DEBUG_THREADS typedef void* ( *pthread_function_t )( void* ); +/* +======================== +Sys_SetThreadName + +caedes: This should be seen as a helper-function for Sys_CreateThread() only. + (re)setting the name of a running thread seems like a bad idea and + currently (fresh d3 bfg source) isn't done anyway. + Furthermore SDL doesn't support it + +======================== +*/ +static int Sys_SetThreadName( pthread_t handle, const char* name ) +{ + int ret = 0; +#ifdef __linux__ + ret = pthread_setname_np( handle, name ); + // pthread_getname_np(pthread_t, char*, size_t) +#elif defined(__FreeBSD__) + // according to http://www.freebsd.org/cgi/man.cgi?query=pthread_set_name_np&sektion=3 + // the interface is void pthread_set_name_np(pthread_t tid, const char *name); + pthread_set_name_np( handle, name ); // doesn't return anything + // seems like there is no get_name equivalent +#endif + /* TODO: OSX: + // according to http://stackoverflow.com/a/7989973 + // this needs to be called in the thread to be named! + ret = pthread_setname_np(name); + // int pthread_getname_np(pthread_t, char*, size_t); + + // so we'd have to wrap the xthread_t function in Sys_CreateThread and set the name in the wrapping function... + */ + return ret; +} + +// TODO: Sys_GetThreadName() ? + /* ======================== Sys_Createthread @@ -60,9 +100,8 @@ uintptr_t Sys_CreateThread( xthread_t function, void* parms, xthreadPriority pri } pthread_attr_destroy( &attr ); - // RB: TODO pthread_setname_np is different on Linux, MacOSX and other systems #if defined(DEBUG_THREADS) - if( pthread_setname_np( handle, name ) != 0 ) + if( Sys_SetThreadName( handle, name ) != 0 ) { idLib::common->FatalError( "ERROR: pthread_setname_np %s failed\n", name ); return ( uintptr_t )0; @@ -104,7 +143,7 @@ Sys_GetCurrentThreadID uintptr_t Sys_GetCurrentThreadID() { /* - * This cast is save because pthread_self() + * This cast is safe because pthread_self() * returns a pointer and uintptr_t is * designed to hold a pointer. The compiler * is just too stupid to know. :) @@ -163,8 +202,23 @@ void Sys_Yield() ================================================================================================ */ -idSysSignal::idSysSignal( bool manualReset ) +/* +======================== +Sys_SignalCreate +======================== +*/ +void Sys_SignalCreate( signalHandle_t& handle, bool manualReset ) { + // handle = CreateEvent( NULL, manualReset, FALSE, NULL ); + + handle.manualReset = manualReset; + // if this is true, the signal is only set to nonsignaled when Clear() is called, + // else it's "auto-reset" and the state is set to !signaled after a single waiting + // thread has been released + + // the inital state is always "not signaled" + handle.signaled = false; + handle.waiting = 0; #if 0 pthread_mutexattr_t attr; @@ -174,121 +228,138 @@ idSysSignal::idSysSignal( bool manualReset ) pthread_mutex_init( &mutex, &attr ); pthread_mutexattr_destroy( &attr ); #else - pthread_mutex_init( &mutex, NULL ); + pthread_mutex_init( &handle.mutex, NULL ); #endif - pthread_cond_init( &cond, NULL ); + pthread_cond_init( &handle.cond, NULL ); - signaled = false; - signalCounter = 0; - waiting = false; - this->manualReset = manualReset; } -idSysSignal::~idSysSignal() +/* +======================== +Sys_SignalDestroy +======================== +*/ +void Sys_SignalDestroy( signalHandle_t& handle ) { - pthread_cond_destroy( &cond ); - pthread_mutex_destroy( &mutex ); + // CloseHandle( handle ); + handle.signaled = false; + handle.waiting = 0; + pthread_mutex_destroy( &handle.mutex ); + pthread_cond_destroy( &handle.cond ); } -void idSysSignal::Raise() +/* +======================== +Sys_SignalRaise +======================== +*/ +void Sys_SignalRaise( signalHandle_t& handle ) { - pthread_mutex_lock( &mutex ); + // SetEvent( handle ); + pthread_mutex_lock( &handle.mutex ); - //if( waiting ) + if( handle.manualReset ) { - //pthread_cond_signal( &cond ); - //pthread_cond_broadcast( &cond ); - } - //else - if( !signaled ) - { - // emulate Windows behaviour: if no thread is waiting, leave the signal on so next wait keeps going - signaled = true; - signalCounter++; - - pthread_cond_signal( &cond ); - //pthread_cond_broadcast( &cond ); - } - - pthread_mutex_unlock( &mutex ); -} - -void idSysSignal::Clear() -{ - pthread_mutex_lock( &mutex ); - - signaled = false; - - pthread_mutex_unlock( &mutex ); -} - -// Wait returns true if the object is in a signalled state and -// returns false if the wait timed out. Wait also clears the signalled -// state when the signalled state is reached within the time out period. -bool idSysSignal::Wait( int timeout ) -{ - pthread_mutex_lock( &mutex ); - - int result = 0; - -#if 1 - assert( !waiting ); // WaitForEvent from multiple threads? that wouldn't be good - - if( signaled ) - { - if( !manualReset ) - { - // emulate Windows behaviour: signal has been raised already. clear and keep going - signaled = false; - result = 0; - } + // signaled until reset + handle.signaled = true; + // wake *all* threads waiting on this cond + pthread_cond_broadcast( &handle.cond ); } else -#endif { - -#if 0 - - int signalValue = signalCounter; - //while( !signaled && signalValue == signalCounter ) -#endif + // automode: signaled until first thread is released + if( handle.waiting > 0 ) { - waiting = true; - - if( timeout == WAIT_INFINITE ) - { - result = pthread_cond_wait( &cond, &mutex ); - - assert( result == 0 ); - } - else - { - timespec ts; - clock_gettime( CLOCK_REALTIME, &ts ); - // DG: handle timeouts > 1s better - int64 t = timeout * 1000000; - ts.tv_nsec += t % 1000000000; - ts.tv_sec += t / 1000000000; - // DG end - result = pthread_cond_timedwait( &cond, &mutex, &ts ); - - assert( result == 0 || ( timeout != idSysSignal::WAIT_INFINITE && result == ETIMEDOUT ) ); - } - - waiting = false; + // there are waiting threads => release one + pthread_cond_signal( &handle.cond ); } - - if( !manualReset ) + else { - signaled = false; + // no waiting threads, save signal + handle.signaled = true; + // while the MSDN documentation is a bit unspecific about what happens + // when SetEvent() is called n times without a wait inbetween + // (will only one wait be successful afterwards or n waits?) + // it seems like the signaled state is a flag, not a counter. + // http://stackoverflow.com/a/13703585 claims the same. } } - pthread_mutex_unlock( &mutex ); - - return ( result == 0 ); + pthread_mutex_unlock( &handle.mutex ); } + +/* +======================== +Sys_SignalClear +======================== +*/ +void Sys_SignalClear( signalHandle_t& handle ) +{ + // ResetEvent( handle ); + pthread_mutex_lock( &handle.mutex ); + + // TODO: probably signaled could be atomically changed? + handle.signaled = false; + + pthread_mutex_unlock( &handle.mutex ); +} + + +/* +======================== +Sys_SignalWait +======================== +*/ +bool Sys_SignalWait( signalHandle_t& handle, int timeout ) +{ + //DWORD result = WaitForSingleObject( handle, timeout == idSysSignal::WAIT_INFINITE ? INFINITE : timeout ); + //assert( result == WAIT_OBJECT_0 || ( timeout != idSysSignal::WAIT_INFINITE && result == WAIT_TIMEOUT ) ); + //return ( result == WAIT_OBJECT_0 ); + + int status; + pthread_mutex_lock( &handle.mutex ); + + if( handle.signaled ) // there is a signal that hasn't been used yet + { + if( ! handle.manualReset ) // for auto-mode only one thread may be released - this one. + handle.signaled = false; + + status = 0; // success! + } + else // we'll have to wait for a signal + { + ++handle.waiting; + if( timeout == idSysSignal::WAIT_INFINITE ) + { + status = pthread_cond_wait( &handle.cond, &handle.mutex ); + } + else + { + timespec ts; + clock_gettime( CLOCK_REALTIME, &ts ); + // DG: handle timeouts > 1s better + ts.tv_nsec += ( timeout % 1000 ) * 1000000; // millisec to nanosec + ts.tv_sec += timeout / 1000; + if( ts.tv_nsec >= 1000000000 ) // nanoseconds are more than one second + { + ts.tv_nsec -= 1000000000; // remove one second in nanoseconds + ts.tv_sec += 1; // add one second to seconds + } + // DG end + status = pthread_cond_timedwait( &handle.cond, &handle.mutex, &ts ); + } + --handle.waiting; + } + + pthread_mutex_unlock( &handle.mutex ); + + assert( status == 0 || ( timeout != idSysSignal::WAIT_INFINITE && status == ETIMEDOUT ) ); + + return ( status == 0 ); + +} + /* ================================================================================================ diff --git a/neo/idlib/sys/sys_threading.h b/neo/idlib/sys/sys_threading.h index 5d1a5307..1a8b7fa1 100644 --- a/neo/idlib/sys/sys_threading.h +++ b/neo/idlib/sys/sys_threading.h @@ -45,8 +45,19 @@ typedef CRITICAL_SECTION mutexHandle_t; typedef HANDLE signalHandle_t; typedef LONG interlockedInt_t; #else + +struct signalHandle_t +{ + // DG: all this stuff is needed to emulate Window's Event API + // (CreateEvent(), SetEvent(), WaitForSingleObject(), ...) + pthread_cond_t cond; + pthread_mutex_t mutex; + int waiting; // number of threads waiting for a signal + bool manualReset; + bool signaled; // is it signaled right now? +}; + typedef pthread_mutex_t mutexHandle_t; -typedef pthread_cond_t signalHandle_t; typedef int interlockedInt_t; #endif // RB end @@ -195,15 +206,11 @@ uintptr_t Sys_CreateThread( xthread_t function, void* parms, xthreadPriority p void Sys_DestroyThread( uintptr_t threadHandle ); void Sys_SetCurrentThreadName( const char* name ); -// use alternative pthread implementation in idSysSignal -#if defined(_WIN32) void Sys_SignalCreate( signalHandle_t& handle, bool manualReset ); void Sys_SignalDestroy( signalHandle_t& handle ); void Sys_SignalRaise( signalHandle_t& handle ); void Sys_SignalClear( signalHandle_t& handle ); bool Sys_SignalWait( signalHandle_t& handle, int timeout ); -#endif -// RB end void Sys_MutexCreate( mutexHandle_t& handle ); void Sys_MutexDestroy( mutexHandle_t& handle ); From da9ab07e9c67fe574a31696ed1241ff3e6447cd6 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Mon, 25 Mar 2013 23:34:26 +0100 Subject: [PATCH 3/9] Refactor setting threadname on POSIX systems * setting threadname is now done in a seperate function * if setting the threadname fails, it just prints a warning now instead of terminating the game with a FatalError --- neo/idlib/sys/posix/posix_thread.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/neo/idlib/sys/posix/posix_thread.cpp b/neo/idlib/sys/posix/posix_thread.cpp index 32c6ad0c..39ce2ae6 100644 --- a/neo/idlib/sys/posix/posix_thread.cpp +++ b/neo/idlib/sys/posix/posix_thread.cpp @@ -34,6 +34,7 @@ If you have questions concerning this license or the applicable additional terms #include // for pthread_set_name_np #endif +// DG: Note: On Linux you need at least (e)glibc 2.12 to be able to set the threadname //#define DEBUG_THREADS typedef void* ( *pthread_function_t )( void* ); @@ -49,11 +50,19 @@ caedes: This should be seen as a helper-function for Sys_CreateThread() only. ======================== */ +#ifdef DEBUG_THREADS static int Sys_SetThreadName( pthread_t handle, const char* name ) { int ret = 0; #ifdef __linux__ + // NOTE: linux only supports threadnames up to 16chars *including* terminating NULL + // http://man7.org/linux/man-pages/man3/pthread_setname_np.3.html + // on my machine a longer name (eg "JobListProcessor_0") caused an ENOENT error (instead of ERANGE) + assert( strlen( name ) < 16 ); + ret = pthread_setname_np( handle, name ); + if( ret != 0 ) + idLib::common->Printf( "Setting threadname \"%s\" failed, reason: %s (%i)\n", name, strerror( errno ), errno ); // pthread_getname_np(pthread_t, char*, size_t) #elif defined(__FreeBSD__) // according to http://www.freebsd.org/cgi/man.cgi?query=pthread_set_name_np&sektion=3 @@ -69,10 +78,14 @@ static int Sys_SetThreadName( pthread_t handle, const char* name ) // so we'd have to wrap the xthread_t function in Sys_CreateThread and set the name in the wrapping function... */ + return ret; } // TODO: Sys_GetThreadName() ? +#endif // DEBUG_THREADS + + /* ======================== @@ -103,7 +116,7 @@ uintptr_t Sys_CreateThread( xthread_t function, void* parms, xthreadPriority pri #if defined(DEBUG_THREADS) if( Sys_SetThreadName( handle, name ) != 0 ) { - idLib::common->FatalError( "ERROR: pthread_setname_np %s failed\n", name ); + idLib::common->Warning( "Warning: pthread_setname_np %s failed\n", name ); return ( uintptr_t )0; } #endif From 3c755e490b61102b130f39338a1e7c87e9245168 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Tue, 26 Mar 2013 00:34:24 +0100 Subject: [PATCH 4/9] change threadnames so they fit into 15chars + terminating null byte - that's the limit of threadnames on linux Furthermore: idJobThread::Start used va() to create the threadname. va() isn't threadsafe... so I replaced it with a local buffer and idStr::snPrintf() --- neo/framework/File_SaveGame.cpp | 20 ++++++++++++++++---- neo/idlib/ParallelJobList.cpp | 10 ++++++++-- neo/renderer/AutoRender.cpp | 7 ++++--- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/neo/framework/File_SaveGame.cpp b/neo/framework/File_SaveGame.cpp index 83ed54ea..b37f5926 100644 --- a/neo/framework/File_SaveGame.cpp +++ b/neo/framework/File_SaveGame.cpp @@ -411,7 +411,10 @@ bool idFile_SaveGamePipelined::OpenForWriting( const char* const filename, bool { compressThread = new( TAG_IDFILE ) idSGFcompressThread(); compressThread->sgf = this; - compressThread->StartWorkerThread( "SGF_CompressThread", CORE_2B, THREAD_NORMAL ); + // DG: change threadname from "SGF_CompressThread" to "SGF_Compress", because Linux + // has a 15 (+ \0) char limit for threadnames. also, "thread" in a threadname is redundant + compressThread->StartWorkerThread( "SGF_Compress", CORE_2B, THREAD_NORMAL ); + // DG end } if( nativeFile != NULL && sgf_threads.GetInteger() >= 2 ) { @@ -468,7 +471,10 @@ bool idFile_SaveGamePipelined::OpenForWriting( idFile* file ) { compressThread = new( TAG_IDFILE ) idSGFcompressThread(); compressThread->sgf = this; - compressThread->StartWorkerThread( "SGF_CompressThread", CORE_2B, THREAD_NORMAL ); + // DG: change threadname from "SGF_CompressThread" to "SGF_Compress", because Linux + // has a 15 (+ \0) char limit for threadnames. also, "thread" in a threadname is redundant + compressThread->StartWorkerThread( "SGF_Compress", CORE_2B, THREAD_NORMAL ); + // DG end } if( nativeFile != NULL && sgf_threads.GetInteger() >= 2 ) { @@ -810,7 +816,10 @@ bool idFile_SaveGamePipelined::OpenForReading( const char* const filename, bool { decompressThread = new( TAG_IDFILE ) idSGFdecompressThread(); decompressThread->sgf = this; - decompressThread->StartWorkerThread( "SGF_DecompressThread", CORE_2B, THREAD_NORMAL ); + // DG: change threadname from "SGF_DecompressThread" to "SGF_Decompress", because Linux + // has a 15 (+ \0) char limit for threadnames. also, "thread" in a threadname is redundant + decompressThread->StartWorkerThread( "SGF_Decompress", CORE_2B, THREAD_NORMAL ); + // DG end } if( nativeFile != NULL && sgf_threads.GetInteger() >= 2 ) { @@ -857,7 +866,10 @@ bool idFile_SaveGamePipelined::OpenForReading( idFile* file ) { decompressThread = new( TAG_IDFILE ) idSGFdecompressThread(); decompressThread->sgf = this; - decompressThread->StartWorkerThread( "SGF_DecompressThread", CORE_1B, THREAD_NORMAL ); + // DG: change threadname from "SGF_DecompressThread" to "SGF_Decompress", because Linux + // has a 15 (+ \0) char limit for threadnames. also, "thread" in a threadname is redundant + decompressThread->StartWorkerThread( "SGF_Decompress", CORE_1B, THREAD_NORMAL ); + // DG end } if( nativeFile != NULL && sgf_threads.GetInteger() >= 2 ) { diff --git a/neo/idlib/ParallelJobList.cpp b/neo/idlib/ParallelJobList.cpp index c98a3c00..c634c669 100644 --- a/neo/idlib/ParallelJobList.cpp +++ b/neo/idlib/ParallelJobList.cpp @@ -1107,7 +1107,13 @@ idJobThread::Start void idJobThread::Start( core_t core, unsigned int threadNum ) { this->threadNum = threadNum; - StartWorkerThread( va( "JobListProcessor_%d", threadNum ), core, THREAD_NORMAL, JOB_THREAD_STACK_SIZE ); + // DG: change threadname from "JobListProcessor_%d" to "JLProc_%d", because Linux + // has a 15 (+ \0) char limit for threadnames. + // furthermore: va is not thread safe, use snPrintf instead + char name[16]; + idStr::snPrintf( name, 16, "JLProc_%d", threadNum ); + StartWorkerThread( name, core, THREAD_NORMAL, JOB_THREAD_STACK_SIZE ); + // DG end } /* @@ -1493,4 +1499,4 @@ void idParallelJobManagerLocal::Submit( idParallelJobList_Threads* jobList, int threads[i].AddJobList( jobList ); threads[i].SignalWork(); } -} \ No newline at end of file +} diff --git a/neo/renderer/AutoRender.cpp b/neo/renderer/AutoRender.cpp index 439195f6..f4db0640 100644 --- a/neo/renderer/AutoRender.cpp +++ b/neo/renderer/AutoRender.cpp @@ -91,8 +91,9 @@ void idAutoRender::StartBackgroundAutoSwaps( autoRenderIconType_t iconType ) // it is getting purged before our our first frame has been rendered. globalImages->UnbindAll(); - - StartThread( "BackgroundAutoSwaps", CORE_0B, THREAD_NORMAL, AUTO_RENDER_STACK_SIZE ); + // DG: set name to "BGAutoSwaps" because Linux has a 16char (incl. \0) namelimit for threads + StartThread( "BGAutoSwaps", CORE_0B, THREAD_NORMAL, AUTO_RENDER_STACK_SIZE ); + // DG end } /* @@ -300,4 +301,4 @@ void idAutoRender::RenderLoadingIcon( float fracX, float fracY, float size, floa renderProgManager.BindShader_TextureVertexColor(); RB_DrawElementsWithCounters( &backEnd.unitSquareSurface ); -} \ No newline at end of file +} From dcae311b7f898439b434892a9afe4962c4db1e94 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Tue, 26 Mar 2013 09:56:48 +0100 Subject: [PATCH 5/9] Renamed in_kbd to in_keyboard --- neo/sys/sdl/sdl_events.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/neo/sys/sdl/sdl_events.cpp b/neo/sys/sdl/sdl_events.cpp index 262b8bcb..13f151f3 100644 --- a/neo/sys/sdl/sdl_events.cpp +++ b/neo/sys/sdl/sdl_events.cpp @@ -72,7 +72,7 @@ const char* kbdNames[] = "english", "french", "german", "italian", "spanish", "turkish", "norwegian", NULL }; -idCVar in_kbd( "in_kbd", "english", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT, "keyboard layout", kbdNames, idCmdSystem::ArgCompletion_String ); +idCVar in_keyboard( "in_keyboard", "english", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT, "keyboard layout", kbdNames, idCmdSystem::ArgCompletion_String ); struct kbd_poll_t { @@ -552,7 +552,7 @@ void Sys_InitInput() SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL ); #endif - in_kbd.SetModified(); + in_keyboard.SetModified(); } /* @@ -587,9 +587,9 @@ unsigned char Sys_GetConsoleKey( bool shifted ) { static unsigned char keys[2] = { '`', '~' }; - if( in_kbd.IsModified() ) + if( in_keyboard.IsModified() ) { - idStr lang = in_kbd.GetString(); + idStr lang = in_keyboard.GetString(); if( lang.Length() ) { @@ -625,7 +625,7 @@ unsigned char Sys_GetConsoleKey( bool shifted ) } } - in_kbd.ClearModified(); + in_keyboard.ClearModified(); } return shifted ? keys[1] : keys[0]; From 3023533e57067a49f81d8d08f7f4dde02d11d44b Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Tue, 26 Mar 2013 13:43:30 +0100 Subject: [PATCH 6/9] Added pthread priority code but it is disabled by default because it requires root privileges --- neo/idlib/sys/posix/posix_thread.cpp | 69 +++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/neo/idlib/sys/posix/posix_thread.cpp b/neo/idlib/sys/posix/posix_thread.cpp index 39ce2ae6..a0bd8fca 100644 --- a/neo/idlib/sys/posix/posix_thread.cpp +++ b/neo/idlib/sys/posix/posix_thread.cpp @@ -3,7 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. -Copyright (C) 2012 Robert Beckebans +Copyright (C) 2012-2013 Robert Beckebans Copyright (C) 2013 Daniel Gibson This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -94,8 +94,6 @@ Sys_Createthread */ uintptr_t Sys_CreateThread( xthread_t function, void* parms, xthreadPriority priority, const char* name, core_t core, int stackSize, bool suspended ) { - //Sys_EnterCriticalSection(); - pthread_attr_t attr; pthread_attr_init( &attr ); @@ -111,7 +109,6 @@ uintptr_t Sys_CreateThread( xthread_t function, void* parms, xthreadPriority pri idLib::common->FatalError( "ERROR: pthread_create %s failed\n", name ); return ( uintptr_t )0; } - pthread_attr_destroy( &attr ); #if defined(DEBUG_THREADS) if( Sys_SetThreadName( handle, name ) != 0 ) @@ -121,26 +118,76 @@ uintptr_t Sys_CreateThread( xthread_t function, void* parms, xthreadPriority pri } #endif - /* - TODO RB: support thread priorities? + pthread_attr_destroy( &attr ); + + +#if 0 + // RB: realtime policies require root privileges + + // all Linux threads have one of the following scheduling policies: + + // SCHED_OTHER or SCHED_NORMAL: the default policy, priority: [-20..0..19], default 0 + + // SCHED_FIFO: first in/first out realtime policy + + // SCHED_RR: round-robin realtime policy + + // SCHED_BATCH: similar to SCHED_OTHER, but with a throughput orientation + + // SCHED_IDLE: lower priority than SCHED_OTHER + + int schedulePolicy = SCHED_OTHER; + struct sched_param scheduleParam; + + int error = pthread_getschedparam( handle, &schedulePolicy, &scheduleParam ); + if( error != 0 ) + { + idLib::common->FatalError( "ERROR: pthread_getschedparam %s failed: %s\n", name, strerror( error ) ); + return ( uintptr_t )0; + } + + schedulePolicy = SCHED_FIFO; + + int minPriority = sched_get_priority_min( schedulePolicy ); + int maxPriority = sched_get_priority_max( schedulePolicy ); if( priority == THREAD_HIGHEST ) { - SetThreadPriority( ( HANDLE )handle, THREAD_PRIORITY_HIGHEST ); // we better sleep enough to do this + // we better sleep enough to do this + scheduleParam.__sched_priority = maxPriority; } else if( priority == THREAD_ABOVE_NORMAL ) { - SetThreadPriority( ( HANDLE )handle, THREAD_PRIORITY_ABOVE_NORMAL ); + scheduleParam.__sched_priority = Lerp( minPriority, maxPriority, 0.75f ); + } + else if( priority == THREAD_NORMAL ) + { + scheduleParam.__sched_priority = Lerp( minPriority, maxPriority, 0.5f ); } else if( priority == THREAD_BELOW_NORMAL ) { - SetThreadPriority( ( HANDLE )handle, THREAD_PRIORITY_BELOW_NORMAL ); + scheduleParam.__sched_priority = Lerp( minPriority, maxPriority, 0.25f ); } else if( priority == THREAD_LOWEST ) { - SetThreadPriority( ( HANDLE )handle, THREAD_PRIORITY_LOWEST ); + scheduleParam.__sched_priority = minPriority; } - */ + + // set new priority + error = pthread_setschedparam( handle, schedulePolicy, &scheduleParam ); + if( error != 0 ) + { + idLib::common->FatalError( "ERROR: pthread_setschedparam( name = %s, policy = %i, priority = %i ) failed: %s\n", name, schedulePolicy, scheduleParam.__sched_priority, strerror( error ) ); + return ( uintptr_t )0; + } + + pthread_getschedparam( handle, &schedulePolicy, &scheduleParam ); + if( error != 0 ) + { + idLib::common->FatalError( "ERROR: pthread_getschedparam %s failed: %s\n", name, strerror( error ) ); + return ( uintptr_t )0; + } +#endif // Under Linux, we don't set the thread affinity and let the OS deal with scheduling From d176bca1e701b902eb18d121ceb441bcfe620b60 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Wed, 10 Apr 2013 02:13:27 +0200 Subject: [PATCH 7/9] Fixed critical out of bounds bug in RB_DrawShaderPasses() --- neo/renderer/RenderProgs.cpp | 93 -------------------------------- neo/renderer/RenderProgs.h | 1 - neo/renderer/tr_backend_draw.cpp | 14 +++-- 3 files changed, 6 insertions(+), 102 deletions(-) diff --git a/neo/renderer/RenderProgs.cpp b/neo/renderer/RenderProgs.cpp index b59579e5..572518e5 100644 --- a/neo/renderer/RenderProgs.cpp +++ b/neo/renderer/RenderProgs.cpp @@ -310,99 +310,6 @@ void idRenderProgManager::LoadFragmentShader( int index ) fragmentShaders[index].progId = ( GLuint ) LoadGLSLShader( GL_FRAGMENT_SHADER, fragmentShaders[index].name, fragmentShaders[index].uniforms ); } -/* -================================================================================================ -idRenderProgManager::LoadShader -================================================================================================ -*/ -GLuint idRenderProgManager::LoadShader( GLenum target, const char* name, const char* startToken ) -{ - - idStr fullPath = "renderprogs\\gl\\"; - fullPath += name; - - common->Printf( "%s", fullPath.c_str() ); - - char* fileBuffer = NULL; - fileSystem->ReadFile( fullPath.c_str(), ( void** )&fileBuffer, NULL ); - if( fileBuffer == NULL ) - { - common->Printf( ": File not found\n" ); - return INVALID_PROGID; - } - if( !R_IsInitialized() ) - { - common->Printf( ": Renderer not initialized\n" ); - fileSystem->FreeFile( fileBuffer ); - return INVALID_PROGID; - } - - // vertex and fragment shaders are both be present in a single file, so - // scan for the proper header to be the start point, and stamp a 0 in after the end - char* start = strstr( ( char* )fileBuffer, startToken ); - if( start == NULL ) - { - common->Printf( ": %s not found\n", startToken ); - fileSystem->FreeFile( fileBuffer ); - return INVALID_PROGID; - } - char* end = strstr( start, "END" ); - if( end == NULL ) - { - common->Printf( ": END not found for %s\n", startToken ); - fileSystem->FreeFile( fileBuffer ); - return INVALID_PROGID; - } - end[3] = 0; - - idStr program = start; - program.Replace( "vertex.normal", "vertex.attrib[11]" ); - program.Replace( "vertex.texcoord[0]", "vertex.attrib[8]" ); - program.Replace( "vertex.texcoord", "vertex.attrib[8]" ); - - GLuint progId; - qglGenProgramsARB( 1, &progId ); - - qglBindProgramARB( target, progId ); - qglGetError(); - - qglProgramStringARB( target, GL_PROGRAM_FORMAT_ASCII_ARB, program.Length(), program.c_str() ); - GLenum err = qglGetError(); - - GLint ofs = -1; - qglGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &ofs ); - if( ( err == GL_INVALID_OPERATION ) || ( ofs != -1 ) ) - { - if( err == GL_INVALID_OPERATION ) - { - const GLubyte* str = qglGetString( GL_PROGRAM_ERROR_STRING_ARB ); - common->Printf( "\nGL_PROGRAM_ERROR_STRING_ARB: %s\n", str ); - } - else - { - common->Printf( "\nUNKNOWN ERROR\n" ); - } - if( ofs < 0 ) - { - common->Printf( "GL_PROGRAM_ERROR_POSITION_ARB < 0\n" ); - } - else if( ofs >= program.Length() ) - { - common->Printf( "error at end of shader\n" ); - } - else - { - common->Printf( "error at %i:\n%s", ofs, program.c_str() + ofs ); - } - qglDeleteProgramsARB( 1, &progId ); - fileSystem->FreeFile( fileBuffer ); - return INVALID_PROGID; - } - common->Printf( "\n" ); - fileSystem->FreeFile( fileBuffer ); - return progId; -} - /* ================================================================================================ idRenderProgManager::BindShader diff --git a/neo/renderer/RenderProgs.h b/neo/renderer/RenderProgs.h index ba97546f..098d7b03 100644 --- a/neo/renderer/RenderProgs.h +++ b/neo/renderer/RenderProgs.h @@ -368,7 +368,6 @@ protected: BindShader( builtinShaders[i], builtinShaders[i] ); } - GLuint LoadShader( GLenum target, const char* name, const char* startToken ); bool CompileGLSL( GLenum target, const char* name ); GLuint LoadGLSLShader( GLenum target, const char* name, idList& uniforms ); void LoadGLSLProgram( const int programIndex, const int vertexShaderIndex, const int fragmentShaderIndex ); diff --git a/neo/renderer/tr_backend_draw.cpp b/neo/renderer/tr_backend_draw.cpp index 4979ef46..c625b1a4 100644 --- a/neo/renderer/tr_backend_draw.cpp +++ b/neo/renderer/tr_backend_draw.cpp @@ -3,6 +3,7 @@ Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. +Copyright (C) 2013 Robert Beckebans This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). @@ -178,13 +179,7 @@ void RB_DrawElementsWithCounters( const drawSurf_t* surf ) if( surf->jointCache ) { - // DG: this happens all the time in the erebus1 map with blendlight.vfp, - // so don't call assert (through verify) here until it's fixed (if fixable) - // else the game crashes on linux when using debug builds - // FIXME: fix this properly if possible? - //if( !verify( renderProgManager.ShaderUsesJoints() ) ) - if( ! renderProgManager.ShaderUsesJoints() ) - // DG end + if( !verify( renderProgManager.ShaderUsesJoints() ) ) { return; } @@ -2271,7 +2266,10 @@ static int RB_DrawShaderPasses( const drawSurf_t* const* const drawSurfs, const GL_State( stageGLState ); - renderProgManager.BindShader( newStage->glslProgram, newStage->glslProgram ); + // RB: CRITICAL BUGFIX: changed newStage->glslProgram to vertexProgram and fragmentProgram + // otherwise it will result in an out of bounds crash in RB_DrawElementsWithCounters + renderProgManager.BindShader( newStage->vertexProgram, newStage->fragmentProgram ); + // RB end for( int j = 0; j < newStage->numVertexParms; j++ ) { From e7ffc153c22b73cf90fe90e9adc920e625930308 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Wed, 10 Apr 2013 02:40:09 +0200 Subject: [PATCH 8/9] Added back workaround in RB_DrawElementsWithCounters --- neo/renderer/tr_backend_draw.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/neo/renderer/tr_backend_draw.cpp b/neo/renderer/tr_backend_draw.cpp index c625b1a4..95358c5e 100644 --- a/neo/renderer/tr_backend_draw.cpp +++ b/neo/renderer/tr_backend_draw.cpp @@ -179,7 +179,15 @@ void RB_DrawElementsWithCounters( const drawSurf_t* surf ) if( surf->jointCache ) { - if( !verify( renderProgManager.ShaderUsesJoints() ) ) + // DG: this happens all the time in the erebus1 map with blendlight.vfp, + // so don't call assert (through verify) here until it's fixed (if fixable) + // else the game crashes on linux when using debug builds + + // FIXME: fix this properly if possible? + // RB: yes but it would require an additional blend light skinned shader + //if( !verify( renderProgManager.ShaderUsesJoints() ) ) + if( !renderProgManager.ShaderUsesJoints() ) + // DG end { return; } From bdfc2aadb15d4354c34a5de36a2cb3c503dd7450 Mon Sep 17 00:00:00 2001 From: Robert Beckebans Date: Fri, 24 May 2013 18:55:56 +0200 Subject: [PATCH 9/9] Allow slow-motion sound effect for OpenAL sounds --- neo/sound/OpenAL/AL_SoundVoice.h | 7 +++++++ neo/sound/SoundVoice.h | 10 ++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/neo/sound/OpenAL/AL_SoundVoice.h b/neo/sound/OpenAL/AL_SoundVoice.h index 9fdb1cec..bddd15f4 100644 --- a/neo/sound/OpenAL/AL_SoundVoice.h +++ b/neo/sound/OpenAL/AL_SoundVoice.h @@ -56,6 +56,13 @@ public: alSourcef( openalSource, AL_GAIN, ( gain ) < ( 1.0f ) ? ( gain ) : ( 1.0f ) ); } + void SetPitch( float p ) + { + idSoundVoice_Base::SetPitch( p ); + + alSourcef( openalSource, AL_PITCH, p ); + } + void Create( const idSoundSample* leadinSample, const idSoundSample* loopingSample ); // Start playing at a particular point in the buffer. Does an Update() too diff --git a/neo/sound/SoundVoice.h b/neo/sound/SoundVoice.h index 7963a70b..8708cbb4 100644 --- a/neo/sound/SoundVoice.h +++ b/neo/sound/SoundVoice.h @@ -54,16 +54,18 @@ public: { gain = g; } + + virtual void SetPitch( float p ) + { + pitch = p; + } // RB end void SetCenterChannel( float c ) { centerChannel = c; } - void SetPitch( float p ) - { - pitch = p; - } + void SetInnerRadius( float r ) { innerRadius = r;