mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-22 09:19:18 +02:00
basic texture generation support, python multithreading fixes
This commit is contained in:
@@ -453,7 +453,7 @@ bool TAnimModel::Init(std::string const &asName, std::string const &asReplacable
|
|||||||
bool TAnimModel::Load(cParser *parser, bool ter)
|
bool TAnimModel::Load(cParser *parser, bool ter)
|
||||||
{ // rozpoznanie wpisu modelu i ustawienie świateł
|
{ // rozpoznanie wpisu modelu i ustawienie świateł
|
||||||
std::string name = parser->getToken<std::string>();
|
std::string name = parser->getToken<std::string>();
|
||||||
std::string texture = parser->getToken<std::string>(); // tekstura (zmienia na małe)
|
std::string texture = parser->getToken<std::string>( false ); // tekstura (zmienia na małe)
|
||||||
replace_slashes( name );
|
replace_slashes( name );
|
||||||
if (!Init( name, texture ))
|
if (!Init( name, texture ))
|
||||||
{
|
{
|
||||||
|
|||||||
60
PyInt.cpp
60
PyInt.cpp
@@ -27,7 +27,7 @@ void render_task::run() {
|
|||||||
if( ( outputwidth != nullptr )
|
if( ( outputwidth != nullptr )
|
||||||
&& ( outputheight != nullptr ) ) {
|
&& ( outputheight != nullptr ) ) {
|
||||||
|
|
||||||
::glBindTexture( GL_TEXTURE_2D, GfxRenderer.Texture( m_target ).id );
|
::glBindTexture( GL_TEXTURE_2D, m_target );
|
||||||
// setup texture parameters
|
// setup texture parameters
|
||||||
::glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
|
::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_MAG_FILTER, GL_LINEAR );
|
||||||
@@ -74,8 +74,6 @@ auto python_taskqueue::init() -> bool {
|
|||||||
#endif
|
#endif
|
||||||
Py_Initialize();
|
Py_Initialize();
|
||||||
PyEval_InitThreads();
|
PyEval_InitThreads();
|
||||||
// save a pointer to the main PyThreadState object
|
|
||||||
m_mainthread = PyThreadState_Get();
|
|
||||||
// do the setup work while we hold the lock
|
// do the setup work while we hold the lock
|
||||||
m_main = PyImport_ImportModule("__main__");
|
m_main = PyImport_ImportModule("__main__");
|
||||||
if (m_main == nullptr) {
|
if (m_main == nullptr) {
|
||||||
@@ -101,8 +99,8 @@ auto python_taskqueue::init() -> bool {
|
|||||||
|
|
||||||
if( false == run_file( "abstractscreenrenderer" ) ) { goto release_and_exit; }
|
if( false == run_file( "abstractscreenrenderer" ) ) { goto release_and_exit; }
|
||||||
|
|
||||||
// release the lock
|
// release the lock, save the state for future use
|
||||||
PyEval_ReleaseLock();
|
m_mainthread = PyEval_SaveThread();
|
||||||
|
|
||||||
WriteLog( "Python Interpreter setup complete" );
|
WriteLog( "Python Interpreter setup complete" );
|
||||||
|
|
||||||
@@ -110,12 +108,11 @@ auto python_taskqueue::init() -> bool {
|
|||||||
for( auto &worker : m_workers ) {
|
for( auto &worker : m_workers ) {
|
||||||
|
|
||||||
auto *openglcontextwindow { Application.window( -1 ) };
|
auto *openglcontextwindow { Application.window( -1 ) };
|
||||||
worker =
|
worker = std::thread(
|
||||||
std::make_unique<std::thread>(
|
&python_taskqueue::run, this,
|
||||||
&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_condition ), std::ref( m_exit ) );
|
|
||||||
|
|
||||||
if( worker == nullptr ) { return false; }
|
if( false == worker.joinable() ) { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -131,8 +128,8 @@ void python_taskqueue::exit() {
|
|||||||
m_exit = true;
|
m_exit = true;
|
||||||
m_condition.notify_all();
|
m_condition.notify_all();
|
||||||
// let them free up their shit before we proceed
|
// let them free up their shit before we proceed
|
||||||
for( auto const &worker : m_workers ) {
|
for( auto &worker : m_workers ) {
|
||||||
worker->join();
|
worker.join();
|
||||||
}
|
}
|
||||||
// get rid of the leftover tasks
|
// get rid of the leftover tasks
|
||||||
// with the workers dead we don't have to worry about concurrent access anymore
|
// with the workers dead we don't have to worry about concurrent access anymore
|
||||||
@@ -140,8 +137,7 @@ void python_taskqueue::exit() {
|
|||||||
task->cancel();
|
task->cancel();
|
||||||
}
|
}
|
||||||
// take a bow
|
// take a bow
|
||||||
PyEval_AcquireLock();
|
acquire_lock();
|
||||||
PyThreadState_Swap( m_mainthread );
|
|
||||||
Py_Finalize();
|
Py_Finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +146,7 @@ auto python_taskqueue::insert( task_request const &Task ) -> bool {
|
|||||||
|
|
||||||
if( ( Task.renderer.empty() )
|
if( ( Task.renderer.empty() )
|
||||||
|| ( Task.input == nullptr )
|
|| ( Task.input == nullptr )
|
||||||
|| ( Task.target == null_handle ) ) { return false; }
|
|| ( Task.target == 0 ) ) { return false; }
|
||||||
|
|
||||||
auto *renderer { fetch_renderer( Task.renderer ) };
|
auto *renderer { fetch_renderer( Task.renderer ) };
|
||||||
if( renderer == nullptr ) { return false; }
|
if( renderer == nullptr ) { return false; }
|
||||||
@@ -164,11 +160,11 @@ auto python_taskqueue::insert( task_request const &Task ) -> bool {
|
|||||||
for( auto &task : m_tasks.data ) {
|
for( auto &task : m_tasks.data ) {
|
||||||
if( task->target() == Task.target ) {
|
if( task->target() == Task.target ) {
|
||||||
// replace pending task in the slot with the more recent one
|
// replace pending task in the slot with the more recent one
|
||||||
PyEval_AcquireLock();
|
acquire_lock();
|
||||||
{
|
{
|
||||||
task->cancel();
|
task->cancel();
|
||||||
}
|
}
|
||||||
PyEval_ReleaseLock();
|
release_lock();
|
||||||
task = newtask;
|
task = newtask;
|
||||||
newtaskinserted = true;
|
newtaskinserted = true;
|
||||||
break;
|
break;
|
||||||
@@ -202,6 +198,18 @@ auto python_taskqueue::run_file( std::string const &File, std::string const &Pat
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// acquires the python gil and sets the main thread as current
|
||||||
|
void python_taskqueue::acquire_lock() {
|
||||||
|
|
||||||
|
PyEval_RestoreThread( m_mainthread );
|
||||||
|
}
|
||||||
|
|
||||||
|
// releases the python gil and swaps the main thread out
|
||||||
|
void python_taskqueue::release_lock() {
|
||||||
|
|
||||||
|
PyEval_SaveThread();
|
||||||
|
}
|
||||||
|
|
||||||
auto python_taskqueue::fetch_renderer( std::string const Renderer ) ->PyObject * {
|
auto python_taskqueue::fetch_renderer( std::string const Renderer ) ->PyObject * {
|
||||||
|
|
||||||
auto const lookup { m_renderers.find( Renderer ) };
|
auto const lookup { m_renderers.find( Renderer ) };
|
||||||
@@ -213,7 +221,7 @@ auto python_taskqueue::fetch_renderer( std::string const Renderer ) ->PyObject *
|
|||||||
auto const file { Renderer.substr( path.size() ) };
|
auto const file { Renderer.substr( path.size() ) };
|
||||||
PyObject *renderer { nullptr };
|
PyObject *renderer { nullptr };
|
||||||
PyObject *rendererarguments { nullptr };
|
PyObject *rendererarguments { nullptr };
|
||||||
PyEval_AcquireLock();
|
acquire_lock();
|
||||||
{
|
{
|
||||||
if( m_main == nullptr ) {
|
if( m_main == nullptr ) {
|
||||||
ErrorLog( "Python Renderer: __main__ module is missing" );
|
ErrorLog( "Python Renderer: __main__ module is missing" );
|
||||||
@@ -246,7 +254,7 @@ cache_and_return:
|
|||||||
Py_DECREF( rendererarguments );
|
Py_DECREF( rendererarguments );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PyEval_ReleaseLock();
|
release_lock();
|
||||||
// cache the failures as well so we don't try again on subsequent requests
|
// cache the failures as well so we don't try again on subsequent requests
|
||||||
m_renderers.emplace( Renderer, renderer );
|
m_renderers.emplace( Renderer, renderer );
|
||||||
return renderer;
|
return renderer;
|
||||||
@@ -279,14 +287,14 @@ void python_taskqueue::run( GLFWwindow *Context, rendertask_sequence &Tasks, thr
|
|||||||
}
|
}
|
||||||
if( task != nullptr ) {
|
if( task != nullptr ) {
|
||||||
// swap in my thread state
|
// swap in my thread state
|
||||||
PyEval_AcquireLock();
|
PyEval_RestoreThread( threadstate );
|
||||||
PyThreadState_Swap( threadstate );
|
{
|
||||||
// execute python code
|
// execute python code
|
||||||
task->run();
|
task->run();
|
||||||
error();
|
error();
|
||||||
|
}
|
||||||
// clear the thread state
|
// clear the thread state
|
||||||
PyThreadState_Swap( nullptr );
|
PyEval_SaveThread();
|
||||||
PyEval_ReleaseLock();
|
|
||||||
}
|
}
|
||||||
// TBD, TODO: add some idle time between tasks in case we're on a single thread cpu?
|
// TBD, TODO: add some idle time between tasks in case we're on a single thread cpu?
|
||||||
} while( task != nullptr );
|
} while( task != nullptr );
|
||||||
|
|||||||
14
PyInt.h
14
PyInt.h
@@ -29,7 +29,7 @@ class render_task {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// constructors
|
// constructors
|
||||||
render_task( PyObject *Renderer, PyObject *Input, texture_handle Target ) :
|
render_task( PyObject *Renderer, PyObject *Input, GLuint Target ) :
|
||||||
m_renderer( Renderer ), m_input( Input ), m_target( Target )
|
m_renderer( Renderer ), m_input( Input ), m_target( Target )
|
||||||
{}
|
{}
|
||||||
// methods
|
// methods
|
||||||
@@ -41,9 +41,11 @@ private:
|
|||||||
// members
|
// members
|
||||||
PyObject *m_renderer {nullptr};
|
PyObject *m_renderer {nullptr};
|
||||||
PyObject *m_input { nullptr };
|
PyObject *m_input { nullptr };
|
||||||
texture_handle m_target { null_handle };
|
GLuint m_target { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class python_taskqueue {
|
class python_taskqueue {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -52,7 +54,7 @@ public:
|
|||||||
|
|
||||||
std::string const &renderer;
|
std::string const &renderer;
|
||||||
PyObject *input;
|
PyObject *input;
|
||||||
texture_handle target;
|
GLuint target;
|
||||||
};
|
};
|
||||||
// constructors
|
// constructors
|
||||||
python_taskqueue() = default;
|
python_taskqueue() = default;
|
||||||
@@ -65,11 +67,15 @@ public:
|
|||||||
auto insert( task_request const &Task ) -> bool;
|
auto insert( task_request const &Task ) -> bool;
|
||||||
// executes python script stored in specified file. returns true on success
|
// executes python script stored in specified file. returns true on success
|
||||||
auto run_file( std::string const &File, std::string const &Path = "" ) -> bool;
|
auto run_file( std::string const &File, std::string const &Path = "" ) -> bool;
|
||||||
|
// acquires the python gil and sets the main thread as current
|
||||||
|
void acquire_lock();
|
||||||
|
// releases the python gil and swaps the main thread out
|
||||||
|
void release_lock();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// types
|
// types
|
||||||
static int const WORKERCOUNT { 1 };
|
static int const WORKERCOUNT { 1 };
|
||||||
using worker_array = std::array<std::unique_ptr<std::thread>, WORKERCOUNT >;
|
using worker_array = std::array<std::thread, WORKERCOUNT >;
|
||||||
using rendertask_sequence = threading::lockable< std::deque<render_task *> >;
|
using rendertask_sequence = threading::lockable< std::deque<render_task *> >;
|
||||||
// methods
|
// methods
|
||||||
auto fetch_renderer( std::string const Renderer ) -> PyObject *;
|
auto fetch_renderer( std::string const Renderer ) -> PyObject *;
|
||||||
|
|||||||
219
Texture.cpp
219
Texture.cpp
@@ -19,6 +19,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include <ddraw.h>
|
#include <ddraw.h>
|
||||||
#include "GL/glew.h"
|
#include "GL/glew.h"
|
||||||
|
|
||||||
|
#include "application.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#include "Globals.h"
|
#include "Globals.h"
|
||||||
#include "Logs.h"
|
#include "Logs.h"
|
||||||
@@ -37,18 +38,23 @@ texture_manager::texture_manager() {
|
|||||||
void
|
void
|
||||||
opengl_texture::load() {
|
opengl_texture::load() {
|
||||||
|
|
||||||
if( name.size() < 3 ) { goto fail; }
|
if( type == "make:" ) {
|
||||||
|
// for generated texture we delay data creation until texture is bound
|
||||||
|
// this ensures the script will receive all simulation data it might potentially want
|
||||||
|
// as any binding will happen after simulation is loaded, initialized and running
|
||||||
|
// until then we supply data for a tiny 2x2 grey stub
|
||||||
|
make_stub();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
WriteLog( "Loading texture data from \"" + name + "\"", logtype::texture );
|
WriteLog( "Loading texture data from \"" + name + type + "\"", logtype::texture );
|
||||||
|
|
||||||
data_state = resource_state::loading;
|
data_state = resource_state::loading;
|
||||||
{
|
|
||||||
std::string const extension = name.substr( name.size() - 3, 3 );
|
|
||||||
|
|
||||||
if( extension == "dds" ) { load_DDS(); }
|
if( type == ".dds" ) { load_DDS(); }
|
||||||
else if( extension == "tga" ) { load_TGA(); }
|
else if( type == ".tga" ) { load_TGA(); }
|
||||||
else if( extension == "bmp" ) { load_BMP(); }
|
else if( type == ".bmp" ) { load_BMP(); }
|
||||||
else if( extension == "tex" ) { load_TEX(); }
|
else if( type == ".tex" ) { load_TEX(); }
|
||||||
else { goto fail; }
|
else { goto fail; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,16 +73,54 @@ opengl_texture::load() {
|
|||||||
|
|
||||||
fail:
|
fail:
|
||||||
data_state = resource_state::failed;
|
data_state = resource_state::failed;
|
||||||
ErrorLog( "Bad texture: failed to load texture \"" + name + "\"" );
|
ErrorLog( "Bad texture: failed to load texture \"" + name + type + "\"" );
|
||||||
// NOTE: temporary workaround for texture assignment errors
|
// NOTE: temporary workaround for texture assignment errors
|
||||||
id = 0;
|
id = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
opengl_texture::make_stub() {
|
||||||
|
|
||||||
|
data_width = 2;
|
||||||
|
data_height = 2;
|
||||||
|
data.resize( data_width * data_height * 3 );
|
||||||
|
std::fill( std::begin( data ), std::end( data ), static_cast<char>( 0xc0 ) );
|
||||||
|
data_mapcount = 1;
|
||||||
|
data_format = GL_RGB;
|
||||||
|
data_components = GL_RGB;
|
||||||
|
data_state = resource_state::good;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
opengl_texture::make_request() {
|
||||||
|
|
||||||
|
auto const components { Split( name, '?' ) };
|
||||||
|
auto const query { Split( components.back(), '&' ) };
|
||||||
|
|
||||||
|
PyObject *pythondictionary { nullptr };
|
||||||
|
Application.acquire_python_lock();
|
||||||
|
{
|
||||||
|
pythondictionary = PyDict_New();
|
||||||
|
if( pythondictionary != nullptr ) {
|
||||||
|
for( auto const &querypair : query ) {
|
||||||
|
auto const valuepos { querypair.find( '=' ) };
|
||||||
|
PyDict_SetItemString(
|
||||||
|
pythondictionary,
|
||||||
|
ToLower( querypair.substr( 0, valuepos ) ).c_str(),
|
||||||
|
PyGetString( querypair.substr( valuepos + 1 ).c_str() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Application.release_python_lock();
|
||||||
|
|
||||||
|
Application.request( { ToLower( components.front() ), pythondictionary, id } );
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
opengl_texture::load_BMP() {
|
opengl_texture::load_BMP() {
|
||||||
|
|
||||||
std::ifstream file( name, std::ios::binary ); file.unsetf( std::ios::skipws );
|
std::ifstream file( name + type, std::ios::binary ); file.unsetf( std::ios::skipws );
|
||||||
|
|
||||||
BITMAPFILEHEADER header;
|
BITMAPFILEHEADER header;
|
||||||
|
|
||||||
@@ -198,7 +242,7 @@ DDSURFACEDESC2 opengl_texture::deserialize_ddsd(std::istream &s)
|
|||||||
void
|
void
|
||||||
opengl_texture::load_DDS() {
|
opengl_texture::load_DDS() {
|
||||||
|
|
||||||
std::ifstream file( name, std::ios::binary | std::ios::ate ); file.unsetf( std::ios::skipws );
|
std::ifstream file( name + type, std::ios::binary | std::ios::ate ); file.unsetf( std::ios::skipws );
|
||||||
std::size_t filesize = static_cast<size_t>(file.tellg()); // ios::ate already positioned us at the end of the file
|
std::size_t filesize = static_cast<size_t>(file.tellg()); // ios::ate already positioned us at the end of the file
|
||||||
file.seekg( 0, std::ios::beg ); // rewind the caret afterwards
|
file.seekg( 0, std::ios::beg ); // rewind the caret afterwards
|
||||||
|
|
||||||
@@ -256,12 +300,12 @@ opengl_texture::load_DDS() {
|
|||||||
data_width /= 2;
|
data_width /= 2;
|
||||||
data_height /= 2;
|
data_height /= 2;
|
||||||
--data_mapcount;
|
--data_mapcount;
|
||||||
WriteLog( "Texture size exceeds specified limits, skipping mipmap level" );
|
WriteLog( "Texture pixelcount exceeds specified limits, skipping mipmap level" );
|
||||||
};
|
};
|
||||||
|
|
||||||
if( data_mapcount <= 0 ) {
|
if( data_mapcount <= 0 ) {
|
||||||
// there's a chance we've discarded the provided mipmap(s) as too large
|
// there's a chance we've discarded the provided mipmap(s) as too large
|
||||||
WriteLog( "Texture \"" + name + "\" has no mipmaps which can fit currently set texture size limits." );
|
WriteLog( "Texture \"" + name + "\" has no mipmaps which can fit currently set texture pixelcount limits." );
|
||||||
data_state = resource_state::failed;
|
data_state = resource_state::failed;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -316,7 +360,7 @@ opengl_texture::load_DDS() {
|
|||||||
void
|
void
|
||||||
opengl_texture::load_TEX() {
|
opengl_texture::load_TEX() {
|
||||||
|
|
||||||
std::ifstream file( name, std::ios::binary ); file.unsetf( std::ios::skipws );
|
std::ifstream file( name + type, std::ios::binary ); file.unsetf( std::ios::skipws );
|
||||||
|
|
||||||
char head[ 5 ];
|
char head[ 5 ];
|
||||||
file.read( head, 4 );
|
file.read( head, 4 );
|
||||||
@@ -363,7 +407,7 @@ opengl_texture::load_TEX() {
|
|||||||
void
|
void
|
||||||
opengl_texture::load_TGA() {
|
opengl_texture::load_TGA() {
|
||||||
|
|
||||||
std::ifstream file( name, std::ios::binary ); file.unsetf( std::ios::skipws );
|
std::ifstream file( name + type, std::ios::binary ); file.unsetf( std::ios::skipws );
|
||||||
|
|
||||||
// Read the header of the TGA, compare it with the known headers for compressed and uncompressed TGAs
|
// Read the header of the TGA, compare it with the known headers for compressed and uncompressed TGAs
|
||||||
unsigned char tgaheader[ 18 ];
|
unsigned char tgaheader[ 18 ];
|
||||||
@@ -604,6 +648,12 @@ opengl_texture::create() {
|
|||||||
data = std::vector<char>();
|
data = std::vector<char>();
|
||||||
data_state = resource_state::none;
|
data_state = resource_state::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( type == "make:" ) {
|
||||||
|
// for generated textures send a request to have the actual content of the texture generated
|
||||||
|
make_request();
|
||||||
|
}
|
||||||
|
|
||||||
is_ready = true;
|
is_ready = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,30 +670,36 @@ opengl_texture::release() {
|
|||||||
// if resource move is enabled we don't keep a cpu side copy after upload
|
// if resource move is enabled we don't keep a cpu side copy after upload
|
||||||
// so need to re-acquire the data before release
|
// so need to re-acquire the data before release
|
||||||
// TBD, TODO: instead of vram-ram transfer fetch the data 'normally' from the disk using worker thread
|
// TBD, TODO: instead of vram-ram transfer fetch the data 'normally' from the disk using worker thread
|
||||||
::glBindTexture( GL_TEXTURE_2D, id );
|
if( type == "make:" ) {
|
||||||
GLint datasize {};
|
// auto generated textures only store a stub
|
||||||
GLint iscompressed {};
|
make_stub();
|
||||||
::glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED, &iscompressed );
|
|
||||||
if( iscompressed == GL_TRUE ) {
|
|
||||||
// texture is compressed on the gpu side
|
|
||||||
// query texture details needed to perform the backup...
|
|
||||||
::glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &data_format );
|
|
||||||
::glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &datasize );
|
|
||||||
data.resize( datasize );
|
|
||||||
// ...fetch the data...
|
|
||||||
::glGetCompressedTexImage( GL_TEXTURE_2D, 0, &data[ 0 ] );
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// for whatever reason texture didn't get compressed during upload
|
::glBindTexture( GL_TEXTURE_2D, id );
|
||||||
// fallback on plain rgba storage...
|
GLint datasize{};
|
||||||
data_format = GL_RGBA;
|
GLint iscompressed{};
|
||||||
data.resize( data_width * data_height * 4 );
|
::glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED, &iscompressed );
|
||||||
// ...fetch the data...
|
if( iscompressed == GL_TRUE ) {
|
||||||
::glGetTexImage( GL_TEXTURE_2D, 0, data_format, GL_UNSIGNED_BYTE, &data[ 0 ] );
|
// texture is compressed on the gpu side
|
||||||
|
// query texture details needed to perform the backup...
|
||||||
|
::glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &data_format );
|
||||||
|
::glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &datasize );
|
||||||
|
data.resize( datasize );
|
||||||
|
// ...fetch the data...
|
||||||
|
::glGetCompressedTexImage( GL_TEXTURE_2D, 0, &data[ 0 ] );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// for whatever reason texture didn't get compressed during upload
|
||||||
|
// fallback on plain rgba storage...
|
||||||
|
data_format = GL_RGBA;
|
||||||
|
data.resize( data_width * data_height * 4 );
|
||||||
|
// ...fetch the data...
|
||||||
|
::glGetTexImage( GL_TEXTURE_2D, 0, data_format, GL_UNSIGNED_BYTE, &data[ 0 ] );
|
||||||
|
}
|
||||||
|
// ...and update texture object state
|
||||||
|
data_mapcount = 1; // we keep copy of only top mipmap level
|
||||||
|
data_state = resource_state::good;
|
||||||
}
|
}
|
||||||
// ...and update texture object state
|
|
||||||
data_mapcount = 1; // we keep copy of only top mipmap level
|
|
||||||
data_state = resource_state::good;
|
|
||||||
}
|
}
|
||||||
// release opengl resources
|
// release opengl resources
|
||||||
::glDeleteTextures( 1, &id );
|
::glDeleteTextures( 1, &id );
|
||||||
@@ -697,7 +753,7 @@ opengl_texture::downsize( GLuint const Format ) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteLog( "Texture size exceeds specified limits, downsampling data" );
|
WriteLog( "Texture pixelcount exceeds specified limits, downsampling data" );
|
||||||
// trim potential odd texture sizes
|
// trim potential odd texture sizes
|
||||||
data_width -= ( data_width % 2 );
|
data_width -= ( data_width % 2 );
|
||||||
data_height -= ( data_height % 2 );
|
data_height -= ( data_height % 2 );
|
||||||
@@ -754,48 +810,79 @@ texture_manager::create( std::string Filename, bool const Loadnow ) {
|
|||||||
if( Filename.find( '|' ) != std::string::npos )
|
if( Filename.find( '|' ) != std::string::npos )
|
||||||
Filename.erase( Filename.find( '|' ) ); // po | może być nazwa kolejnej tekstury
|
Filename.erase( Filename.find( '|' ) ); // po | może być nazwa kolejnej tekstury
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> locator; // resource name, resource type
|
||||||
std::string traits;
|
std::string traits;
|
||||||
auto const traitpos = Filename.find( ':' );
|
|
||||||
if( traitpos != std::string::npos ) {
|
|
||||||
// po dwukropku mogą być podane dodatkowe informacje niebędące nazwą tekstury
|
|
||||||
if( Filename.size() > traitpos + 1 )
|
|
||||||
traits = Filename.substr( traitpos + 1 );
|
|
||||||
Filename.erase( traitpos );
|
|
||||||
}
|
|
||||||
|
|
||||||
erase_extension( Filename );
|
// discern textures generated by a script
|
||||||
replace_slashes( Filename );
|
// TBD: support file: for file resources?
|
||||||
if( Filename[ 0 ] == '/' ) {
|
auto const isgenerated { Filename.find( "make:" ) == 0 };
|
||||||
// filename can potentially begin with a slash, and we don't need it
|
// process supplied resource name
|
||||||
Filename.erase( 0, 1 );
|
if( isgenerated ) {
|
||||||
}
|
// generated resource
|
||||||
|
// scheme:(user@)path?query
|
||||||
|
|
||||||
|
// remove scheme indicator
|
||||||
|
Filename.erase( 0, Filename.find( ':' ) + 1 );
|
||||||
|
// TODO: extract traits specification from the query
|
||||||
|
// clean up slashes
|
||||||
|
replace_slashes( Filename );
|
||||||
|
erase_leading_slashes( Filename );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// regular file resource
|
||||||
|
// (filepath/)(#)filename.extension(:traits)
|
||||||
|
|
||||||
|
// extract trait specifications
|
||||||
|
auto const traitpos = Filename.rfind( ':' );
|
||||||
|
if( traitpos != std::string::npos ) {
|
||||||
|
// po dwukropku mogą być podane dodatkowe informacje niebędące nazwą tekstury
|
||||||
|
if( Filename.size() > traitpos + 1 ) {
|
||||||
|
traits = Filename.substr( traitpos + 1 );
|
||||||
|
}
|
||||||
|
Filename.erase( traitpos );
|
||||||
|
}
|
||||||
|
// potentially trim file type indicator since we check for multiple types
|
||||||
|
erase_extension( Filename );
|
||||||
|
// clean up slashes
|
||||||
|
replace_slashes( Filename );
|
||||||
|
erase_leading_slashes( Filename );
|
||||||
|
// temporary code for legacy assets -- textures with names beginning with # are to be sharpened
|
||||||
|
if( ( Filename[ 0 ] == '#' )
|
||||||
|
|| ( Filename.find( "/#" ) != std::string::npos ) ) {
|
||||||
|
traits += '#';
|
||||||
|
}
|
||||||
|
}
|
||||||
// try to locate requested texture in the databank
|
// try to locate requested texture in the databank
|
||||||
auto lookup { find_in_databank( Filename ) };
|
// TBD, TODO: include trait specification in the resource id in the databank?
|
||||||
|
auto const lookup { find_in_databank( Filename ) };
|
||||||
if( lookup != npos ) {
|
if( lookup != npos ) {
|
||||||
return lookup;
|
return lookup;
|
||||||
}
|
}
|
||||||
// if we don't have the texture in the databank, check if it's on disk
|
// if the lookup fails...
|
||||||
auto const disklookup { find_on_disk( Filename ) };
|
if( isgenerated ) {
|
||||||
|
// TODO: verify presence of the generator script
|
||||||
if( true == disklookup.first.empty() ) {
|
locator.first = Filename;
|
||||||
// there's nothing matching in the databank nor on the disk, report failure
|
locator.second = "make:";
|
||||||
ErrorLog( "Bad file: failed do locate texture file \"" + Filename + "\"", logtype::file );
|
}
|
||||||
return npos;
|
else {
|
||||||
|
// ...for file resources check if it's on disk
|
||||||
|
locator = find_on_disk( Filename );
|
||||||
|
if( true == locator.first.empty() ) {
|
||||||
|
// there's nothing matching in the databank nor on the disk, report failure
|
||||||
|
ErrorLog( "Bad file: failed do locate texture file \"" + Filename + "\"", logtype::file );
|
||||||
|
return npos;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto texture = new opengl_texture();
|
auto texture = new opengl_texture();
|
||||||
texture->name = disklookup.first + disklookup.second;
|
texture->name = locator.first;
|
||||||
if( Filename.find('#') != std::string::npos ) {
|
texture->type = locator.second;
|
||||||
// temporary code for legacy assets -- textures with names beginning with # are to be sharpened
|
|
||||||
traits += '#';
|
|
||||||
}
|
|
||||||
texture->traits = traits;
|
texture->traits = traits;
|
||||||
auto const textureindex = (texture_handle)m_textures.size();
|
auto const textureindex = (texture_handle)m_textures.size();
|
||||||
m_textures.emplace_back( texture, std::chrono::steady_clock::time_point() );
|
m_textures.emplace_back( texture, std::chrono::steady_clock::time_point() );
|
||||||
m_texturemappings.emplace( disklookup.first, textureindex );
|
m_texturemappings.emplace( locator.first, textureindex );
|
||||||
|
|
||||||
WriteLog( "Created texture object for \"" + disklookup.first + disklookup.second + "\"", logtype::texture );
|
WriteLog( "Created texture object for \"" + locator.first + "\"", logtype::texture );
|
||||||
|
|
||||||
if( true == Loadnow ) {
|
if( true == Loadnow ) {
|
||||||
|
|
||||||
|
|||||||
@@ -47,10 +47,13 @@ struct opengl_texture {
|
|||||||
bool is_ready{ false }; // indicates the texture was processed and is ready for use
|
bool is_ready{ false }; // indicates the texture was processed and is ready for use
|
||||||
std::string traits; // requested texture attributes: wrapping modes etc
|
std::string traits; // requested texture attributes: wrapping modes etc
|
||||||
std::string name; // name of the texture source file
|
std::string name; // name of the texture source file
|
||||||
|
std::string type; // type of the texture source file
|
||||||
std::size_t size{ 0 }; // size of the texture data, in kb
|
std::size_t size{ 0 }; // size of the texture data, in kb
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// methods
|
// methods
|
||||||
|
void make_stub();
|
||||||
|
void make_request();
|
||||||
void load_BMP();
|
void load_BMP();
|
||||||
void load_DDS();
|
void load_DDS();
|
||||||
void load_TEX();
|
void load_TEX();
|
||||||
|
|||||||
@@ -448,11 +448,11 @@ bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d)
|
|||||||
PyObject *TTrain::GetTrainState() {
|
PyObject *TTrain::GetTrainState() {
|
||||||
|
|
||||||
auto const *mover = DynamicObject->MoverParameters;
|
auto const *mover = DynamicObject->MoverParameters;
|
||||||
PyEval_AcquireLock();
|
Application.acquire_python_lock();
|
||||||
auto *dict = PyDict_New();
|
auto *dict = PyDict_New();
|
||||||
if( ( dict == nullptr )
|
if( ( dict == nullptr )
|
||||||
|| ( mover == nullptr ) ) {
|
|| ( mover == nullptr ) ) {
|
||||||
PyEval_ReleaseLock();
|
Application.release_python_lock();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,7 +582,7 @@ PyObject *TTrain::GetTrainState() {
|
|||||||
PyDict_SetItemString( dict, "seconds", PyGetInt( simulation::Time.second() ) );
|
PyDict_SetItemString( dict, "seconds", PyGetInt( simulation::Time.second() ) );
|
||||||
PyDict_SetItemString( dict, "air_temperature", PyGetInt( Global.AirTemperature ) );
|
PyDict_SetItemString( dict, "air_temperature", PyGetInt( Global.AirTemperature ) );
|
||||||
|
|
||||||
PyEval_ReleaseLock();
|
Application.release_python_lock();
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5993,7 +5993,7 @@ bool TTrain::Update( double const Deltatime )
|
|||||||
&& ( false == FreeFlyModeFlag ) ) { // don't bother if we're outside
|
&& ( false == FreeFlyModeFlag ) ) { // don't bother if we're outside
|
||||||
fScreenTimer = 0.f;
|
fScreenTimer = 0.f;
|
||||||
for( auto const &screen : m_screens ) {
|
for( auto const &screen : m_screens ) {
|
||||||
Application.request( { screen.first, GetTrainState(), screen.second } );
|
Application.request( { screen.first, GetTrainState(), GfxRenderer.Texture( screen.second ).id } );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// sounds
|
// sounds
|
||||||
|
|||||||
@@ -151,12 +151,27 @@ eu07_application::run() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// issues request for a worker thread to perform specified task. returns: true if task was scheduled
|
||||||
bool
|
bool
|
||||||
eu07_application::request( python_taskqueue::task_request const &Task ) {
|
eu07_application::request( python_taskqueue::task_request const &Task ) {
|
||||||
|
|
||||||
return m_taskqueue.insert( Task );
|
return m_taskqueue.insert( Task );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensures the main thread holds the python gil and can safely execute python calls
|
||||||
|
void
|
||||||
|
eu07_application::acquire_python_lock() {
|
||||||
|
|
||||||
|
m_taskqueue.acquire_lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// frees the python gil and swaps out the main thread
|
||||||
|
void
|
||||||
|
eu07_application::release_python_lock() {
|
||||||
|
|
||||||
|
m_taskqueue.release_lock();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
eu07_application::exit() {
|
eu07_application::exit() {
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,15 @@ public:
|
|||||||
init( int Argc, char *Argv[] );
|
init( int Argc, char *Argv[] );
|
||||||
int
|
int
|
||||||
run();
|
run();
|
||||||
|
// issues request for a worker thread to perform specified task. returns: true if task was scheduled
|
||||||
bool
|
bool
|
||||||
request( python_taskqueue::task_request const &Task );
|
request( python_taskqueue::task_request const &Task );
|
||||||
|
// ensures the main thread holds the python gil and can safely execute python calls
|
||||||
|
void
|
||||||
|
acquire_python_lock();
|
||||||
|
// frees the python gil and swaps out the main thread
|
||||||
|
void
|
||||||
|
release_python_lock();
|
||||||
void
|
void
|
||||||
exit();
|
exit();
|
||||||
void
|
void
|
||||||
|
|||||||
87
material.cpp
87
material.cpp
@@ -117,47 +117,75 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) {
|
|||||||
if( filename.find( '|' ) != std::string::npos )
|
if( filename.find( '|' ) != std::string::npos )
|
||||||
filename.erase( filename.find( '|' ) ); // po | może być nazwa kolejnej tekstury
|
filename.erase( filename.find( '|' ) ); // po | może być nazwa kolejnej tekstury
|
||||||
|
|
||||||
erase_extension( filename );
|
// discern references to textures generated by a script
|
||||||
replace_slashes( filename );
|
// TBD: support file: for file resources?
|
||||||
if( filename[ 0 ] == '/' ) {
|
auto const isgenerated { filename.find( "make:" ) == 0 };
|
||||||
// filename can potentially begin with a slash, and we don't need it
|
|
||||||
filename.erase( 0, 1 );
|
// process supplied resource name
|
||||||
|
if( isgenerated ) {
|
||||||
|
// generated resource
|
||||||
|
// scheme:(user@)path?query
|
||||||
|
|
||||||
|
// remove scheme indicator
|
||||||
|
filename.erase( 0, filename.find(':') + 1 );
|
||||||
|
// TBD, TODO: allow shader specification as part of the query?
|
||||||
|
replace_slashes( filename );
|
||||||
|
erase_leading_slashes( filename );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// regular file resource
|
||||||
|
// (filepath/)filename.extension
|
||||||
|
|
||||||
|
erase_extension( filename );
|
||||||
|
replace_slashes( filename );
|
||||||
|
erase_leading_slashes( filename );
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to locate requested material in the databank
|
auto const databanklookup { find_in_databank( ToLower( filename ) ) };
|
||||||
auto const databanklookup { find_in_databank( filename ) };
|
|
||||||
if( databanklookup != null_handle ) {
|
if( databanklookup != null_handle ) {
|
||||||
return databanklookup;
|
return databanklookup;
|
||||||
}
|
}
|
||||||
// if this fails, try to look for it on disk
|
|
||||||
opengl_material material;
|
opengl_material material;
|
||||||
auto const disklookup { find_on_disk( filename ) };
|
material_handle materialhandle { null_handle };
|
||||||
if( false == disklookup.first.empty() ) {
|
|
||||||
cParser materialparser( disklookup.first + disklookup.second, cParser::buffer_FILE );
|
auto const locator {
|
||||||
if( false == material.deserialize( materialparser, Loadnow ) ) {
|
isgenerated ?
|
||||||
// deserialization failed but the .mat file does exist, so we give up at this point
|
std::make_pair( filename, "make:" ) :
|
||||||
return null_handle;
|
find_on_disk( filename ) };
|
||||||
|
|
||||||
|
if( ( false == isgenerated )
|
||||||
|
&& ( false == locator.first.empty() ) ) {
|
||||||
|
// try to parse located file resource
|
||||||
|
cParser materialparser( locator.first + locator.second, cParser::buffer_FILE );
|
||||||
|
if( true == material.deserialize( materialparser, Loadnow ) ) {
|
||||||
|
material.name = locator.first;
|
||||||
}
|
}
|
||||||
material.name = disklookup.first;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// if there's no .mat file, this could be legacy method of referring just to diffuse texture directly, make a material out of it in such case
|
// if there's no .mat file, this can be either autogenerated texture,
|
||||||
|
// or legacy method of referring just to diffuse texture directly.
|
||||||
|
// wrap basic material around it in either case
|
||||||
material.texture1 = GfxRenderer.Fetch_Texture( Filename, Loadnow );
|
material.texture1 = GfxRenderer.Fetch_Texture( Filename, Loadnow );
|
||||||
if( material.texture1 == null_handle ) {
|
if( material.texture1 != null_handle ) {
|
||||||
// if there's also no texture, give up
|
// use texture path and name to tell the newly created materials apart
|
||||||
return null_handle;
|
material.name = GfxRenderer.Texture( material.texture1 ).name;
|
||||||
|
material.has_alpha = GfxRenderer.Texture( material.texture1 ).has_alpha;
|
||||||
}
|
}
|
||||||
// use texture path and name to tell the newly created materials apart
|
|
||||||
filename = GfxRenderer.Texture( material.texture1 ).name;
|
|
||||||
erase_extension( filename );
|
|
||||||
material.name = filename;
|
|
||||||
material.has_alpha = GfxRenderer.Texture( material.texture1 ).has_alpha;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
material_handle handle = m_materials.size();
|
if( false == material.name.empty() ) {
|
||||||
m_materials.emplace_back( material );
|
// if we have material name it means resource was processed succesfully
|
||||||
m_materialmappings.emplace( material.name, handle );
|
materialhandle = m_materials.size();
|
||||||
return handle;
|
m_materials.emplace_back( material );
|
||||||
|
m_materialmappings.emplace( material.name, materialhandle );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// otherwise record our failure to process the resource, to speed up subsequent attempts
|
||||||
|
m_materialmappings.emplace( filename, materialhandle );
|
||||||
|
}
|
||||||
|
|
||||||
|
return materialhandle;
|
||||||
};
|
};
|
||||||
|
|
||||||
// checks whether specified material is in the material bank. returns handle to the material, or a null handle
|
// checks whether specified material is in the material bank. returns handle to the material, or a null handle
|
||||||
@@ -184,9 +212,10 @@ material_manager::find_in_databank( std::string const &Materialname ) const {
|
|||||||
std::pair<std::string, std::string>
|
std::pair<std::string, std::string>
|
||||||
material_manager::find_on_disk( std::string const &Materialname ) const {
|
material_manager::find_on_disk( std::string const &Materialname ) const {
|
||||||
|
|
||||||
|
auto const materialname { ToLower( Materialname ) };
|
||||||
return (
|
return (
|
||||||
FileExists(
|
FileExists(
|
||||||
{ Global.asCurrentTexturePath + Materialname, Materialname, szTexturePath + Materialname },
|
{ Global.asCurrentTexturePath + materialname, materialname, szTexturePath + materialname },
|
||||||
{ ".mat" } ) );
|
{ ".mat" } ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -384,6 +384,14 @@ erase_extension( std::string &Filename ) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
erase_leading_slashes( std::string &Filename ) {
|
||||||
|
|
||||||
|
while( Filename[ 0 ] == '/' ) {
|
||||||
|
Filename.erase( 0, 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// potentially replaces backward slashes in provided file path with unix-compatible forward slashes
|
// potentially replaces backward slashes in provided file path with unix-compatible forward slashes
|
||||||
void
|
void
|
||||||
replace_slashes( std::string &Filename ) {
|
replace_slashes( std::string &Filename ) {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#define szModelPath "models/"
|
#define szModelPath "models/"
|
||||||
#define szDynamicPath "dynamic/"
|
#define szDynamicPath "dynamic/"
|
||||||
#define szSoundPath "sounds/"
|
#define szSoundPath "sounds/"
|
||||||
|
#define szDataPath "data/"
|
||||||
|
|
||||||
#define MAKE_ID4(a,b,c,d) (((std::uint32_t)(d)<<24)|((std::uint32_t)(c)<<16)|((std::uint32_t)(b)<<8)|(std::uint32_t)(a))
|
#define MAKE_ID4(a,b,c,d) (((std::uint32_t)(d)<<24)|((std::uint32_t)(c)<<16)|((std::uint32_t)(b)<<8)|(std::uint32_t)(a))
|
||||||
|
|
||||||
@@ -188,6 +189,10 @@ std::time_t last_modified( std::string const &Filename );
|
|||||||
bool
|
bool
|
||||||
erase_extension( std::string &Filename );
|
erase_extension( std::string &Filename );
|
||||||
|
|
||||||
|
// potentially erase leading slashes from provided file path
|
||||||
|
void
|
||||||
|
erase_leading_slashes( std::string &Filename );
|
||||||
|
|
||||||
// potentially replaces backward slashes in provided file path with unix-compatible forward slashes
|
// potentially replaces backward slashes in provided file path with unix-compatible forward slashes
|
||||||
void
|
void
|
||||||
replace_slashes( std::string &Filename );
|
replace_slashes( std::string &Filename );
|
||||||
|
|||||||
Reference in New Issue
Block a user