diff --git a/Globals.cpp b/Globals.cpp index 721a9829..430da597 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -758,6 +758,11 @@ global_settings::ConfigParse(cParser &Parser) { Parser.getTokens(1); Parser >> python_displaywindows; } + else if (token == "python.threadedupload") + { + Parser.getTokens(1); + Parser >> python_threadedupload; + } } while ((token != "") && (token != "endconfig")); //(!Parser->EndOfFile) // na koniec trochę zależności if (!bLoadTraction) // wczytywanie drutów i słupów diff --git a/Globals.h b/Globals.h index 9cdccd6c..086eaf3a 100644 --- a/Globals.h +++ b/Globals.h @@ -177,6 +177,7 @@ struct global_settings { bool captureonstart = true; bool python_displaywindows = false; + bool python_threadedupload = true; bool map_enabled = true; int gfx_framebuffer_width = -1; diff --git a/PyInt.cpp b/PyInt.cpp index 9f474b58..72ea95fd 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -31,51 +31,62 @@ void render_task::run() { if( ( outputwidth != nullptr ) && ( outputheight != nullptr ) && m_target != -1) { - ::glBindTexture( GL_TEXTURE_2D, m_target ); + m_width = PyInt_AsLong( outputwidth ); + m_height = PyInt_AsLong( outputheight ); - int w = PyInt_AsLong( outputwidth ); - int h = PyInt_AsLong( outputheight ); const unsigned char *image = reinterpret_cast( PyString_AsString( output ) ); + if (!Global.gfx_usegles) + { + int size = m_width * m_height * 3; + m_format = GL_SRGB8; + m_components = GL_RGB; + m_image = new unsigned char[size]; + memcpy(m_image, image, size); + } + else + { + m_format = GL_SRGB8_ALPHA8; + m_components = GL_RGBA; + m_image = new unsigned char[m_width * m_height * 4]; - // build texture - if (!Global.gfx_usegles) - { - glTexImage2D( - GL_TEXTURE_2D, 0, - GL_SRGB8, - w, h, 0, - GL_RGB, GL_UNSIGNED_BYTE, image); - } - else - { - unsigned char *data = new unsigned char[w * h * 4]; - for (int y = 0; y < h; y++) - for (int x = 0; x < w; x++) - { - data[(y * w + x) * 4 + 0] = image[(y * w + x) * 3 + 0]; - data[(y * w + x) * 4 + 1] = image[(y * w + x) * 3 + 1]; - data[(y * w + x) * 4 + 2] = image[(y * w + x) * 3 + 2]; - data[(y * w + x) * 4 + 3] = 0xFF; - } - - glTexImage2D( - GL_TEXTURE_2D, 0, - GL_SRGB8_ALPHA8, - w, h, 0, - GL_RGBA, GL_UNSIGNED_BYTE, data); - - delete[] data; - } - - glGenerateMipmap(GL_TEXTURE_2D); - glFlush(); + int w = m_width; + int h = m_height; + for (int y = 0; y < h; y++) + for (int x = 0; x < w; x++) + { + m_image[(y * w + x) * 4 + 0] = image[(y * w + x) * 3 + 0]; + m_image[(y * w + x) * 4 + 1] = image[(y * w + x) * 3 + 1]; + m_image[(y * w + x) * 4 + 2] = image[(y * w + x) * 3 + 2]; + m_image[(y * w + x) * 4 + 3] = 0xFF; + } + } } if( outputheight != nullptr ) { Py_DECREF( outputheight ); } if( outputwidth != nullptr ) { Py_DECREF( outputwidth ); } Py_DECREF( output ); } - // clean up after yourself - delete this; +} + +void render_task::upload() +{ + if (m_image) + { + glBindTexture(GL_TEXTURE_2D, m_target); + glTexImage2D( + GL_TEXTURE_2D, 0, + m_format, + m_width, m_height, 0, + m_components, GL_UNSIGNED_BYTE, m_image); + + delete[] m_image; + + glGenerateMipmap(GL_TEXTURE_2D); + + if (Global.python_threadedupload) + glFlush(); + } + + delete this; } void render_task::cancel() { @@ -140,10 +151,12 @@ auto python_taskqueue::init() -> bool { // init workers for( auto &worker : m_workers ) { - auto *openglcontextwindow { Application.window( -1 ) }; + GLFWwindow *openglcontextwindow = nullptr; + if (Global.python_threadedupload) + openglcontextwindow = Application.window( -1 ); worker = std::thread( &python_taskqueue::run, this, - openglcontextwindow, std::ref( m_tasks ), std::ref( m_condition ), std::ref( m_exit ) ); + openglcontextwindow, std::ref( m_tasks ), std::ref(m_uploadtasks), std::ref( m_condition ), std::ref( m_exit ) ); if( false == worker.joinable() ) { return false; } } @@ -297,9 +310,11 @@ cache_and_return: return renderer; } -void python_taskqueue::run( GLFWwindow *Context, rendertask_sequence &Tasks, threading::condition_variable &Condition, std::atomic &Exit ) { +void python_taskqueue::run( GLFWwindow *Context, rendertask_sequence &Tasks, uploadtask_sequence &Upload_Tasks, threading::condition_variable &Condition, std::atomic &Exit ) { + + if (Context) + glfwMakeContextCurrent( Context ); - glfwMakeContextCurrent( Context ); // create a state object for this thread PyEval_AcquireLock(); auto *threadstate { PyThreadState_New( m_mainthread->interp ) }; @@ -328,6 +343,13 @@ void python_taskqueue::run( GLFWwindow *Context, rendertask_sequence &Tasks, thr { // execute python code task->run(); + if (Context) + task->upload(); + else + { + std::lock_guard lock(Upload_Tasks.mutex); + Upload_Tasks.data.push_back(task); + } error(); } // clear the thread state @@ -347,6 +369,16 @@ void python_taskqueue::run( GLFWwindow *Context, rendertask_sequence &Tasks, thr PyEval_ReleaseLock(); } +void python_taskqueue::update() +{ + std::lock_guard lock(m_uploadtasks.mutex); + + for (auto &task : m_uploadtasks.data) + task->upload(); + + m_uploadtasks.data.clear(); +} + void python_taskqueue::error() { diff --git a/PyInt.h b/PyInt.h index cf88b200..84f668ce 100644 --- a/PyInt.h +++ b/PyInt.h @@ -56,23 +56,26 @@ class render_task { public: // constructors - render_task( PyObject *Renderer, PyObject *Input, GLuint Target ) : - m_renderer( Renderer ), m_input( Input ), m_target( Target ) + render_task( PyObject *Renderer, PyObject *Input, GLuint Target ) : + m_renderer( Renderer ), m_input( Input ), m_target( Target ) {} // methods - void run(); + void run(); + void upload(); void cancel(); auto target() const -> texture_handle { return m_target; } private: // members PyObject *m_renderer {nullptr}; - PyObject *m_input { nullptr }; + PyObject *m_input { nullptr }; GLuint m_target { 0 }; + + unsigned char *m_image = nullptr; + int m_width, m_height; + GLenum m_components, m_format; }; - - class python_taskqueue { public: @@ -99,14 +102,17 @@ public: // releases the python gil and swaps the main thread out void release_lock(); + void update(); + private: // types static int const WORKERCOUNT { 1 }; using worker_array = std::array; using rendertask_sequence = threading::lockable< std::deque >; + using uploadtask_sequence = threading::lockable< std::deque >; // methods auto fetch_renderer( std::string const Renderer ) -> PyObject *; - void run( GLFWwindow *Context, rendertask_sequence &Tasks, threading::condition_variable &Condition, std::atomic &Exit ); + void run(GLFWwindow *Context, rendertask_sequence &Tasks, uploadtask_sequence &Upload_Tasks, threading::condition_variable &Condition, std::atomic &Exit ); void error(); // members PyObject *m_main { nullptr }; @@ -117,6 +123,7 @@ private: std::atomic m_exit { false }; // signals the workers to quit std::unordered_map m_renderers; // cache of python classes rendertask_sequence m_tasks; + uploadtask_sequence m_uploadtasks; bool m_initialized { false }; }; diff --git a/application.cpp b/application.cpp index 7a3a4d07..0741aa00 100644 --- a/application.cpp +++ b/application.cpp @@ -152,6 +152,10 @@ eu07_application::run() { if (!m_modes[ m_modestack.top() ]->update()) break; + + m_taskqueue.update(); + opengl_texture::reset_unit_cache(); + if (!GfxRenderer.Render()) break;