mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-17 23:39:18 +02:00
python interpreter refactored to a task queue
This commit is contained in:
@@ -70,4 +70,6 @@ enum class TCommandType
|
|||||||
cm_Command // komenda pobierana z komórki
|
cm_Command // komenda pobierana z komórki
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using material_handle = int;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
817
PyInt.cpp
817
PyInt.cpp
@@ -1,24 +1,56 @@
|
|||||||
|
/*
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "PyInt.h"
|
#include "PyInt.h"
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "parser.h"
|
#include "application.h"
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
#include "Model3d.h"
|
|
||||||
#include "Train.h"
|
|
||||||
#include "Logs.h"
|
#include "Logs.h"
|
||||||
|
|
||||||
TPythonInterpreter *TPythonInterpreter::_instance = NULL;
|
void render_task::run() {
|
||||||
|
// call the renderer
|
||||||
|
auto *output { PyObject_CallMethod( m_renderer, "render", "O", m_input ) };
|
||||||
|
Py_DECREF( m_input );
|
||||||
|
|
||||||
//#define _PY_INT_MORE_LOG
|
if( output != nullptr ) {
|
||||||
|
auto *outputwidth { PyObject_CallMethod( m_renderer, "get_width", nullptr ) };
|
||||||
|
auto *outputheight { PyObject_CallMethod( m_renderer, "get_height", nullptr ) };
|
||||||
|
// upload texture data
|
||||||
|
if( ( outputwidth != nullptr )
|
||||||
|
&& ( outputheight != nullptr ) ) {
|
||||||
|
|
||||||
#ifdef __GNUC__
|
GfxRenderer.Bind_Material( m_target );
|
||||||
#pragma GCC diagnostic ignored "-Wwrite-strings"
|
// setup texture parameters
|
||||||
#endif
|
::glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
|
||||||
|
::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
||||||
|
::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
|
||||||
|
::glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0 );
|
||||||
|
// build texture
|
||||||
|
::glTexImage2D(
|
||||||
|
GL_TEXTURE_2D, 0,
|
||||||
|
GL_RGBA8,
|
||||||
|
PyInt_AsLong( outputwidth ), PyInt_AsLong( outputheight ), 0,
|
||||||
|
GL_RGB, GL_UNSIGNED_BYTE, reinterpret_cast<GLubyte const *>( PyString_AsString( output ) ) );
|
||||||
|
}
|
||||||
|
Py_DECREF( outputheight );
|
||||||
|
Py_DECREF( outputwidth );
|
||||||
|
Py_DECREF( output );
|
||||||
|
}
|
||||||
|
// clean up after yourself
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initializes the module. returns true on success
|
||||||
|
auto python_taskqueue::init() -> bool {
|
||||||
|
|
||||||
TPythonInterpreter::TPythonInterpreter()
|
|
||||||
{
|
|
||||||
WriteLog("Loading Python ...");
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (sizeof(void*) == 8)
|
if (sizeof(void*) == 8)
|
||||||
Py_SetPythonHome("python64");
|
Py_SetPythonHome("python64");
|
||||||
@@ -31,568 +63,253 @@ TPythonInterpreter::TPythonInterpreter()
|
|||||||
Py_SetPythonHome("linuxpython");
|
Py_SetPythonHome("linuxpython");
|
||||||
#endif
|
#endif
|
||||||
Py_Initialize();
|
Py_Initialize();
|
||||||
_main = PyImport_ImportModule("__main__");
|
PyEval_InitThreads();
|
||||||
if (_main == NULL)
|
// save a pointer to the main PyThreadState object
|
||||||
{
|
m_mainthread = PyThreadState_Get();
|
||||||
WriteLog("Cannot import Python module __main__");
|
// do the setup work while we hold the lock
|
||||||
|
m_main = PyImport_ImportModule("__main__");
|
||||||
|
if (m_main == nullptr) {
|
||||||
|
ErrorLog( "Python Interpreter: __main__ module is missing" );
|
||||||
|
goto release_and_exit;
|
||||||
}
|
}
|
||||||
PyObject *cStringModule = PyImport_ImportModule("cStringIO");
|
|
||||||
_stdErr = NULL;
|
auto *stringiomodule { PyImport_ImportModule( "cStringIO" ) };
|
||||||
if (cStringModule == NULL)
|
auto *stringioclassname { (
|
||||||
return;
|
stringiomodule != nullptr ?
|
||||||
PyObject *cStringClassName = PyObject_GetAttrString(cStringModule, "StringIO");
|
PyObject_GetAttrString( stringiomodule, "StringIO" ) :
|
||||||
if (cStringClassName == NULL)
|
nullptr ) };
|
||||||
return;
|
auto *stringioobject { (
|
||||||
PyObject *cString = PyObject_CallObject(cStringClassName, NULL);
|
stringioclassname != nullptr ?
|
||||||
if (cString == NULL)
|
PyObject_CallObject( stringioclassname, nullptr ) :
|
||||||
return;
|
nullptr ) };
|
||||||
if (PySys_SetObject("stderr", cString) != 0)
|
m_error = { (
|
||||||
return;
|
stringioobject == nullptr ? nullptr :
|
||||||
_stdErr = cString;
|
PySys_SetObject( "stderr", stringioobject ) != 0 ? nullptr :
|
||||||
|
stringioobject ) };
|
||||||
|
|
||||||
|
if( m_error == nullptr ) { goto release_and_exit; }
|
||||||
|
|
||||||
|
if( false == run_file( "abstractscreenrenderer" ) ) { goto release_and_exit; }
|
||||||
|
|
||||||
|
// release the lock
|
||||||
|
PyEval_ReleaseLock();
|
||||||
|
|
||||||
|
WriteLog( "Python Interpreter setup complete" );
|
||||||
|
|
||||||
|
// init workers
|
||||||
|
for( auto &worker : m_workers ) {
|
||||||
|
|
||||||
|
auto *openglcontextwindow { Application.window( -1 ) };
|
||||||
|
worker =
|
||||||
|
std::make_unique<std::thread>(
|
||||||
|
&python_taskqueue::run, this,
|
||||||
|
openglcontextwindow, std::ref( m_tasks ), std::ref( m_condition ), std::ref( m_exit ) );
|
||||||
|
|
||||||
|
if( worker == nullptr ) { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
release_and_exit:
|
||||||
|
PyEval_ReleaseLock();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TPythonInterpreter *TPythonInterpreter::getInstance()
|
// shuts down the module
|
||||||
{
|
void python_taskqueue::exit() {
|
||||||
if (!_instance)
|
// let the workers know we're done with them
|
||||||
{
|
m_exit = true;
|
||||||
_instance = new TPythonInterpreter();
|
m_condition.notify_all();
|
||||||
|
// let them free up their shit before we proceed
|
||||||
|
for( auto const &worker : m_workers ) {
|
||||||
|
worker->join();
|
||||||
}
|
}
|
||||||
return _instance;
|
// get rid of the leftover tasks
|
||||||
|
// with the workers dead we don't have to worry about concurrent access anymore
|
||||||
|
for( auto *task : m_tasks.data ) {
|
||||||
|
delete task;
|
||||||
|
}
|
||||||
|
// take a bow
|
||||||
|
PyEval_AcquireLock();
|
||||||
|
PyThreadState_Swap( m_mainthread );
|
||||||
|
Py_Finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
// adds specified task along with provided collection of data to the work queue. returns true on success
|
||||||
TPythonInterpreter::killInstance() {
|
auto python_taskqueue::insert( task_request const &Task ) -> bool {
|
||||||
|
|
||||||
delete _instance;
|
if( ( Task.renderer.empty() )
|
||||||
}
|
|| ( Task.input == nullptr )
|
||||||
|
|| ( Task.target == null_handle ) ) { return false; }
|
||||||
|
|
||||||
bool TPythonInterpreter::loadClassFile( std::string const &lookupPath, std::string const &className )
|
auto *renderer { fetch_renderer( Task.renderer ) };
|
||||||
{
|
if( renderer == nullptr ) { return false; }
|
||||||
std::set<std::string>::const_iterator it = _classes.find(className);
|
// acquire a lock on the task queue and add a new task
|
||||||
if (it == _classes.end())
|
|
||||||
{
|
{
|
||||||
FILE *sourceFile = _getFile(lookupPath, className);
|
std::lock_guard<std::mutex> lock( m_tasks.mutex );
|
||||||
if (sourceFile != nullptr)
|
m_tasks.data.emplace_back( new render_task( renderer, Task.input, Task.target ) );
|
||||||
{
|
|
||||||
fseek(sourceFile, 0, SEEK_END);
|
|
||||||
auto const fsize = ftell(sourceFile);
|
|
||||||
char *buffer = (char *)calloc(fsize + 1, sizeof(char));
|
|
||||||
fseek(sourceFile, 0, SEEK_SET);
|
|
||||||
auto const freaded = fread(buffer, sizeof(char), fsize, sourceFile);
|
|
||||||
buffer[freaded] = 0; // z jakiegos powodu czytamy troche mniej i trzczeba dodac konczace
|
|
||||||
// zero do bufora (mimo ze calloc teoretycznie powiniene zwrocic
|
|
||||||
// wyzerowana pamiec)
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
char buf[255];
|
|
||||||
sprintf(buf, "readed %d / %d characters for %s", freaded, fsize, className);
|
|
||||||
WriteLog(buf);
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
fclose(sourceFile);
|
|
||||||
if (PyRun_SimpleString(buffer) != 0)
|
|
||||||
{
|
|
||||||
handleError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_classes.insert( className );
|
|
||||||
/*
|
|
||||||
char *classNameToRemember = (char *)calloc(strlen(className) + 1, sizeof(char));
|
|
||||||
strcpy(classNameToRemember, className);
|
|
||||||
_classes.insert(classNameToRemember);
|
|
||||||
*/
|
|
||||||
free(buffer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
// potentially wake a worker to handle the new task
|
||||||
|
m_condition.notify_one();
|
||||||
|
// all done
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *TPythonInterpreter::newClass( std::string const &className )
|
// executes python script stored in specified file. returns true on success
|
||||||
{
|
auto python_taskqueue::run_file( std::string const &File, std::string const &Path ) -> bool {
|
||||||
return newClass(className, NULL);
|
|
||||||
|
auto const lookup { FileExists( { Path + File, "python/local/" + File }, { ".py" } ) };
|
||||||
|
if( lookup.first.empty() ) { return false; }
|
||||||
|
|
||||||
|
std::ifstream inputfile { lookup.first + lookup.second };
|
||||||
|
std::string input;
|
||||||
|
input.assign( std::istreambuf_iterator<char>( inputfile ), std::istreambuf_iterator<char>() );
|
||||||
|
|
||||||
|
if( PyRun_SimpleString( input.c_str() ) != 0 ) {
|
||||||
|
error();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *TPythonInterpreter::_getFile( std::string const &lookupPath, std::string const &className )
|
auto python_taskqueue::fetch_renderer( std::string const Renderer ) ->PyObject * {
|
||||||
{
|
|
||||||
if( false == lookupPath.empty() ) {
|
|
||||||
std::string const sourcefilepath = lookupPath + className + ".py";
|
|
||||||
FILE *file = fopen( sourcefilepath.c_str(), "r" );
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
WriteLog( sourceFilePath );
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
if( nullptr != file ) { return file; }
|
|
||||||
}
|
|
||||||
std::string sourcefilepath = "python/local/" + className + ".py";
|
|
||||||
FILE *file = fopen( sourcefilepath.c_str(), "r" );
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
WriteLog( sourceFilePath );
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
return file; // either the file, or a nullptr on fail
|
|
||||||
/*
|
|
||||||
char *sourceFilePath;
|
|
||||||
if (lookupPath != NULL)
|
|
||||||
{
|
|
||||||
sourceFilePath = (char *)calloc(strlen(lookupPath) + strlen(className) + 4, sizeof(char));
|
|
||||||
strcat(sourceFilePath, lookupPath);
|
|
||||||
strcat(sourceFilePath, className);
|
|
||||||
strcat(sourceFilePath, ".py");
|
|
||||||
|
|
||||||
FILE *file = fopen(sourceFilePath, "r");
|
auto const lookup { m_renderers.find( Renderer ) };
|
||||||
#ifdef _PY_INT_MORE_LOG
|
if( lookup != std::end( m_renderers ) ) {
|
||||||
WriteLog(sourceFilePath);
|
return lookup->second;
|
||||||
#endif // _PY_INT_MORE_LOG
|
}
|
||||||
free(sourceFilePath);
|
// try to load specified renderer class
|
||||||
if (file != NULL)
|
auto const path { substr_path( Renderer ) };
|
||||||
{
|
auto const file { Renderer.substr( path.size() ) };
|
||||||
return file;
|
PyObject *renderer { nullptr };
|
||||||
|
if( m_main == nullptr ) {
|
||||||
|
ErrorLog( "Python Renderer: __main__ module is missing" );
|
||||||
|
goto cache_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyEval_AcquireLock();
|
||||||
|
{
|
||||||
|
PyObject *rendererarguments{ nullptr };
|
||||||
|
if( false == run_file( file, path ) ) {
|
||||||
|
goto cache_and_return;
|
||||||
|
}
|
||||||
|
auto *renderername{ PyObject_GetAttrString( m_main, file.c_str() ) };
|
||||||
|
if( renderername == nullptr ) {
|
||||||
|
ErrorLog( "Python Renderer: class \"" + file + "\" not defined" );
|
||||||
|
goto cache_and_return;
|
||||||
|
}
|
||||||
|
rendererarguments = Py_BuildValue( "(s)", path.c_str() );
|
||||||
|
if( rendererarguments == nullptr ) {
|
||||||
|
ErrorLog( "Python Renderer: failed to create initialization arguments" );
|
||||||
|
goto cache_and_return;
|
||||||
|
}
|
||||||
|
renderer = PyObject_CallObject( renderername, rendererarguments );
|
||||||
|
|
||||||
|
if( PyErr_Occurred() != nullptr ) {
|
||||||
|
error();
|
||||||
|
renderer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache_and_return:
|
||||||
|
// clean up after yourself
|
||||||
|
if( rendererarguments != nullptr ) {
|
||||||
|
Py_DECREF( rendererarguments );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
char *basePath = "python/local/";
|
PyEval_ReleaseLock();
|
||||||
sourceFilePath = (char *)calloc(strlen(basePath) + strlen(className) + 4, sizeof(char));
|
// cache the failures as well so we don't try again on subsequent requests
|
||||||
strcat(sourceFilePath, basePath);
|
m_renderers.emplace( Renderer, renderer );
|
||||||
strcat(sourceFilePath, className);
|
return renderer;
|
||||||
strcat(sourceFilePath, ".py");
|
|
||||||
|
|
||||||
FILE *file = fopen(sourceFilePath, "r");
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
WriteLog(sourceFilePath);
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
free(sourceFilePath);
|
|
||||||
if (file != NULL)
|
|
||||||
{
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TPythonInterpreter::handleError()
|
void python_taskqueue::run( GLFWwindow *Context, rendertask_sequence &Tasks, threading::condition_variable &Condition, std::atomic<bool> &Exit ) {
|
||||||
{
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
glfwMakeContextCurrent( Context );
|
||||||
WriteLog("Python Error occured");
|
// create a state object for this thread
|
||||||
#endif // _PY_INT_MORE_LOG
|
PyEval_AcquireLock();
|
||||||
if (_stdErr != NULL)
|
auto *threadstate { PyThreadState_New( m_mainthread->interp ) };
|
||||||
{ // std err pythona jest buforowane
|
PyEval_ReleaseLock();
|
||||||
|
|
||||||
|
render_task *task { nullptr };
|
||||||
|
|
||||||
|
while( false == Exit.load() ) {
|
||||||
|
// regardless of the reason we woke up prime the spurious wakeup flag for the next time
|
||||||
|
Condition.spurious( true );
|
||||||
|
// keep working as long as there's any scheduled tasks
|
||||||
|
do {
|
||||||
|
task = nullptr;
|
||||||
|
// acquire a lock on the task queue and potentially grab a task from it
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock( Tasks.mutex );
|
||||||
|
if( false == Tasks.data.empty() ) {
|
||||||
|
// fifo
|
||||||
|
task = Tasks.data.front();
|
||||||
|
Tasks.data.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( task != nullptr ) {
|
||||||
|
// swap in my thread state
|
||||||
|
PyEval_AcquireLock();
|
||||||
|
PyThreadState_Swap( threadstate );
|
||||||
|
// execute python code
|
||||||
|
task->run();
|
||||||
|
error();
|
||||||
|
// clear the thread state
|
||||||
|
PyThreadState_Swap( nullptr );
|
||||||
|
PyEval_ReleaseLock();
|
||||||
|
}
|
||||||
|
// TBD, TODO: add some idle time between tasks in case we're on a single thread cpu?
|
||||||
|
} while( task != nullptr );
|
||||||
|
// if there's nothing left to do wait until there is
|
||||||
|
// but check every now and then on your own to minimize potential deadlock situations
|
||||||
|
Condition.wait_for( std::chrono::seconds( 5 ) );
|
||||||
|
}
|
||||||
|
// clean up thread state data
|
||||||
|
PyEval_AcquireLock();
|
||||||
|
PyThreadState_Swap( nullptr );
|
||||||
|
PyThreadState_Clear( threadstate );
|
||||||
|
PyThreadState_Delete( threadstate );
|
||||||
|
PyEval_ReleaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
python_taskqueue::error() {
|
||||||
|
|
||||||
|
if( PyErr_Occurred() == nullptr ) { return; }
|
||||||
|
|
||||||
|
if( m_error != nullptr ) {
|
||||||
|
// std err pythona jest buforowane
|
||||||
PyErr_Print();
|
PyErr_Print();
|
||||||
PyObject *bufferContent = PyObject_CallMethod(_stdErr, "getvalue", NULL);
|
auto *errortext { PyObject_CallMethod( m_error, "getvalue", nullptr ) };
|
||||||
PyObject_CallMethod(_stdErr, "truncate", "i", 0); // czyscimy bufor na kolejne bledy
|
ErrorLog( PyString_AsString( errortext ) );
|
||||||
WriteLog(PyString_AsString(bufferContent));
|
// czyscimy bufor na kolejne bledy
|
||||||
|
PyObject_CallMethod( m_error, "truncate", "i", 0 );
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{ // nie dziala buffor pythona
|
// nie dziala buffor pythona
|
||||||
if (PyErr_Occurred() != NULL)
|
PyObject *type, *value, *traceback;
|
||||||
{
|
PyErr_Fetch( &type, &value, &traceback );
|
||||||
PyObject *ptype, *pvalue, *ptraceback;
|
if( type == nullptr ) {
|
||||||
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
|
ErrorLog( "Python Interpreter: don't know how to handle null exception" );
|
||||||
if (ptype == NULL)
|
|
||||||
{
|
|
||||||
WriteLog("Don't konw how to handle NULL exception");
|
|
||||||
}
|
|
||||||
PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
|
|
||||||
if (ptype == NULL)
|
|
||||||
{
|
|
||||||
WriteLog("Don't konw how to handle NULL exception");
|
|
||||||
}
|
|
||||||
PyObject *pStrType = PyObject_Str(ptype);
|
|
||||||
if (pStrType != NULL)
|
|
||||||
{
|
|
||||||
WriteLog(PyString_AsString(pStrType));
|
|
||||||
}
|
|
||||||
WriteLog(PyString_AsString(pvalue));
|
|
||||||
PyObject *pStrTraceback = PyObject_Str(ptraceback);
|
|
||||||
if (pStrTraceback != NULL)
|
|
||||||
{
|
|
||||||
WriteLog(PyString_AsString(pStrTraceback));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WriteLog("Python Traceback cannot be shown");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
PyErr_NormalizeException( &type, &value, &traceback );
|
||||||
{
|
if( type == nullptr ) {
|
||||||
#ifdef _PY_INT_MORE_LOG
|
ErrorLog( "Python Interpreter: don't know how to handle null exception" );
|
||||||
WriteLog("Called python error handler when no error occured!");
|
}
|
||||||
#endif // _PY_INT_MORE_LOG
|
auto *typetext { PyObject_Str( type ) };
|
||||||
|
if( typetext != nullptr ) {
|
||||||
|
ErrorLog( PyString_AsString( typetext ) );
|
||||||
|
}
|
||||||
|
if( value != nullptr ) {
|
||||||
|
ErrorLog( PyString_AsString( value ) );
|
||||||
|
}
|
||||||
|
auto *tracebacktext { PyObject_Str( traceback ) };
|
||||||
|
if( tracebacktext != nullptr ) {
|
||||||
|
WriteLog( PyString_AsString( tracebacktext ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
WriteLog( "Python Interpreter: failed to retrieve the stack traceback" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PyObject *TPythonInterpreter::newClass(std::string const &className, PyObject *argsTuple)
|
|
||||||
{
|
|
||||||
if (_main == NULL)
|
|
||||||
{
|
|
||||||
WriteLog("main turned into null");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
PyObject *classNameObj = PyObject_GetAttrString(_main, className.c_str());
|
|
||||||
if (classNameObj == NULL)
|
|
||||||
{
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
char buf[255];
|
|
||||||
sprintf(buf, "Python class %s not defined!", className);
|
|
||||||
WriteLog(buf);
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
PyObject *object = PyObject_CallObject(classNameObj, argsTuple);
|
|
||||||
|
|
||||||
if (PyErr_Occurred() != NULL)
|
|
||||||
{
|
|
||||||
handleError();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
TPythonScreenRenderer::TPythonScreenRenderer(int textureId, PyObject *renderer)
|
|
||||||
{
|
|
||||||
_textureId = textureId;
|
|
||||||
_pyRenderer = renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPythonScreenRenderer::updateTexture()
|
|
||||||
{
|
|
||||||
int width, height;
|
|
||||||
if (_pyWidth == NULL || _pyHeight == NULL)
|
|
||||||
{
|
|
||||||
WriteLog("Unknown python texture size!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
width = PyInt_AsLong(_pyWidth);
|
|
||||||
height = PyInt_AsLong(_pyHeight);
|
|
||||||
if (_pyTexture != NULL)
|
|
||||||
{
|
|
||||||
char *textureData = PyString_AsString(_pyTexture);
|
|
||||||
if (textureData != NULL)
|
|
||||||
{
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
char buff[255];
|
|
||||||
sprintf(buff, "Sending texture id: %d w: %d h: %d", _textureId, width, height);
|
|
||||||
WriteLog(buff);
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
/*
|
|
||||||
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
|
|
||||||
glPixelStorei( GL_PACK_ALIGNMENT, 1 );
|
|
||||||
*/
|
|
||||||
GfxRenderer.Bind_Material(_textureId);
|
|
||||||
// setup texture parameters
|
|
||||||
glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
|
|
||||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
|
||||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
|
|
||||||
glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0 );
|
|
||||||
// build texture
|
|
||||||
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData );
|
|
||||||
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
GLenum status = glGetError();
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case GL_INVALID_ENUM:
|
|
||||||
WriteLog("An unacceptable value is specified for an enumerated argument. The "
|
|
||||||
"offending function is ignored, having no side effect other than to set "
|
|
||||||
"the error flag.");
|
|
||||||
break;
|
|
||||||
case GL_INVALID_VALUE:
|
|
||||||
WriteLog("A numeric argument is out of range. The offending function is ignored, "
|
|
||||||
"having no side effect other than to set the error flag.");
|
|
||||||
break;
|
|
||||||
case GL_INVALID_OPERATION:
|
|
||||||
WriteLog("The specified operation is not allowed in the current state. The "
|
|
||||||
"offending function is ignored, having no side effect other than to set "
|
|
||||||
"the error flag.");
|
|
||||||
break;
|
|
||||||
case GL_NO_ERROR:
|
|
||||||
WriteLog("No error has been recorded. The value of this symbolic constant is "
|
|
||||||
"guaranteed to be zero.");
|
|
||||||
break;
|
|
||||||
case GL_STACK_OVERFLOW:
|
|
||||||
WriteLog("This function would cause a stack overflow. The offending function is "
|
|
||||||
"ignored, having no side effect other than to set the error flag.");
|
|
||||||
break;
|
|
||||||
case GL_STACK_UNDERFLOW:
|
|
||||||
WriteLog("This function would cause a stack underflow. The offending function is "
|
|
||||||
"ignored, having no side effect other than to set the error flag.");
|
|
||||||
break;
|
|
||||||
case GL_OUT_OF_MEMORY:
|
|
||||||
WriteLog("There is not enough memory left to execute the function. The state of "
|
|
||||||
"OpenGL is undefined, except for the state of the error flags, after this "
|
|
||||||
"error is recorded.");
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WriteLog("RAW python texture data is NULL!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WriteLog("Python texture object is NULL!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPythonScreenRenderer::render(PyObject *trainState)
|
|
||||||
{
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
WriteLog("Python rendering texture ...");
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
_pyTexture = PyObject_CallMethod(_pyRenderer, "render", "O", trainState);
|
|
||||||
|
|
||||||
if (_pyTexture == NULL)
|
|
||||||
{
|
|
||||||
TPythonInterpreter::getInstance()->handleError();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_pyWidth = PyObject_CallMethod(_pyRenderer, "get_width", NULL);
|
|
||||||
if (_pyWidth == NULL)
|
|
||||||
{
|
|
||||||
TPythonInterpreter::getInstance()->handleError();
|
|
||||||
}
|
|
||||||
_pyHeight = PyObject_CallMethod(_pyRenderer, "get_height", NULL);
|
|
||||||
if (_pyHeight == NULL)
|
|
||||||
{
|
|
||||||
TPythonInterpreter::getInstance()->handleError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TPythonScreenRenderer::~TPythonScreenRenderer()
|
|
||||||
{
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
WriteLog("PythonScreenRenderer descturctor called");
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
if (_pyRenderer != NULL)
|
|
||||||
{
|
|
||||||
Py_CLEAR(_pyRenderer);
|
|
||||||
}
|
|
||||||
cleanup();
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
WriteLog("PythonScreenRenderer desctructor finished");
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPythonScreenRenderer::cleanup()
|
|
||||||
{
|
|
||||||
if (_pyTexture != NULL)
|
|
||||||
{
|
|
||||||
Py_CLEAR(_pyTexture);
|
|
||||||
_pyTexture = NULL;
|
|
||||||
}
|
|
||||||
if (_pyWidth != NULL)
|
|
||||||
{
|
|
||||||
Py_CLEAR(_pyWidth);
|
|
||||||
_pyWidth = NULL;
|
|
||||||
}
|
|
||||||
if (_pyHeight != NULL)
|
|
||||||
{
|
|
||||||
Py_CLEAR(_pyHeight);
|
|
||||||
_pyHeight = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPythonScreens::reset(void *train)
|
|
||||||
{
|
|
||||||
_terminationFlag = true;
|
|
||||||
if (_thread != NULL)
|
|
||||||
{
|
|
||||||
// WriteLog("Awaiting python thread to end");
|
|
||||||
_thread->join();
|
|
||||||
delete _thread;
|
|
||||||
_thread = nullptr;
|
|
||||||
}
|
|
||||||
_terminationFlag = false;
|
|
||||||
_cleanupReadyFlag = false;
|
|
||||||
_renderReadyFlag = false;
|
|
||||||
for (std::vector<TPythonScreenRenderer *>::iterator i = _screens.begin(); i != _screens.end();
|
|
||||||
++i)
|
|
||||||
{
|
|
||||||
delete *i;
|
|
||||||
}
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
WriteLog("Clearing renderer vector");
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
_screens.clear();
|
|
||||||
_train = train;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPythonScreens::init(cParser &parser, TModel3d *model, std::string const &name, int const cab)
|
|
||||||
{
|
|
||||||
std::string asSubModelName, asPyClassName;
|
|
||||||
parser.getTokens( 2, false );
|
|
||||||
parser
|
|
||||||
>> asSubModelName
|
|
||||||
>> asPyClassName;
|
|
||||||
std::string subModelName = ToLower( asSubModelName );
|
|
||||||
std::string pyClassName = ToLower( asPyClassName );
|
|
||||||
TSubModel *subModel = model->GetFromName(subModelName);
|
|
||||||
if (subModel == NULL)
|
|
||||||
{
|
|
||||||
WriteLog( "Python Screen: submodel " + subModelName + " not found - Ignoring screen" );
|
|
||||||
return; // nie ma takiego sub modelu w danej kabinie pomijamy
|
|
||||||
}
|
|
||||||
auto textureId = subModel->GetMaterial();
|
|
||||||
if (textureId <= 0)
|
|
||||||
{
|
|
||||||
WriteLog( "Python Screen: invalid texture id " + std::to_string(textureId) + " - Ignoring screen" );
|
|
||||||
return; // sub model nie posiada tekstury lub tekstura wymienna - nie obslugiwana
|
|
||||||
}
|
|
||||||
TPythonInterpreter *python = TPythonInterpreter::getInstance();
|
|
||||||
python->loadClassFile(_lookupPath, pyClassName);
|
|
||||||
PyObject *args = Py_BuildValue("(ssi)", _lookupPath.c_str(), name.c_str(), cab);
|
|
||||||
if (args == NULL)
|
|
||||||
{
|
|
||||||
WriteLog("Python Screen: cannot create __init__ arguments");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PyObject *pyRenderer = python->newClass(pyClassName, args);
|
|
||||||
Py_CLEAR(args);
|
|
||||||
if (pyRenderer == NULL)
|
|
||||||
{
|
|
||||||
WriteLog( "Python Screen: null renderer for " + pyClassName + " - Ignoring screen" );
|
|
||||||
return; // nie mozna utworzyc obiektu Pythonowego
|
|
||||||
}
|
|
||||||
m_updaterate = Global.PythonScreenUpdateRate;
|
|
||||||
TPythonScreenRenderer *renderer = new TPythonScreenRenderer(textureId, pyRenderer);
|
|
||||||
_screens.push_back(renderer);
|
|
||||||
WriteLog( "Created python screen " + pyClassName + " on submodel " + subModelName + " (" + std::to_string(textureId) + ")" );
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPythonScreens::update()
|
|
||||||
{
|
|
||||||
if (!_renderReadyFlag)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_renderReadyFlag = false;
|
|
||||||
for (std::vector<TPythonScreenRenderer *>::iterator i = _screens.begin(); i != _screens.end();
|
|
||||||
++i)
|
|
||||||
{
|
|
||||||
(*i)->updateTexture();
|
|
||||||
}
|
|
||||||
_cleanupReadyFlag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPythonScreens::setLookupPath(std::string const &path)
|
|
||||||
{
|
|
||||||
_lookupPath = path;
|
|
||||||
replace_slashes( _lookupPath );
|
|
||||||
}
|
|
||||||
|
|
||||||
TPythonScreens::TPythonScreens()
|
|
||||||
{
|
|
||||||
TPythonInterpreter::getInstance()->loadClassFile("", "abstractscreenrenderer");
|
|
||||||
}
|
|
||||||
|
|
||||||
TPythonScreens::~TPythonScreens()
|
|
||||||
{
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
WriteLog("Called python sceeens destructor");
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
reset(NULL);
|
|
||||||
/*
|
|
||||||
if (_lookupPath != NULL)
|
|
||||||
{
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
WriteLog("Freeing lookup path");
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
free(_lookupPath);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPythonScreens::run()
|
|
||||||
{
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
m_updatestopwatch.start();
|
|
||||||
if (_terminationFlag)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
TTrain *train = (TTrain *)_train;
|
|
||||||
_trainState = train->GetTrainState();
|
|
||||||
if (_terminationFlag)
|
|
||||||
{
|
|
||||||
_freeTrainState();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (std::vector<TPythonScreenRenderer *>::iterator i = _screens.begin();
|
|
||||||
i != _screens.end(); ++i)
|
|
||||||
{
|
|
||||||
(*i)->render(_trainState);
|
|
||||||
}
|
|
||||||
_freeTrainState();
|
|
||||||
if (_terminationFlag)
|
|
||||||
{
|
|
||||||
_cleanup();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_renderReadyFlag = true;
|
|
||||||
m_updatestopwatch.stop();
|
|
||||||
while (!_cleanupReadyFlag && !_terminationFlag)
|
|
||||||
{
|
|
||||||
auto const sleeptime {
|
|
||||||
std::max(
|
|
||||||
100,
|
|
||||||
m_updaterate - static_cast<int>( m_updatestopwatch.average() ) ) };
|
|
||||||
#ifdef _WIN32
|
|
||||||
Sleep( sleeptime );
|
|
||||||
#elif __linux__
|
|
||||||
usleep( sleeptime * 1000 );
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if (_terminationFlag)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_cleanup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPythonScreens::finish()
|
|
||||||
{
|
|
||||||
// nothing to do here, proper clean up takes place afterwards
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScreenRendererThread(TPythonScreens* renderer)
|
|
||||||
{
|
|
||||||
renderer->run();
|
|
||||||
renderer->finish();
|
|
||||||
#ifdef _PY_INT_MORE_LOG
|
|
||||||
WriteLog("Python Screen Renderer Thread Ends");
|
|
||||||
#endif // _PY_INT_MORE_LOG
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPythonScreens::start()
|
|
||||||
{
|
|
||||||
if (_screens.size() > 0)
|
|
||||||
{
|
|
||||||
_thread = new std::thread(ScreenRendererThread, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPythonScreens::_cleanup()
|
|
||||||
{
|
|
||||||
_cleanupReadyFlag = false;
|
|
||||||
for (std::vector<TPythonScreenRenderer *>::iterator i = _screens.begin(); i != _screens.end();
|
|
||||||
++i)
|
|
||||||
{
|
|
||||||
(*i)->cleanup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TPythonScreens::_freeTrainState()
|
|
||||||
{
|
|
||||||
if (_trainState != NULL)
|
|
||||||
{
|
|
||||||
PyDict_Clear(_trainState);
|
|
||||||
Py_CLEAR(_trainState);
|
|
||||||
_trainState = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
137
PyInt.h
137
PyInt.h
@@ -1,13 +1,13 @@
|
|||||||
#ifndef PyIntH
|
/*
|
||||||
#define PyIntH
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifdef _POSIX_C_SOURCE
|
#pragma once
|
||||||
#undef _POSIX_C_SOURCE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _XOPEN_SOURCE
|
|
||||||
#undef _XOPEN_SOURCE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
#undef _DEBUG // bez tego macra Py_DECREF powoduja problemy przy linkowaniu
|
#undef _DEBUG // bez tego macra Py_DECREF powoduja problemy przy linkowaniu
|
||||||
@@ -17,78 +17,69 @@
|
|||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#endif
|
#endif
|
||||||
#include "Classes.h"
|
#include "Classes.h"
|
||||||
#include "timer.h"
|
|
||||||
|
|
||||||
#define PyGetFloat(param) PyFloat_FromDouble(param >= 0 ? param : -param)
|
#define PyGetFloat(param) PyFloat_FromDouble(param >= 0 ? param : -param)
|
||||||
#define PyGetFloatS(param) PyFloat_FromDouble(param)
|
#define PyGetFloatS(param) PyFloat_FromDouble(param)
|
||||||
#define PyGetInt(param) PyInt_FromLong(param)
|
#define PyGetInt(param) PyInt_FromLong(param)
|
||||||
#define PyGetFloatS(param) PyFloat_FromDouble(param)
|
|
||||||
#define PyGetBool(param) param ? Py_True : Py_False
|
#define PyGetBool(param) param ? Py_True : Py_False
|
||||||
#define PyGetString(param) PyString_FromString(param)
|
#define PyGetString(param) PyString_FromString(param)
|
||||||
|
|
||||||
class TPythonInterpreter
|
// TODO: extract common base and inherit specialization from it
|
||||||
{
|
class render_task {
|
||||||
protected:
|
|
||||||
TPythonInterpreter();
|
|
||||||
~TPythonInterpreter() {}
|
|
||||||
static TPythonInterpreter *_instance;
|
|
||||||
std::set<std::string> _classes;
|
|
||||||
PyObject *_main;
|
|
||||||
PyObject *_stdErr;
|
|
||||||
FILE *_getFile( std::string const &lookupPath, std::string const &className );
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static TPythonInterpreter *getInstance();
|
// constructors
|
||||||
static void killInstance();
|
render_task( PyObject *Renderer, PyObject *Input, material_handle Target ) :
|
||||||
bool loadClassFile( std::string const &lookupPath, std::string const &className );
|
m_renderer( Renderer ), m_input( Input ), m_target( Target )
|
||||||
PyObject *newClass( std::string const &className );
|
{}
|
||||||
PyObject *newClass( std::string const &className, PyObject *argsTuple );
|
// methods
|
||||||
void handleError();
|
|
||||||
};
|
|
||||||
|
|
||||||
class TPythonScreenRenderer
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
PyObject *_pyRenderer;
|
|
||||||
PyObject *_pyTexture;
|
|
||||||
int _textureId;
|
|
||||||
PyObject *_pyWidth;
|
|
||||||
PyObject *_pyHeight;
|
|
||||||
|
|
||||||
public:
|
|
||||||
TPythonScreenRenderer(int textureId, PyObject *renderer);
|
|
||||||
~TPythonScreenRenderer();
|
|
||||||
void render(PyObject *trainState);
|
|
||||||
void cleanup();
|
|
||||||
void updateTexture();
|
|
||||||
};
|
|
||||||
|
|
||||||
class TPythonScreens
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
bool _cleanupReadyFlag{ false };
|
|
||||||
bool _renderReadyFlag{ false };
|
|
||||||
bool _terminationFlag{ false };
|
|
||||||
std::thread *_thread{ nullptr };
|
|
||||||
std::vector<TPythonScreenRenderer *> _screens;
|
|
||||||
std::string _lookupPath;
|
|
||||||
void *_train;
|
|
||||||
void _cleanup();
|
|
||||||
void _freeTrainState();
|
|
||||||
PyObject *_trainState;
|
|
||||||
int m_updaterate { 200 };
|
|
||||||
Timer::stopwatch m_updatestopwatch;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void reset(void *train);
|
|
||||||
void setLookupPath(std::string const &path);
|
|
||||||
void init(cParser &parser, TModel3d *model, std::string const &name, int const cab);
|
|
||||||
void update();
|
|
||||||
TPythonScreens();
|
|
||||||
~TPythonScreens();
|
|
||||||
void run();
|
void run();
|
||||||
void start();
|
|
||||||
void finish();
|
private:
|
||||||
|
// members
|
||||||
|
PyObject *m_renderer {nullptr};
|
||||||
|
PyObject *m_input { nullptr };
|
||||||
|
material_handle m_target { null_handle };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PyIntH
|
class python_taskqueue {
|
||||||
|
|
||||||
|
public:
|
||||||
|
// types
|
||||||
|
struct task_request {
|
||||||
|
|
||||||
|
std::string const &renderer;
|
||||||
|
PyObject *input;
|
||||||
|
material_handle target;
|
||||||
|
};
|
||||||
|
// constructors
|
||||||
|
python_taskqueue() = default;
|
||||||
|
// methods
|
||||||
|
// initializes the module. returns true on success
|
||||||
|
auto init() -> bool;
|
||||||
|
// shuts down the module
|
||||||
|
void exit();
|
||||||
|
// adds specified task along with provided collection of data to the work queue. returns true on success
|
||||||
|
auto insert( task_request const &Task ) -> bool;
|
||||||
|
// executes python script stored in specified file. returns true on success
|
||||||
|
auto run_file( std::string const &File, std::string const &Path = "" ) -> bool;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// types
|
||||||
|
static int const WORKERCOUNT { 1 };
|
||||||
|
using worker_array = std::array<std::unique_ptr<std::thread>, WORKERCOUNT >;
|
||||||
|
using rendertask_sequence = threading::lockable< std::deque<render_task *> >;
|
||||||
|
// methods
|
||||||
|
auto fetch_renderer( std::string const Renderer ) -> PyObject *;
|
||||||
|
void run( GLFWwindow *Context, rendertask_sequence &Tasks, threading::condition_variable &Condition, std::atomic<bool> &Exit );
|
||||||
|
void error();
|
||||||
|
// members
|
||||||
|
PyObject *m_main { nullptr };
|
||||||
|
PyObject *m_error { nullptr };
|
||||||
|
PyThreadState *m_mainthread{ nullptr };
|
||||||
|
worker_array m_workers;
|
||||||
|
threading::condition_variable m_condition; // wakes up the workers
|
||||||
|
std::atomic<bool> m_exit { false }; // signals the workers to quit
|
||||||
|
std::unordered_map<std::string, PyObject *> m_renderers; // cache of python classes
|
||||||
|
rendertask_sequence m_tasks;
|
||||||
|
};
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
int const null_handle = 0;
|
|
||||||
|
|
||||||
enum class resource_state {
|
enum class resource_state {
|
||||||
none,
|
none,
|
||||||
loading,
|
loading,
|
||||||
|
|||||||
51
Train.cpp
51
Train.cpp
@@ -28,6 +28,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include "dynobj.h"
|
#include "dynobj.h"
|
||||||
#include "mtable.h"
|
#include "mtable.h"
|
||||||
#include "Console.h"
|
#include "Console.h"
|
||||||
|
#include "application.h"
|
||||||
|
|
||||||
namespace input {
|
namespace input {
|
||||||
|
|
||||||
@@ -449,6 +450,7 @@ PyObject *TTrain::GetTrainState() {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyDict_SetItemString( dict, "name", PyGetString( DynamicObject->asName.c_str() ) );
|
||||||
PyDict_SetItemString( dict, "cab", PyGetInt( mover->ActiveCab ) );
|
PyDict_SetItemString( dict, "cab", PyGetInt( mover->ActiveCab ) );
|
||||||
// basic systems state data
|
// basic systems state data
|
||||||
PyDict_SetItemString( dict, "battery", PyGetBool( mvControlled->Battery ) );
|
PyDict_SetItemString( dict, "battery", PyGetBool( mvControlled->Battery ) );
|
||||||
@@ -469,7 +471,7 @@ PyObject *TTrain::GetTrainState() {
|
|||||||
PyDict_SetItemString( dict, "dir_brake", PyGetBool( bEP ) );
|
PyDict_SetItemString( dict, "dir_brake", PyGetBool( bEP ) );
|
||||||
bool bPN;
|
bool bPN;
|
||||||
if( ( typeid( *mvControlled->Hamulec ) == typeid( TLSt ) )
|
if( ( typeid( *mvControlled->Hamulec ) == typeid( TLSt ) )
|
||||||
|| ( typeid( *mvControlled->Hamulec ) == typeid( TEStED ) ) ) {
|
|| ( typeid( *mvControlled->Hamulec ) == typeid( TEStED ) ) ) {
|
||||||
|
|
||||||
TBrake* temp_ham = mvControlled->Hamulec.get();
|
TBrake* temp_ham = mvControlled->Hamulec.get();
|
||||||
bPN = ( static_cast<TLSt*>( temp_ham )->GetEDBCP() > 0.2 );
|
bPN = ( static_cast<TLSt*>( temp_ham )->GetEDBCP() > 0.2 );
|
||||||
@@ -4682,7 +4684,6 @@ bool TTrain::Update( double const Deltatime )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tor = DynamicObject->GetTrack(); // McZapkie-180203
|
|
||||||
// McZapkie: predkosc wyswietlana na tachometrze brana jest z obrotow kol
|
// McZapkie: predkosc wyswietlana na tachometrze brana jest z obrotow kol
|
||||||
auto const maxtacho { 3.0 };
|
auto const maxtacho { 3.0 };
|
||||||
fTachoVelocity = static_cast<float>( std::min( std::abs(11.31 * mvControlled->WheelDiameter * mvControlled->nrot), mvControlled->Vmax * 1.05) );
|
fTachoVelocity = static_cast<float>( std::min( std::abs(11.31 * mvControlled->WheelDiameter * mvControlled->nrot), mvControlled->Vmax * 1.05) );
|
||||||
@@ -5606,7 +5607,6 @@ bool TTrain::Update( double const Deltatime )
|
|||||||
ggFuelPumpButton.Update();
|
ggFuelPumpButton.Update();
|
||||||
ggOilPumpButton.Update();
|
ggOilPumpButton.Update();
|
||||||
//------
|
//------
|
||||||
pyScreens.update();
|
|
||||||
}
|
}
|
||||||
// wyprowadzenie sygnałów dla haslera na PoKeys (zaznaczanie na taśmie)
|
// wyprowadzenie sygnałów dla haslera na PoKeys (zaznaczanie na taśmie)
|
||||||
btHaslerBrakes.Turn(DynamicObject->MoverParameters->BrakePress > 0.4); // ciśnienie w cylindrach
|
btHaslerBrakes.Turn(DynamicObject->MoverParameters->BrakePress > 0.4); // ciśnienie w cylindrach
|
||||||
@@ -5676,8 +5676,8 @@ bool TTrain::Update( double const Deltatime )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
// NOTE: disabled while switch state isn't preserved while moving between compartments
|
|
||||||
// check whether we should raise the pantographs, based on volume in pantograph tank
|
// check whether we should raise the pantographs, based on volume in pantograph tank
|
||||||
|
// NOTE: disabled while switch state isn't preserved while moving between compartments
|
||||||
if( mvControlled->PantPress > (
|
if( mvControlled->PantPress > (
|
||||||
mvControlled->TrainType == dt_EZT ?
|
mvControlled->TrainType == dt_EZT ?
|
||||||
2.4 :
|
2.4 :
|
||||||
@@ -5692,7 +5692,15 @@ bool TTrain::Update( double const Deltatime )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
// screens
|
||||||
|
fScreenTimer += Deltatime;
|
||||||
|
if( ( fScreenTimer > Global.PythonScreenUpdateRate * 0.001f )
|
||||||
|
&& ( false == FreeFlyModeFlag ) ) { // don't bother if we're outside
|
||||||
|
fScreenTimer = 0.f;
|
||||||
|
for( auto const &screen : m_screens ) {
|
||||||
|
Application.request( { screen.first, GetTrainState(), screen.second } );
|
||||||
|
}
|
||||||
|
}
|
||||||
// sounds
|
// sounds
|
||||||
update_sounds( Deltatime );
|
update_sounds( Deltatime );
|
||||||
|
|
||||||
@@ -5853,7 +5861,6 @@ TTrain::update_sounds( double const Deltatime ) {
|
|||||||
dsbSlipAlarm.stop();
|
dsbSlipAlarm.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
// szum w czasie jazdy
|
// szum w czasie jazdy
|
||||||
if( ( false == FreeFlyModeFlag )
|
if( ( false == FreeFlyModeFlag )
|
||||||
&& ( false == Global.CabWindowOpen )
|
&& ( false == Global.CabWindowOpen )
|
||||||
@@ -5865,7 +5872,6 @@ TTrain::update_sounds( double const Deltatime ) {
|
|||||||
// don't play the optional ending sound if the listener switches views
|
// don't play the optional ending sound if the listener switches views
|
||||||
rsRunningNoise.stop( true == FreeFlyModeFlag );
|
rsRunningNoise.stop( true == FreeFlyModeFlag );
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
// hunting oscillation noise
|
// hunting oscillation noise
|
||||||
if( ( false == FreeFlyModeFlag )
|
if( ( false == FreeFlyModeFlag )
|
||||||
&& ( false == Global.CabWindowOpen )
|
&& ( false == Global.CabWindowOpen )
|
||||||
@@ -6255,6 +6261,8 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName)
|
|||||||
bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
|
bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
|
||||||
{
|
{
|
||||||
m_controlmapper.clear();
|
m_controlmapper.clear();
|
||||||
|
// clear python screens
|
||||||
|
m_screens.clear();
|
||||||
// reset sound positions and owner
|
// reset sound positions and owner
|
||||||
auto const nullvector { glm::vec3() };
|
auto const nullvector { glm::vec3() };
|
||||||
std::vector<sound_source *> sounds = {
|
std::vector<sound_source *> sounds = {
|
||||||
@@ -6272,9 +6280,6 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
|
|||||||
pMechViewAngle = { 0.0, 0.0 };
|
pMechViewAngle = { 0.0, 0.0 };
|
||||||
Global.pCamera.Pitch = pMechViewAngle.x;
|
Global.pCamera.Pitch = pMechViewAngle.x;
|
||||||
Global.pCamera.Yaw = pMechViewAngle.y;
|
Global.pCamera.Yaw = pMechViewAngle.y;
|
||||||
|
|
||||||
pyScreens.reset(this);
|
|
||||||
pyScreens.setLookupPath(DynamicObject->asBaseDir);
|
|
||||||
bool parse = false;
|
bool parse = false;
|
||||||
int cabindex = 0;
|
int cabindex = 0;
|
||||||
DynamicObject->mdKabina = NULL; // likwidacja wskaźnika na dotychczasową kabinę
|
DynamicObject->mdKabina = NULL; // likwidacja wskaźnika na dotychczasową kabinę
|
||||||
@@ -6469,9 +6474,32 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
|
|||||||
// matched the token, grab the next one
|
// matched the token, grab the next one
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// TODO: add "pydestination:"
|
||||||
else if (token == "pyscreen:")
|
else if (token == "pyscreen:")
|
||||||
{
|
{
|
||||||
pyScreens.init(parser, DynamicObject->mdKabina, DynamicObject->name(), NewCabNo);
|
std::string submodelname, renderername;
|
||||||
|
parser.getTokens( 2 );
|
||||||
|
parser
|
||||||
|
>> submodelname
|
||||||
|
>> renderername;
|
||||||
|
|
||||||
|
auto const *submodel { DynamicObject->mdKabina->GetFromName( submodelname ) };
|
||||||
|
if( submodel == nullptr ) {
|
||||||
|
WriteLog( "Python Screen: submodel " + submodelname + " not found - Ignoring screen" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto const material { submodel->GetMaterial() };
|
||||||
|
if( material <= 0 ) {
|
||||||
|
// sub model nie posiada tekstury lub tekstura wymienna - nie obslugiwana
|
||||||
|
WriteLog( "Python Screen: invalid texture id " + std::to_string( material ) + " - Ignoring screen" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// record renderer and material binding for future update requests
|
||||||
|
m_screens.emplace_back(
|
||||||
|
( substr_path(renderername).empty() ? // supply vehicle folder as path if none is provided
|
||||||
|
DynamicObject->asBaseDir + renderername :
|
||||||
|
renderername ),
|
||||||
|
material );
|
||||||
}
|
}
|
||||||
// btLampkaUnknown.Init("unknown",mdKabina,false);
|
// btLampkaUnknown.Init("unknown",mdKabina,false);
|
||||||
} while (token != "");
|
} while (token != "");
|
||||||
@@ -6480,7 +6508,6 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
pyScreens.start();
|
|
||||||
if (DynamicObject->mdKabina)
|
if (DynamicObject->mdKabina)
|
||||||
{
|
{
|
||||||
// configure placement of sound emitters which aren't bound with any device model, and weren't placed manually
|
// configure placement of sound emitters which aren't bound with any device model, and weren't placed manually
|
||||||
|
|||||||
8
Train.h
8
Train.h
@@ -628,12 +628,10 @@ private:
|
|||||||
float fConverterTimer; // hunter-261211: dla przekaznika
|
float fConverterTimer; // hunter-261211: dla przekaznika
|
||||||
float fMainRelayTimer; // hunter-141211: zalaczanie WSa z opoznieniem
|
float fMainRelayTimer; // hunter-141211: zalaczanie WSa z opoznieniem
|
||||||
float fCzuwakTestTimer; // hunter-091012: do testu czuwaka
|
float fCzuwakTestTimer; // hunter-091012: do testu czuwaka
|
||||||
float fLightsTimer; // yB 150617: timer do swiatel
|
float fScreenTimer { 0.f };
|
||||||
|
|
||||||
bool CAflag { false }; // hunter-131211: dla osobnego zbijania CA i SHP
|
bool CAflag { false }; // hunter-131211: dla osobnego zbijania CA i SHP
|
||||||
|
|
||||||
double fPoslizgTimer;
|
|
||||||
TTrack *tor;
|
|
||||||
// McZapkie-240302 - przyda sie do tachometru
|
// McZapkie-240302 - przyda sie do tachometru
|
||||||
float fTachoVelocity{ 0.0f };
|
float fTachoVelocity{ 0.0f };
|
||||||
float fTachoVelocityJump{ 0.0f }; // ze skakaniem
|
float fTachoVelocityJump{ 0.0f }; // ze skakaniem
|
||||||
@@ -659,10 +657,8 @@ private:
|
|||||||
bool bHeat[8]; // grzanie
|
bool bHeat[8]; // grzanie
|
||||||
// McZapkie: do syczenia
|
// McZapkie: do syczenia
|
||||||
float fPPress, fNPress;
|
float fPPress, fNPress;
|
||||||
// float fSPPress, fSNPress;
|
|
||||||
int iSekunda; // Ra: sekunda aktualizacji pr?dko?ci
|
|
||||||
int iRadioChannel { 1 }; // numer aktualnego kana?u radiowego
|
int iRadioChannel { 1 }; // numer aktualnego kana?u radiowego
|
||||||
TPythonScreens pyScreens;
|
std::vector<std::pair<std::string, material_handle>> m_screens;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
float fPress[20][3]; // cisnienia dla wszystkich czlonow
|
float fPress[20][3]; // cisnienia dla wszystkich czlonow
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "simulation.h"
|
#include "simulation.h"
|
||||||
#include "train.h"
|
#include "train.h"
|
||||||
#include "pyint.h"
|
|
||||||
#include "sceneeditor.h"
|
#include "sceneeditor.h"
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
#include "uilayer.h"
|
#include "uilayer.h"
|
||||||
@@ -129,6 +128,7 @@ eu07_application::init( int Argc, char *Argv[] ) {
|
|||||||
if( ( result = init_audio() ) != 0 ) {
|
if( ( result = init_audio() ) != 0 ) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
m_taskqueue.init();
|
||||||
if( ( result = init_modes() ) != 0 ) {
|
if( ( result = init_modes() ) != 0 ) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,7 @@ int
|
|||||||
eu07_application::run() {
|
eu07_application::run() {
|
||||||
|
|
||||||
// main application loop
|
// main application loop
|
||||||
while( ( false == glfwWindowShouldClose( m_window ) )
|
while( ( false == glfwWindowShouldClose( m_windows.front() ) )
|
||||||
&& ( false == m_modestack.empty() )
|
&& ( false == m_modestack.empty() )
|
||||||
&& ( true == m_modes[ m_modestack.top() ]->update() )
|
&& ( true == m_modes[ m_modestack.top() ]->update() )
|
||||||
&& ( true == GfxRenderer.Render() ) ) {
|
&& ( true == GfxRenderer.Render() ) ) {
|
||||||
@@ -151,6 +151,12 @@ eu07_application::run() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
eu07_application::request( python_taskqueue::task_request const &Task ) {
|
||||||
|
|
||||||
|
return m_taskqueue.insert( Task );
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
eu07_application::exit() {
|
eu07_application::exit() {
|
||||||
|
|
||||||
@@ -159,10 +165,11 @@ eu07_application::exit() {
|
|||||||
|
|
||||||
ui_layer::shutdown();
|
ui_layer::shutdown();
|
||||||
|
|
||||||
glfwDestroyWindow( m_window );
|
for( auto *window : m_windows ) {
|
||||||
|
glfwDestroyWindow( window );
|
||||||
|
}
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
|
m_taskqueue.exit();
|
||||||
TPythonInterpreter::killInstance();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -197,7 +204,7 @@ eu07_application::push_mode( eu07_application::mode const Mode ) {
|
|||||||
void
|
void
|
||||||
eu07_application::set_title( std::string const &Title ) {
|
eu07_application::set_title( std::string const &Title ) {
|
||||||
|
|
||||||
glfwSetWindowTitle( m_window, Title.c_str() );
|
glfwSetWindowTitle( m_windows.front(), Title.c_str() );
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -217,17 +224,13 @@ eu07_application::set_cursor( int const Mode ) {
|
|||||||
void
|
void
|
||||||
eu07_application::set_cursor_pos( double const Horizontal, double const Vertical ) {
|
eu07_application::set_cursor_pos( double const Horizontal, double const Vertical ) {
|
||||||
|
|
||||||
if( m_window != nullptr ) {
|
glfwSetCursorPos( m_windows.front(), Horizontal, Vertical );
|
||||||
glfwSetCursorPos( m_window, Horizontal, Vertical );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
eu07_application::get_cursor_pos( double &Horizontal, double &Vertical ) const {
|
eu07_application::get_cursor_pos( double &Horizontal, double &Vertical ) const {
|
||||||
|
|
||||||
if( m_window != nullptr ) {
|
glfwGetCursorPos( m_windows.front(), &Horizontal, &Vertical );
|
||||||
glfwGetCursorPos( m_window, &Horizontal, &Vertical );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -262,6 +265,24 @@ eu07_application::on_scroll( double const Xoffset, double const Yoffset ) {
|
|||||||
m_modes[ m_modestack.top() ]->on_scroll( Xoffset, Yoffset );
|
m_modes[ m_modestack.top() ]->on_scroll( Xoffset, Yoffset );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLFWwindow *
|
||||||
|
eu07_application::window( int const Windowindex ) {
|
||||||
|
|
||||||
|
if( Windowindex >= 0 ) {
|
||||||
|
return (
|
||||||
|
Windowindex < m_windows.size() ?
|
||||||
|
m_windows[ Windowindex ] :
|
||||||
|
nullptr );
|
||||||
|
}
|
||||||
|
// for index -1 create a new child window
|
||||||
|
glfwWindowHint( GLFW_VISIBLE, GL_FALSE );
|
||||||
|
auto *childwindow = glfwCreateWindow( 1, 1, "eu07helper", nullptr, m_windows.front() );
|
||||||
|
if( childwindow != nullptr ) {
|
||||||
|
m_windows.emplace_back( childwindow );
|
||||||
|
}
|
||||||
|
return childwindow;
|
||||||
|
}
|
||||||
|
|
||||||
// private:
|
// private:
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -433,7 +454,7 @@ eu07_application::init_glfw() {
|
|||||||
// switch off the topmost flag
|
// switch off the topmost flag
|
||||||
::SetWindowPos( Hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
|
::SetWindowPos( Hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
|
||||||
#endif
|
#endif
|
||||||
m_window = window;
|
m_windows.emplace_back( window );
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -441,16 +462,17 @@ eu07_application::init_glfw() {
|
|||||||
void
|
void
|
||||||
eu07_application::init_callbacks() {
|
eu07_application::init_callbacks() {
|
||||||
|
|
||||||
glfwSetFramebufferSizeCallback( m_window, window_resize_callback );
|
auto *window { m_windows.front() };
|
||||||
glfwSetCursorPosCallback( m_window, cursor_pos_callback );
|
glfwSetFramebufferSizeCallback( window, window_resize_callback );
|
||||||
glfwSetMouseButtonCallback( m_window, mouse_button_callback );
|
glfwSetCursorPosCallback( window, cursor_pos_callback );
|
||||||
glfwSetKeyCallback( m_window, key_callback );
|
glfwSetMouseButtonCallback( window, mouse_button_callback );
|
||||||
glfwSetScrollCallback( m_window, scroll_callback );
|
glfwSetKeyCallback( window, key_callback );
|
||||||
glfwSetWindowFocusCallback( m_window, focus_callback );
|
glfwSetScrollCallback( window, scroll_callback );
|
||||||
|
glfwSetWindowFocusCallback( window, focus_callback );
|
||||||
{
|
{
|
||||||
int width, height;
|
int width, height;
|
||||||
glfwGetFramebufferSize( m_window, &width, &height );
|
glfwGetFramebufferSize( window, &width, &height );
|
||||||
window_resize_callback( m_window, width, height );
|
window_resize_callback( window, width, height );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,8 +484,8 @@ eu07_application::init_gfx() {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ( false == GfxRenderer.Init( m_window ) )
|
if( ( false == GfxRenderer.Init( m_windows.front() ) )
|
||||||
|| ( false == ui_layer::init( m_window ) ) ) {
|
|| ( false == ui_layer::init( m_windows.front() ) ) ) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "applicationmode.h"
|
#include "applicationmode.h"
|
||||||
|
#include "pyint.h"
|
||||||
|
|
||||||
class eu07_application {
|
class eu07_application {
|
||||||
|
|
||||||
@@ -29,6 +30,8 @@ public:
|
|||||||
init( int Argc, char *Argv[] );
|
init( int Argc, char *Argv[] );
|
||||||
int
|
int
|
||||||
run();
|
run();
|
||||||
|
bool
|
||||||
|
request( python_taskqueue::task_request const &Task );
|
||||||
void
|
void
|
||||||
exit();
|
exit();
|
||||||
void
|
void
|
||||||
@@ -57,10 +60,9 @@ public:
|
|||||||
on_mouse_button( int const Button, int const Action, int const Mods );
|
on_mouse_button( int const Button, int const Action, int const Mods );
|
||||||
void
|
void
|
||||||
on_scroll( double const Xoffset, double const Yoffset );
|
on_scroll( double const Xoffset, double const Yoffset );
|
||||||
inline
|
// gives access to specified window, creates a new window if index == -1
|
||||||
GLFWwindow *
|
GLFWwindow *
|
||||||
window() {
|
window( int const Windowindex = 0 );
|
||||||
return m_window; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// types
|
// types
|
||||||
@@ -77,9 +79,10 @@ private:
|
|||||||
int init_audio();
|
int init_audio();
|
||||||
int init_modes();
|
int init_modes();
|
||||||
// members
|
// members
|
||||||
GLFWwindow * m_window { nullptr };
|
|
||||||
modeptr_array m_modes { nullptr }; // collection of available application behaviour modes
|
modeptr_array m_modes { nullptr }; // collection of available application behaviour modes
|
||||||
mode_stack m_modestack; // current behaviour mode
|
mode_stack m_modestack; // current behaviour mode
|
||||||
|
python_taskqueue m_taskqueue;
|
||||||
|
std::vector<GLFWwindow *> m_windows;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern eu07_application Application;
|
extern eu07_application Application;
|
||||||
|
|||||||
2
stdafx.h
2
stdafx.h
@@ -103,6 +103,8 @@
|
|||||||
#include <glm/gtx/rotate_vector.hpp>
|
#include <glm/gtx/rotate_vector.hpp>
|
||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
|
int const null_handle = 0;
|
||||||
|
|
||||||
#include "openglmatrixstack.h"
|
#include "openglmatrixstack.h"
|
||||||
#include "openglcolor.h"
|
#include "openglcolor.h"
|
||||||
|
|
||||||
|
|||||||
57
utilities.h
57
utilities.h
@@ -313,4 +313,61 @@ glm::dvec3 LoadPoint( class cParser &Input );
|
|||||||
std::string
|
std::string
|
||||||
deserialize_random_set( cParser &Input, char const *Break = "\n\r\t ;" );
|
deserialize_random_set( cParser &Input, char const *Break = "\n\r\t ;" );
|
||||||
|
|
||||||
|
namespace threading {
|
||||||
|
|
||||||
|
// simple POD pairing of a data item and a mutex
|
||||||
|
// NOTE: doesn't do any locking itself, it's merely for cleaner argument arrangement and passing
|
||||||
|
template <typename Type_>
|
||||||
|
struct lockable {
|
||||||
|
|
||||||
|
Type_ data;
|
||||||
|
std::mutex mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
// basic wrapper simplifying use of std::condition_variable for most typical cases.
|
||||||
|
// has its own mutex and secondary variable to ignore spurious wakeups
|
||||||
|
class condition_variable {
|
||||||
|
|
||||||
|
public:
|
||||||
|
// methods
|
||||||
|
void
|
||||||
|
wait() {
|
||||||
|
std::unique_lock<std::mutex> lock( m_mutex );
|
||||||
|
m_condition.wait(
|
||||||
|
lock,
|
||||||
|
[ this ]() {
|
||||||
|
return m_spurious == false; } ); }
|
||||||
|
template< class Rep_, class Period_ >
|
||||||
|
void
|
||||||
|
wait_for( const std::chrono::duration<Rep_, Period_> &Time ) {
|
||||||
|
std::unique_lock<std::mutex> lock( m_mutex );
|
||||||
|
m_condition.wait_for(
|
||||||
|
lock,
|
||||||
|
Time,
|
||||||
|
[ this ]() {
|
||||||
|
return m_spurious == false; } ); }
|
||||||
|
void
|
||||||
|
notify_one() {
|
||||||
|
spurious( false );
|
||||||
|
m_condition.notify_one();
|
||||||
|
}
|
||||||
|
void
|
||||||
|
notify_all() {
|
||||||
|
spurious( false );
|
||||||
|
m_condition.notify_all();
|
||||||
|
}
|
||||||
|
void
|
||||||
|
spurious( bool const Spurious ) {
|
||||||
|
std::lock_guard<std::mutex> lock( m_mutex );
|
||||||
|
m_spurious = Spurious; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// members
|
||||||
|
mutable std::mutex m_mutex;
|
||||||
|
std::condition_variable m_condition;
|
||||||
|
bool m_spurious { true };
|
||||||
|
};
|
||||||
|
|
||||||
|
} // threading
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user