17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-30 17:50:29 +01:00

perf(gpucull): keep the walks of the submodel trees of the instances from frame to frame and make them again only when the model changes (gfx.gpucull.walkcache)

This commit is contained in:
2026-10-24 01:01:43 +02:00
parent 794ac578de
commit 3e2d292709
10 changed files with 264 additions and 36 deletions

View File

@@ -658,6 +658,11 @@ void TAnimModel::on_season_update() {
void TAnimModel::RaPrepare()
{ // ustawia światła i animacje we wzorcu modelu przed renderowaniem egzemplarza
if ((pModel != nullptr) && ((iNumLights > 0) || (false == m_animlist.empty())))
{
// the lights and animations of this instance are set in the submodels the other instances share
pModel->state_changed();
}
if (Global.UpdateMaterials)
on_season_update();
for (int i = 0; i < iNumLights; ++i)

View File

@@ -1617,6 +1617,7 @@ void TModel3d::AddTo(TSubModel *tmp, TSubModel *SubModel)
++iSubModelsCount; // teraz jest o 1 submodel więcej
iFlags |= 0x0200; // submodele są oddzielne
m_lodboundsfound = false; // the new submodel may have its own range
state_changed();
};
TSubModel *TModel3d::GetFromName(std::string const &Name) const

View File

@@ -294,6 +294,7 @@ private:
bool m_instancesubmodelslocated { false };
std::vector<float> m_lodbounds; // filled on first use, see lod_bounds()
bool m_lodboundsfound { false };
std::uint32_t m_stateversion { 0 }; // see state_version()
public:
TModel3d() = default;
@@ -308,6 +309,13 @@ public:
// returns the distinct bounds of the visibility ranges of the submodels (fSquareMinDist and fSquareMaxDist, squared), sorted:
// they split the distance into bands in which the same submodels are drawn. found once for the model, like instance_parts()
std::vector<float> const & lod_bounds();
// the submodels of the model are shared by everything drawn with it, which sets their state (lights, animations, visibility)
// before it's drawn. the version changes when something which may set it differently than the last time did, or the submodels
// changed, so the renderer can tell whether what it worked out from them still holds
std::uint32_t state_version() const {
return m_stateversion; }
void state_changed() {
++m_stateversion; }
TSubModel * AddToNamed(const char *Name, TSubModel *SubModel);
nameoffset_sequence const & find_smoke_sources();
void AddTo(TSubModel *tmp, TSubModel *SubModel);

View File

@@ -46,6 +46,8 @@ struct texture_request {
// handed to the workers (opengl_texture::prefetch()), and when: the main thread only
bool submitted { false };
std::chrono::steady_clock::time_point submittime;
bool ahead { false }; // read ahead of the camera, which was at the origin then
glm::dvec3 origin { 0.0 };
// results, written by the thread reading the data
std::vector<unsigned char> data;
bool failed { false };
@@ -1052,6 +1054,12 @@ opengl_texture::create( bool const Static ) {
components_hint = GL_SRGB_ALPHA;
GLint internal_format = mapping[components][components_hint];
if( ( internal_format == 0 )
&& ( f_it != precompressed_formats.end() ) ) {
// data read back from the card (Global.ResourceMove) is in the format it was made in, which can be one the
// mapping doesn't start from (srgb)
internal_format = data_format;
}
auto blocksize_it = precompressed_formats.find(internal_format);
@@ -1090,7 +1098,8 @@ opengl_texture::create( bool const Static ) {
}
if( true == Global.ResourceMove
|| false == Global.ResourceSweep ) {
|| false == Global.ResourceSweep
|| true == streamed() ) {
// if garbage collection is disabled we don't expect having to upload the texture more than once
data = std::vector<unsigned char>();
data_state = resource_state::none;
@@ -1133,17 +1142,40 @@ opengl_texture::complete_data() {
size = data.size() / 1024;
}
// has a worker thread read the data of the texture, if it's waiting for that
// has a worker thread read the data of the texture, if it's waiting for that. Origin: the camera, for data read ahead of it
void
opengl_texture::prefetch() {
opengl_texture::prefetch( glm::dvec3 const *Origin ) {
if( ( request == nullptr )
&& ( id == invalid_id )
&& ( data_state == resource_state::none )
&& ( true == reload_on_use )
&& ( true == streamed() ) ) {
// the data went with the gl texture, it's read from the file again
reload_on_use = false;
load();
}
if( ( request == nullptr ) || ( true == request->submitted ) ) { return; }
request->submitted = true;
request->submittime = std::chrono::steady_clock::now();
request->ahead = ( Origin != nullptr );
if( Origin != nullptr ) {
request->origin = *Origin;
}
texture_loader::instance().submit( request );
}
// the data of the texture is read from its file by worker threads when it's needed (gfx.textures.streaming), and not kept
// after the upload: it can be read again
bool
opengl_texture::streamed() const {
return ( ( true == Global.gfx_texture_streaming )
&& ( type == ".dds" )
&& ( false == is_rendertarget ) );
}
// releases resources allocated on the opengl end, storing local copy if requested
void
opengl_texture::release() {
@@ -1151,7 +1183,13 @@ opengl_texture::release() {
if( id == -1 ) { return; }
if( is_static ) { return; }
if( true == Global.ResourceMove && !is_rendertarget ) {
if( true == streamed() ) {
// no copy of the data is kept, it's read from the file again when the texture is needed (prefetch(), create())
data = std::vector<unsigned char>();
data_state = resource_state::none;
reload_on_use = true;
}
else if( true == Global.ResourceMove && !is_rendertarget ) {
// if resource move is enabled we don't keep a cpu side copy after upload
// 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
@@ -1552,19 +1590,19 @@ texture_manager::memory() const {
// has the data of a texture read, if it's waiting for that
void
texture_manager::prefetch( texture_handle const Texture ) {
texture_manager::prefetch( texture_handle const Texture, glm::dvec3 const &Origin ) {
if( ( Texture <= 0 )
|| ( Texture >= static_cast<texture_handle>( m_textures.size() ) ) ) {
return;
}
m_textures[ Texture ].first->prefetch();
m_textures[ Texture ].first->prefetch( &Origin );
}
// gives back the data read for textures which nothing drew within the specified time since it was asked for. it's read again when
// a texture is needed
// gives back the data read for textures which nothing drew within the specified time since it was asked for, or read ahead of the
// camera which went farther than the specified distance from there since. it's read again when a texture is needed
std::size_t
texture_manager::drop_read_ahead( std::chrono::steady_clock::duration const Age ) {
texture_manager::drop_read_ahead( std::chrono::steady_clock::duration const Age, glm::dvec3 const &Camera, double const Distance ) {
auto const now { std::chrono::steady_clock::now() };
std::size_t count { 0 };
@@ -1574,8 +1612,11 @@ texture_manager::drop_read_ahead( std::chrono::steady_clock::duration const Age
continue;
}
auto &request { *texture.first->request };
if( ( false == request.submitted )
|| ( now - request.submittime < Age ) ) {
if( false == request.submitted ) {
continue;
}
if( ( now - request.submittime < Age )
&& ( ( false == request.ahead ) || ( glm::length2( Camera - request.origin ) <= Distance * Distance ) ) ) {
continue;
}
if( true == texture_loader::instance().drop( request ) ) {

View File

@@ -118,8 +118,11 @@ public:
// takes over the data read by a worker thread, waiting for it if needed
void complete_data();
// has a worker thread read the data of the texture, if it's waiting for that (gfx.textures.streaming)
void prefetch();
// has a worker thread read the data of the texture, if it's waiting for that (gfx.textures.streaming). Origin: the camera,
// for data read ahead of it
void prefetch( glm::dvec3 const *Origin = nullptr );
// the data is read from the file when it's needed and not kept after the upload (gfx.textures.streaming)
bool streamed() const;
// members
std::shared_ptr<texture_request> request; // data being read by a worker thread
@@ -199,10 +202,11 @@ public:
memory() const;
// has the data of a texture read, if it's waiting for that (gfx.textures.streaming)
void
prefetch( texture_handle const Texture );
// gives back the data read for textures which nothing drew within the specified time since it was asked for
prefetch( texture_handle const Texture, glm::dvec3 const &Origin );
// gives back the data read for textures which nothing drew within the specified time since it was asked for, or read ahead of
// the camera which went farther than the specified distance from there since
std::size_t
drop_read_ahead( std::chrono::steady_clock::duration const Age );
drop_read_ahead( std::chrono::steady_clock::duration const Age, glm::dvec3 const &Camera, double const Distance );
private:
// types: