Commit 6edc4c53 authored by Martin Turski's avatar Martin Turski

- added Entity class that will be used to render things

- added NativePerfLocks.h which is a source-file-only header containing locks that use very low system resources
- added per-model locking for resource safety
- added per-txd locking for resource safety
- added texture support to model loading

Pretty cool stuff is coming soon, especially with aap's efficient feature work :) With entities added I can start working on world sectoring, basically what makes all GTA engines.
parent d3fa06e6
Pipeline #40 skipped
#pragma once
#include "Streaming.h"
// Entity class for having actual objects in the world of things.
namespace krt
{
struct Entity
{
Entity( void );
~Entity();
void SetModelIndex( streaming::ident_t modelID );
bool CreateRWObject( void );
void DeleteRWObject( void );
void SetMatrix( const rw::Matrix& mat );
const rw::Matrix& GetMatrix( void ) const;
//todo.
private:
streaming::ident_t modelID;
rw::Matrix matrix;
rw::Object *rwObject;
};
}
\ No newline at end of file
......@@ -21,17 +21,20 @@ struct ModelManager : public streaming::StreamingTypeInterface
VEHICLE,
PED
};
struct ModelResource
{
inline streaming::ident_t GetID( void ) const { return this->id; }
inline eModelType GetType( void ) const { return this->modelType; }
rw::Object* CloneModel( void );
void ReleaseModel( rw::Object *rwobj );
private:
friend struct ModelManager;
static void NativeReleaseModel( rw::Object *rwobj );
inline ModelResource( vfs::DevicePtr device, std::string pathToRes ) : vfsResLoc( device, pathToRes )
{
return;
......@@ -54,6 +57,8 @@ struct ModelManager : public streaming::StreamingTypeInterface
DeviceResourceLocation vfsResLoc;
rw::Object *modelPtr;
SRWLOCK_VIRTUAL lockModelLoading;
};
ModelManager( streaming::StreamMan& streaming, TextureManager& texManager );
......@@ -81,8 +86,6 @@ private:
std::vector <ModelResource*> models;
std::map <std::string, ModelResource*> modelByName;
std::shared_timed_mutex lockModelContext;
};
}
\ No newline at end of file
#pragma once
// Native performance locks because C++ objects tend to overuse OS resources.
// Please port these locks to all kind of systems we want to support.
// Do not import this header in the global namespace.
#include <Windows.h>
namespace krt
{
// Lock based on the "SRWLOCK_VIRTUAL" definition.
struct NativeSRW_Exclusive
{
inline NativeSRW_Exclusive( SRWLOCK_VIRTUAL& lockHandle ) : lockHandle( lockHandle )
{
AcquireSRWLockExclusive( (SRWLOCK*)&lockHandle );
}
inline ~NativeSRW_Exclusive( void )
{
ReleaseSRWLockExclusive( (SRWLOCK*)&lockHandle );
}
private:
SRWLOCK_VIRTUAL& lockHandle;
};
struct NativeSRW_Shared
{
inline NativeSRW_Shared( SRWLOCK_VIRTUAL& lockHandle ) : lockHandle( lockHandle )
{
AcquireSRWLockShared( (SRWLOCK*)&lockHandle );
}
inline ~NativeSRW_Shared( void )
{
ReleaseSRWLockShared( (SRWLOCK*)&lockHandle );
}
private:
SRWLOCK_VIRTUAL& lockHandle;
};
}
\ No newline at end of file
......@@ -26,4 +26,6 @@ inline void clumpForAllCameras( rw::Clump *clump, cbType& cb )
cb( Camera::fromClump( lnk ) );
}
};
\ No newline at end of file
};
typedef void *SRWLOCK_VIRTUAL;
\ No newline at end of file
......@@ -31,6 +31,10 @@ struct TextureManager : public streaming::StreamingTypeInterface
private:
streaming::StreamMan& streaming;
static rw::Texture* _rwFindTextureCallback( const char *name );
rw::Texture* (*_tmpStoreOldCB)( const char *name );
struct TexDictResource
{
TexDictResource( vfs::DevicePtr device, std::string pathToRes ) : vfsResLoc( device, pathToRes )
......@@ -49,15 +53,56 @@ private:
DeviceResourceLocation vfsResLoc;
rw::TexDictionary *txdPtr;
SRWLOCK_VIRTUAL lockResourceLoad;
};
struct ThreadLocal_CurrentTXDEnv
{
inline ThreadLocal_CurrentTXDEnv( TextureManager *manager )
{
exclusive_lock_acquire <std::shared_timed_mutex> ctxAddTXDEnv( manager->lockTXDEnvList );
LIST_INSERT( manager->_tlCurrentTXDEnvList.root, node );
this->isRegistered = true;
this->manager = manager;
}
inline ~ThreadLocal_CurrentTXDEnv( void )
{
if ( manager != NULL )
{
exclusive_lock_acquire <std::shared_timed_mutex> ctxRemoveTXDEnv( manager->lockTXDEnvList );
if ( this->isRegistered )
{
LIST_REMOVE( node );
this->isRegistered = false;
}
}
}
rw::TexDictionary *currentTXD;
bool isRegistered;
NestedListEntry <ThreadLocal_CurrentTXDEnv> node;
TextureManager *manager;
};
NestedList <ThreadLocal_CurrentTXDEnv> _tlCurrentTXDEnvList;
mutable std::shared_timed_mutex lockTXDEnvList;
ThreadLocal_CurrentTXDEnv* GetCurrentTXDEnv( void );
TexDictResource* FindTexDictInternal( const std::string& name ) const;
std::vector <TexDictResource*> texDictList;
std::map <std::string, TexDictResource*> texDictMap;
mutable std::shared_timed_mutex lockTextureContext;
};
}
\ No newline at end of file
#include "StdInc.h"
#include "Entity.h"
#include "Game.h"
namespace krt
{
Entity::Entity( void )
{
this->modelID = -1;
this->matrix.setIdentity();
this->rwObject = NULL;
}
Entity::~Entity( void )
{
}
void Entity::SetModelIndex( streaming::ident_t modelID )
{
// Make sure we have no RW object when switching models
// This is required because models are not entirely thread-safe if not following certain rules.
assert( this->rwObject == NULL );
this->modelID = modelID;
}
bool Entity::CreateRWObject( void )
{
streaming::ident_t modelID = this->modelID;
if ( modelID == -1 )
return false;
// If we already have an atomic, we refuse to update.
if ( this->rwObject )
return false;
// Fetch a model from the model manager.
Game *game = theGame;
ModelManager::ModelResource *modelEntry = game->GetModelManager().GetModelByID( modelID );
if ( !modelEntry )
return false;
rw::Object *rwobj = modelEntry->CloneModel();
if ( !rwobj )
return false;
this->rwObject = rwobj;
return true;
}
void Entity::DeleteRWObject( void )
{
rw::Object *rwobj = this->rwObject;
if ( !rwobj )
return;
Game *game = theGame;
ModelManager::ModelResource *modelEntry = game->GetModelManager().GetModelByID( this->modelID );
if ( !modelEntry )
return;
modelEntry->ReleaseModel( rwobj );
this->rwObject = NULL;
}
void Entity::SetMatrix( const rw::Matrix& mat )
{
this->matrix = mat;
}
const rw::Matrix& Entity::GetMatrix( void ) const
{
return this->matrix;
}
}
\ No newline at end of file
......@@ -2,9 +2,10 @@
#include "ModelInfo.h"
#include "vfs\Manager.h"
#include "Game.h"
#include "NativePerfLocks.h"
namespace krt
{
......@@ -78,6 +79,8 @@ void ModelManager::RegisterAtomicModel(
modelEntry->modelPtr = NULL;
modelEntry->modelType = eModelType::ATOMIC;
modelEntry->lockModelLoading = SRWLOCK_INIT;
bool couldLink = streaming.LinkResource( id, name, &modelEntry->vfsResLoc );
if ( !couldLink )
......@@ -124,8 +127,6 @@ ModelManager::ModelResource* ModelManager::GetModelByID( streaming::ident_t id )
if ( id < 0 || id >= MAX_MODELS )
return NULL;
shared_lock_acquire <std::shared_timed_mutex> ctxGetModel( this->lockModelContext );
return this->models[ id ];
}
......@@ -146,13 +147,13 @@ void ModelManager::LoadAllModels( void )
rw::Object* ModelManager::ModelResource::CloneModel( void )
{
exclusive_lock_acquire <std::shared_timed_mutex> ctxCloneModel( this->manager->lockModelContext );
rw::Object *modelItem = this->modelPtr;
if ( modelItem == NULL )
return NULL;
NativeSRW_Exclusive ctxCloneModel( this->lockModelLoading );
rw::uint8 modelType = modelItem->type;
if ( modelType == rw::Atomic::ID )
......@@ -171,6 +172,36 @@ rw::Object* ModelManager::ModelResource::CloneModel( void )
return NULL;
}
void ModelManager::ModelResource::NativeReleaseModel( rw::Object *rwobj )
{
rw::uint8 modelType = rwobj->type;
if ( modelType == rw::Atomic::ID )
{
rw::Atomic *atomic = (rw::Atomic*)rwobj;
atomic->destroy();
}
else if ( modelType == rw::Clump::ID )
{
rw::Clump *clump = (rw::Clump*)rwobj;
clump->destroy();
}
else
{
assert( 0 );
}
}
void ModelManager::ModelResource::ReleaseModel( rw::Object *rwobj )
{
// Release the resource under our lock.
NativeSRW_Exclusive ctxReleaseModel( this->lockModelLoading );
NativeReleaseModel( rwobj );
}
static rw::Atomic* GetFirstClumpAtomic( rw::Clump *clump )
{
rw::Atomic *firstAtom = NULL;
......@@ -189,12 +220,12 @@ static rw::Atomic* GetFirstClumpAtomic( rw::Clump *clump )
void ModelManager::LoadResource( streaming::ident_t localID, const void *dataBuf, size_t memSize )
{
exclusive_lock_acquire <std::shared_timed_mutex> ctxLoadModel( this->lockModelContext );
ModelResource *modelEntry = this->models[ localID ];
assert( modelEntry != NULL );
NativeSRW_Exclusive ctxLoadModel( modelEntry->lockModelLoading );
// Load the model resource.
rw::Object *modelPtr = NULL;
{
......@@ -208,48 +239,72 @@ void ModelManager::LoadResource( streaming::ident_t localID, const void *dataBuf
throw std::exception( "not a model resource" );
}
rw::Clump *newClump = rw::Clump::streamRead( &memoryStream );
if ( !newClump )
// Set the current TXD.
{
throw std::exception( "failed to parse model file" );
}
streaming::ident_t txdID = modelEntry->texDictID;
// Process it so that it is in the model format that we want.
eModelType modelType = modelEntry->modelType;
if ( txdID != -1 )
{
// TODO: this does not work because this thing is a global variable.
// It breaks multi-threaded loading, which is a real shame.
// Solution: wait till tomorrow so that aap has built in some texture lookup function!
texManager.SetCurrentTXD( txdID );
}
}
if ( modelType == eModelType::ATOMIC )
try
{
// We just store an atomic.
rw::Atomic *firstAtom = GetFirstClumpAtomic( newClump );
rw::Clump *newClump = rw::Clump::streamRead( &memoryStream );
if ( !newClump )
{
throw std::exception( "failed to parse model file" );
}
// Process it so that it is in the model format that we want.
eModelType modelType = modelEntry->modelType;
if ( firstAtom )
if ( modelType == eModelType::ATOMIC )
{
rw::Atomic *clonedObject = firstAtom->clone();
// We just store an atomic.
rw::Atomic *firstAtom = GetFirstClumpAtomic( newClump );
if ( clonedObject )
if ( firstAtom )
{
modelPtr = (rw::Object*)clonedObject;
rw::Atomic *clonedObject = firstAtom->clone();
if ( clonedObject )
{
modelPtr = (rw::Object*)clonedObject;
}
}
newClump->destroy();
}
else if ( modelType == eModelType::VEHICLE ||
modelType == eModelType::PED )
{
// Just store it as clump.
modelPtr = (rw::Object*)newClump;
}
else
{
assert( 0 );
}
newClump->destroy();
}
else if ( modelType == eModelType::VEHICLE ||
modelType == eModelType::PED )
{
// Just store it as clump.
modelPtr = (rw::Object*)newClump;
if ( modelPtr == NULL )
{
throw std::exception( "invalid model file" );
}
}
else
catch( ... )
{
assert( 0 );
}
texManager.UnsetCurrentTXD();
if ( modelPtr == NULL )
{
throw std::exception( "invalid model file" );
throw;
}
texManager.UnsetCurrentTXD();
}
// Store us. :)
......@@ -258,34 +313,19 @@ void ModelManager::LoadResource( streaming::ident_t localID, const void *dataBuf
void ModelManager::UnloadResource( streaming::ident_t localID )
{
exclusive_lock_acquire <std::shared_timed_mutex> ctxUnloadModel( this->lockModelContext );
ModelResource *modelEntry = this->models[ localID ];
assert( modelEntry != NULL );
NativeSRW_Exclusive ctxUnloadModel( modelEntry->lockModelLoading );
// Delete GPU data.
{
assert( modelEntry->modelPtr != NULL );
rw::uint8 modelType = modelEntry->modelPtr->type;
if ( modelType == rw::Atomic::ID )
{
rw::Atomic *atomic = (rw::Atomic*)modelEntry->modelPtr;
atomic->destroy();
}
else if ( modelType == rw::Clump::ID )
{
rw::Clump *clump = (rw::Clump*)modelEntry->modelPtr;
rw::Object *rwobj = modelEntry->modelPtr;
clump->destroy();
}
else
{
assert( 0 );
}
ModelResource::NativeReleaseModel( rwobj );
}
modelEntry->modelPtr = NULL;
......
......@@ -2,18 +2,49 @@
#include "TexDict.h"
#include "Game.h"
#include "NativePerfLocks.h"
namespace krt
{
TextureManager::TextureManager( streaming::StreamMan& streaming ) : streaming( streaming )
{
// WARNING: there can only be ONE texture manager!
// This is because of TLS things, and because we dont have a sophisticated enough treading library that can abstract problems away.
bool success = streaming.RegisterResourceType( TXD_START_ID, MAX_TXD, this );
assert( success == true );
// We register our texture find callback and use it solely from here on.
this->_tmpStoreOldCB = rw::Texture::findCB;
rw::Texture::findCB = _rwFindTextureCallback;
LIST_CLEAR( this->_tlCurrentTXDEnvList.root );
}
TextureManager::~TextureManager( void )
{
// Unregister all current TXD environments.
{
exclusive_lock_acquire <std::shared_timed_mutex> ctxUnregisterTXDEnvs( this->lockTXDEnvList );
while ( !LIST_EMPTY( this->_tlCurrentTXDEnvList.root ) )
{
ThreadLocal_CurrentTXDEnv *env = LIST_GETITEM(ThreadLocal_CurrentTXDEnv, this->_tlCurrentTXDEnvList.root.next, node);
LIST_REMOVE( env->node );
env->isRegistered = false;
env->manager = NULL;
}
}
// Unregister our find CB.
rw::Texture::findCB = this->_tmpStoreOldCB;
// We expect that things cannot stream anymore at this point.
// Delete all resource things.
......@@ -43,8 +74,14 @@ void TextureManager::RegisterResource( std::string name, vfs::DevicePtr device,
resEntry->parentID = -1;
resEntry->txdPtr = NULL;
resEntry->lockResourceLoad = SRWLOCK_INIT;
bool couldLink = streaming.LinkResource( curID, name, &resEntry->vfsResLoc );
// German radio is really really low quality with songs that are sung by wanna-be musicians.
// I heavily dislike those new sarcastic songs. It is one reason why I dislike the German society aswell.
// Not to say that the German society is honor based, which makes it good again.
if ( !couldLink )
{
// Dang, something failed. Bail :/
......@@ -59,6 +96,39 @@ void TextureManager::RegisterResource( std::string name, vfs::DevicePtr device,
this->texDictList.push_back( resEntry );
}
TextureManager::ThreadLocal_CurrentTXDEnv* TextureManager::GetCurrentTXDEnv( void )
{
static thread_local ThreadLocal_CurrentTXDEnv txdEnv( this );
return &txdEnv;
}
rw::Texture* TextureManager::_rwFindTextureCallback( const char *name )
{
Game *theGame = krt::theGame;
if ( theGame )
{
TextureManager& texManager = theGame->GetTextureManager();
ThreadLocal_CurrentTXDEnv *txdEnv = texManager.GetCurrentTXDEnv();
if ( txdEnv )
{
// TODO: add parsing of parent TXD archives.
rw::TexDictionary *currentTXD = txdEnv->currentTXD;
if ( currentTXD )
{
return currentTXD->find( name );
}
}
}
return NULL;
}
TextureManager::TexDictResource* TextureManager::FindTexDictInternal( const std::string& name ) const
{
auto findIter = this->texDictMap.find( name );
......@@ -73,8 +143,6 @@ TextureManager::TexDictResource* TextureManager::FindTexDictInternal( const std:
streaming::ident_t TextureManager::FindTexDict( const std::string& name ) const
{
shared_lock_acquire <std::shared_timed_mutex> ctxFindTXD( this->lockTextureContext );
TexDictResource *texRes = FindTexDictInternal( name );
if ( !texRes )
......@@ -87,18 +155,20 @@ streaming::ident_t TextureManager::FindTexDict( const std::string& name ) const
void TextureManager::SetTexParent( const std::string& texName, const std::string& texParentName )
{
shared_lock_acquire <std::shared_timed_mutex> ctxSetParent( this->lockTextureContext );
TexDictResource *texDict = this->FindTexDictInternal( texName );
if ( !texDict )
return;
NativeSRW_Exclusive ctxSetParent( texDict->lockResourceLoad );
TexDictResource *texParentDict = this->FindTexDictInternal( texParentName );
if ( !texParentDict )
return;
NativeSRW_Shared ctxIsParent( texParentDict->lockResourceLoad );
// Remove any previous link.
streaming::ident_t mainID = texDict->id;
{
......@@ -130,12 +200,51 @@ void TextureManager::SetTexParent( const std::string& texName, const std::string
return;
}
void TextureManager::LoadResource( streaming::ident_t localID, const void *dataBuf, size_t memSize )
void TextureManager::SetCurrentTXD( streaming::ident_t id )
{
if ( id < TXD_START_ID || id >= TXD_START_ID + this->texDictList.size() )
return;
id -= TXD_START_ID;
TexDictResource *texDict = this->texDictList[ id ];
if ( texDict == NULL )
return;
NativeSRW_Shared ctxSetCurrentTXD( texDict->lockResourceLoad );
// Set it as current TXD.
{
rw::TexDictionary *txdPtr = texDict->txdPtr;
if ( txdPtr )
{
ThreadLocal_CurrentTXDEnv *txdEnv = this->GetCurrentTXDEnv();
txdEnv->currentTXD = txdPtr;
}
}
}
void TextureManager::UnsetCurrentTXD( void )
{
exclusive_lock_acquire <std::shared_timed_mutex> ctxLoadTXD( this->lockTextureContext );
ThreadLocal_CurrentTXDEnv *txdEnv = this->GetCurrentTXDEnv();
if ( txdEnv )
{
txdEnv->currentTXD = NULL;
}
}
void TextureManager::LoadResource( streaming::ident_t localID, const void *dataBuf, size_t memSize )
{
TexDictResource *texEntry = this->texDictList[ localID ];
assert( texEntry != NULL );
NativeSRW_Exclusive ctxLoadTXD( texEntry->lockResourceLoad );
// Load the TXD resource.
rw::TexDictionary *txdRes = NULL;
{
......@@ -163,15 +272,33 @@ void TextureManager::LoadResource( streaming::ident_t localID, const void *dataB
void TextureManager::UnloadResource( streaming::ident_t localID )
{
exclusive_lock_acquire <std::shared_timed_mutex> ctxUnloadTXD( this->lockTextureContext );
TexDictResource *texEntry = this->texDictList[ localID ];
assert( texEntry != NULL );
NativeSRW_Exclusive( texEntry->lockResourceLoad );
// Unload the TXD again.
{
assert( texEntry->txdPtr != NULL );
rw::TexDictionary *txdObj = texEntry->txdPtr;
assert( txdObj != NULL );
// Make sure we dont have this TXD object as active current TXD or something.
{
shared_lock_acquire <std::shared_timed_mutex> ctxCleanUpCurrentTXD( this->lockTXDEnvList );
LIST_FOREACH_BEGIN(ThreadLocal_CurrentTXDEnv, this->_tlCurrentTXDEnvList.root, node)
if ( item->currentTXD == txdObj )
{
item->currentTXD = NULL;
}
LIST_FOREACH_END
}
texEntry->txdPtr->destroy();
txdObj->destroy();
}
texEntry->txdPtr = NULL;
......