mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-30 09:40:29 +01:00
Cull the scenery models drawn on their own on the GPU with the instances
This commit is contained in:
@@ -82,6 +82,7 @@ void opengl46_instance_cull::begin_pass(pass_setup const &Setup)
|
||||
m_entries = 0;
|
||||
m_sleepertracks = 0;
|
||||
m_sleepers = 0;
|
||||
m_models = 0;
|
||||
}
|
||||
|
||||
void opengl46_instance_cull::add_bucket(scene::basic_cell::instance_bucket_key const &Key, scene::basic_cell::instance_bucket const &Bucket, scene::bounding_area const &Area)
|
||||
@@ -196,6 +197,10 @@ void opengl46_instance_cull::add(scene::basic_cell::instance_bucket_key const &K
|
||||
++m_sleepertracks;
|
||||
m_sleepers += job.count;
|
||||
}
|
||||
else if (Key.instance != nullptr)
|
||||
{
|
||||
++m_models;
|
||||
}
|
||||
}
|
||||
|
||||
void opengl46_instance_cull::place(scene::basic_cell::instance_bucket const &Bucket)
|
||||
@@ -421,6 +426,22 @@ void opengl46_instance_cull::cpu_result(scene::basic_cell::instance_bucket_key c
|
||||
m_cpucounts[group.entryfirst + band] += Bandcounts[band];
|
||||
}
|
||||
|
||||
void opengl46_instance_cull::cpu_instance(scene::basic_cell::instance_bucket_key const &Key, float const Distancesquared)
|
||||
{
|
||||
if (!m_dispatched)
|
||||
return;
|
||||
auto const lookup{m_groupmap.find(Key)};
|
||||
if (lookup == m_groupmap.end())
|
||||
return;
|
||||
auto const &group{lookup->second};
|
||||
auto const &lods{m_grouplist[group.index]};
|
||||
// the band the culling gives the distance: the number of bounds it reached
|
||||
auto const first{m_lodlist.begin() + lods.lodfirst};
|
||||
auto const band{static_cast<std::uint32_t>(std::upper_bound(first, first + lods.lodcount, Distancesquared) - first)};
|
||||
if (band < group.entrycount)
|
||||
++m_cpucounts[group.entryfirst + band];
|
||||
}
|
||||
|
||||
void opengl46_instance_cull::end_pass()
|
||||
{
|
||||
// the counts of both phases, for the panel and the comparison
|
||||
@@ -439,6 +460,7 @@ void opengl46_instance_cull::end_pass()
|
||||
}
|
||||
m_lastsleepertracks = m_sleepertracks;
|
||||
m_lastsleepers = m_sleepers;
|
||||
m_lastmodels = m_models;
|
||||
m_inpass = false;
|
||||
m_dispatched = false;
|
||||
}
|
||||
@@ -490,7 +512,10 @@ std::string opengl46_instance_cull::info(std::string const &Label, bool const Oc
|
||||
if (Global.gfx_gpucull == 0)
|
||||
return {};
|
||||
// the sleepers are among the instances counted
|
||||
auto const sleepers{m_lastsleepertracks > 0 ? "\n" + Label + ": " + std::to_string(m_lastsleepers) + " sleepers of " + std::to_string(m_lastsleepertracks) + " tracks queued" : std::string{}};
|
||||
auto sleepers{m_lastsleepertracks > 0 ? "\n" + Label + ": " + std::to_string(m_lastsleepers) + " sleepers of " + std::to_string(m_lastsleepertracks) + " tracks queued" : std::string{}};
|
||||
// and so are the models drawn on their own
|
||||
if (m_lastmodels > 0)
|
||||
sleepers += "\n" + Label + ": " + std::to_string(m_lastmodels) + " models drawn on their own queued";
|
||||
if (Global.gfx_gpucull == 1)
|
||||
{
|
||||
if (m_last.frame == 0)
|
||||
|
||||
@@ -49,6 +49,8 @@ class TAnimModel;
|
||||
// queues the bucket
|
||||
// the sleepers of tracks (TTrack::m_sleeper_local_transforms) are culled the same way, each track with them a bucket of its
|
||||
// own, and drawn together with the instances; their groups are kept apart from those of the model instances
|
||||
// so are the model instances drawn on their own (not instanceable): each one a bucket and a group of its own (the instance in
|
||||
// its key), whose submodel tree the renderer animates and walks for it alone
|
||||
class opengl46_instance_cull
|
||||
{
|
||||
public:
|
||||
@@ -176,6 +178,9 @@ class opengl46_instance_cull
|
||||
// counts per lod band of the instances Render_Instanced() drew for a model and its skins in this pass, or of the sleepers
|
||||
// Render_Sleepers() drew. needs dispatch() before it
|
||||
void cpu_result(scene::basic_cell::instance_bucket_key const &Key, std::vector<std::uint32_t> const &Bandcounts, bool const Sleepers = false);
|
||||
// the cpu drew the instance of a group of its own (Key with the instance) at a squared distance scaled as the culling
|
||||
// scales it: counted in the lod band the distance falls in. needs dispatch() before it
|
||||
void cpu_instance(scene::basic_cell::instance_bucket_key const &Key, float const Distancesquared);
|
||||
// ends the pass: its cpu counts are kept for the comparison
|
||||
void end_pass();
|
||||
// takes the newest counts of the gpu which arrived and compares them with the ones of the cpu. done when a pass begins;
|
||||
@@ -275,6 +280,9 @@ class opengl46_instance_cull
|
||||
std::uint32_t m_sleepers{0};
|
||||
std::uint32_t m_lastsleepertracks{0};
|
||||
std::uint32_t m_lastsleepers{0};
|
||||
// model instances drawn on their own queued in the pass, and in the last pass, for the panel
|
||||
std::uint32_t m_models{0};
|
||||
std::uint32_t m_lastmodels{0};
|
||||
bool m_dispatched{false};
|
||||
|
||||
std::vector<std::uint32_t> m_cpucounts; // of the current pass, by entry
|
||||
|
||||
@@ -685,8 +685,9 @@ bool opengl46_renderer::Render()
|
||||
m_locallights.reset();
|
||||
m_localshadows.reset_lamps();
|
||||
m_localshadows.reset_headlights();
|
||||
// and so may the tracks the sleeper records were made for
|
||||
// and so may the tracks the sleeper records were made for, and the models drawn on their own
|
||||
m_sleeperrecords.clear();
|
||||
m_modelrecords.clear();
|
||||
}
|
||||
// generate new frame
|
||||
opengl_texture::reset_unit_cache();
|
||||
@@ -705,6 +706,11 @@ bool opengl46_renderer::Render()
|
||||
if( m_framestamp - records->second.frame > 256 ) { records = m_sleeperrecords.erase( records ); }
|
||||
else { ++records; }
|
||||
}
|
||||
// and so are the records of the models drawn on their own
|
||||
for( auto records { m_modelrecords.begin() }; records != m_modelrecords.end(); ) {
|
||||
if( m_framestamp - records->second.frame > 256 ) { records = m_modelrecords.erase( records ); }
|
||||
else { ++records; }
|
||||
}
|
||||
}
|
||||
// scenery lamps which get shadows in this frame. their maps hold the scenery as of the version of the instance records
|
||||
if (simulation::is_ready && (false == Global.gfx_skiprendering))
|
||||
@@ -1025,6 +1031,7 @@ void opengl46_renderer::draw_debug_ui()
|
||||
ImGui::Checkbox("the same for the shadow maps of headlights (gfx.gpucull.shadows.headlights)", &Global.gfx_gpucull_shadows_headlights);
|
||||
ImGui::Checkbox("the same for the scenery of the shadow maps of lamps, when it's drawn next (gfx.gpucull.shadows.lamps)", &Global.gfx_gpucull_shadows_lamps);
|
||||
ImGui::Checkbox("the same for the sleepers of the tracks of the view (gfx.gpucull.sleepers)", &Global.gfx_gpucull_sleepers);
|
||||
ImGui::Checkbox("the same for the models drawn on their own, with lights, animations or skins of their own (gfx.gpucull.models)", &Global.gfx_gpucull_models);
|
||||
}
|
||||
ImGui::Checkbox("draw the depth of the whole scene before its colour (gfx.depthprepass.scene)", &Global.gfx_depthprepass_scene);
|
||||
if (Global.gfx_contactshadows > 0)
|
||||
@@ -3013,6 +3020,9 @@ void opengl46_renderer::Render(scene::basic_region *Region)
|
||||
counts.second.clear();
|
||||
}
|
||||
}
|
||||
// the models drawn on their own go with the instances of every pass culled on the gpu
|
||||
m_modelspass = ( m_instancecullpass && Global.gfx_gpucull_models );
|
||||
m_modelcpudistances.clear();
|
||||
// build a list of region sections to render
|
||||
glm::vec3 const cameraposition{m_renderpass.pass_camera.position()};
|
||||
auto const camerax = static_cast<int>(std::floor(cameraposition.x / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2));
|
||||
@@ -3361,8 +3371,9 @@ void opengl46_renderer::Render(cell_sequence::iterator First, cell_sequence::ite
|
||||
// cell that still holds a drawable instance. Shadows measure from the
|
||||
// real (viewport) camera, matching Render_Instanced()'s shadow branch;
|
||||
// every other gated mode measures from the pass camera. Non-instanced
|
||||
// scenery and vehicles below keep their own per-node culling and are
|
||||
// intentionally left untouched.
|
||||
// scenery drawn by the cpu and vehicles below keep their own per-node
|
||||
// culling and are intentionally left untouched; the non-instanced models
|
||||
// culled on the gpu (Queue_Model()) are left out with the cell the same way.
|
||||
{
|
||||
auto const &distancecamera = (
|
||||
m_renderpass.draw_mode == rendermode::shadows
|
||||
@@ -3374,17 +3385,21 @@ void opengl46_renderer::Render(cell_sequence::iterator First, cell_sequence::ite
|
||||
( cellnearestdistance * cellnearestdistance )
|
||||
/ ( static_cast<double>( Global.ZoomFactor ) * static_cast<double>( Global.ZoomFactor ) )
|
||||
/ static_cast<double>( Global.fDistanceFactor ) };
|
||||
if( cellnearestdistancesquared <= sq( static_cast<double>( m_renderpass.draw_range ) + 250.0 ) ) {
|
||||
auto const cellinrange { cellnearestdistancesquared <= sq( static_cast<double>( m_renderpass.draw_range ) + 250.0 ) };
|
||||
if( cellinrange ) {
|
||||
Queue_Instances( cell );
|
||||
}
|
||||
}
|
||||
// remaining (non-instanceable) opaque instance nodes go through the per-node path
|
||||
for (auto *instance : cell->m_instancesopaque)
|
||||
{
|
||||
if( instance->m_instanceable ) { continue; } // already handled via bucket
|
||||
// the model of a lamp would shadow everything in the map of the lamp
|
||||
if( instance == m_cubeskipinstance ) { continue; }
|
||||
Render(instance);
|
||||
// remaining (non-instanceable) opaque instance nodes go through the per-node path, or with the instances
|
||||
// to the gpu culling, each on its own
|
||||
for (auto *instance : cell->m_instancesopaque)
|
||||
{
|
||||
if( instance->m_instanceable ) { continue; } // already handled via bucket
|
||||
// the model of a lamp would shadow everything in the map of the lamp
|
||||
if( instance == m_cubeskipinstance ) { continue; }
|
||||
if( Queue_Model( instance, cellinrange ) ) { continue; }
|
||||
Render(instance);
|
||||
m_modelcomparekey = nullptr;
|
||||
}
|
||||
}
|
||||
// opaque parts of vehicles. the kept scenery part of the map of a lamp is drawn without them
|
||||
if( false == m_cubescenery ) {
|
||||
@@ -3415,7 +3430,9 @@ void opengl46_renderer::Render(cell_sequence::iterator First, cell_sequence::ite
|
||||
Queue_Instances( cell );
|
||||
for( auto *instance : cell->m_instancesopaque ) {
|
||||
if( instance->m_instanceable ) { continue; }
|
||||
if( Queue_Model( instance, true ) ) { continue; }
|
||||
Render( instance );
|
||||
m_modelcomparekey = nullptr;
|
||||
}
|
||||
}
|
||||
if( Global.reflectiontune.fidelity >= 2 ) {
|
||||
@@ -3485,6 +3502,10 @@ void opengl46_renderer::Render(cell_sequence::iterator First, cell_sequence::ite
|
||||
}
|
||||
}
|
||||
}
|
||||
// and the models drawn on their own
|
||||
for( auto const &model : m_modelcpudistances ) {
|
||||
m_passcull->cpu_instance( model.first, model.second );
|
||||
}
|
||||
}
|
||||
}
|
||||
for( auto &bucket : m_frame_instance_buckets ) {
|
||||
@@ -3501,6 +3522,8 @@ void opengl46_renderer::Render(cell_sequence::iterator First, cell_sequence::ite
|
||||
m_passcull = nullptr;
|
||||
}
|
||||
m_sleeperspass = false;
|
||||
m_modelspass = false;
|
||||
m_modelcpudistances.clear();
|
||||
}
|
||||
|
||||
void opengl46_renderer::Draw_Geometry(std::vector<gfx::geometrybank_handle>::iterator begin, std::vector<gfx::geometrybank_handle>::iterator end)
|
||||
@@ -3685,6 +3708,10 @@ void opengl46_renderer::Render(TAnimModel *Instance)
|
||||
if( radiussquared * Global.ZoomFactor / distancesquared < 0.003 * 0.003 ) {
|
||||
return;
|
||||
}
|
||||
if( m_modelcomparekey != nullptr ) {
|
||||
// queued in the gpu culling as well, to compare with (Queue_Model())
|
||||
m_modelcpudistances.emplace_back( *m_modelcomparekey, static_cast<float>( distancesquared ) );
|
||||
}
|
||||
|
||||
switch (m_renderpass.draw_mode)
|
||||
{
|
||||
@@ -3794,6 +3821,58 @@ void opengl46_renderer::Queue_Instances( scene::basic_cell const *Cell )
|
||||
}
|
||||
}
|
||||
|
||||
// queues a model instance drawn on its own in the gpu culling of the pass, as a bucket and a group of its own. the culling
|
||||
// makes the tests of Render( TAnimModel * ): visibility, frustum, the range of the instance, the draw range, the size on the
|
||||
// screen, and gives it the lod band of the bounds of its submodels (mindistance/maxdistance) its distance falls in, then
|
||||
// Record_Indirect_Draws() animates, prepares and walks its submodel tree from its origin, like it does for a group of
|
||||
// instanced ones
|
||||
bool opengl46_renderer::Queue_Model( TAnimModel *Instance, bool const Inrange )
|
||||
{
|
||||
// a billboard is turned to the camera from the modelview of the instance, which a walk from its origin doesn't have
|
||||
if( ( false == Instance->m_cameraindependent ) || ( Instance->Model() == nullptr ) ) { return false; }
|
||||
if( false == m_modelspass ) {
|
||||
// the depth pass of the whole scene culled and recorded it, the colour pass draws what it recorded
|
||||
return ( m_indirectcolourpending && m_indirectcolourmodels );
|
||||
}
|
||||
auto const drawing { m_passcull->drawing() };
|
||||
// a model hidden by an event, or in a cell wholly out of the range of the pass, would be left out by the culling
|
||||
if( ( false == Instance->visible() ) || ( false == Inrange ) ) { return drawing; }
|
||||
|
||||
auto &records { m_modelrecords[ Instance ] };
|
||||
auto &bucket { records.bucket };
|
||||
if( ( bucket.recordsversion != m_instancerecordsversion ) || ( bucket.records.size() != 1 ) ) {
|
||||
// the key, the instance and the record anew: the editor may have given the instance another model or skins, or
|
||||
// another instance may have taken the place of a deleted one
|
||||
auto &key { records.key };
|
||||
key = {};
|
||||
key.pModel = Instance->Model();
|
||||
if( auto const *material { Instance->Material() } ) {
|
||||
for( int skin = 0; skin < 5; ++skin ) { key.skins[ skin ] = material->replacable_skins[ skin ]; }
|
||||
}
|
||||
key.instance = Instance;
|
||||
bucket.instances.assign( 1, Instance );
|
||||
// the location of the instance as the origin of the record, which keeps the precision
|
||||
Update_Instance_Records( key.pModel, bucket, Instance->location() );
|
||||
// drawn by its bucket, though it isn't instanceable
|
||||
bucket.records.front().flags |= scene::basic_cell::instance_instanceable;
|
||||
records.area = scene::bounding_area{ Instance->location(), 0.f };
|
||||
}
|
||||
records.frame = m_framestamp;
|
||||
if( m_renderpass.draw_mode == rendermode::shadows ) {
|
||||
// a model with nothing to draw in the shadows is left out of them, like the instances. the answer depends on the
|
||||
// model and the skins only, and is shared by the instances with them
|
||||
auto modelkey { records.key };
|
||||
modelkey.instance = nullptr;
|
||||
if( false == Instances_Cast_Shadow( modelkey, bucket ) ) { return drawing; }
|
||||
}
|
||||
m_passcull->add_bucket( records.key, bucket, records.area );
|
||||
if( false == drawing ) {
|
||||
// the cpu draws it too, and tells what it drew for the comparison
|
||||
m_modelcomparekey = &records.key;
|
||||
}
|
||||
return drawing;
|
||||
}
|
||||
|
||||
// makes the records of the instances held by a bucket of a scene cell, with locations relative to Origin
|
||||
void opengl46_renderer::Update_Instance_Records( TModel3d const *Model, scene::basic_cell::instance_bucket const &Bucket, glm::dvec3 const &Origin )
|
||||
{
|
||||
@@ -4169,6 +4248,7 @@ void opengl46_renderer::Render_Instances_Indirect()
|
||||
m_indirectcolourpending = true;
|
||||
m_indirectcolourset = colourset;
|
||||
m_indirectcoloursleepers = m_sleeperspass;
|
||||
m_indirectcolourmodels = m_modelspass;
|
||||
if( m_scenerecording ) {
|
||||
// where the colour pass, which draws the record, draws them, in the light the walk had here
|
||||
auto &marker { m_scenedraws.emplace_back() };
|
||||
@@ -4242,6 +4322,10 @@ void opengl46_renderer::Record_Indirect_Draws( opengl46_instance_cull &Cull )
|
||||
TSubModel::iVariantSeed = 0;
|
||||
}
|
||||
else {
|
||||
if( group.key->instance != nullptr ) {
|
||||
// a model drawn on its own: its lights and animations, the way Render( TAnimModel * ) sets them up
|
||||
group.instance->RaAnimate( m_framestamp );
|
||||
}
|
||||
group.instance->RaPrepare();
|
||||
auto const *material { group.instance->Material() };
|
||||
if( material != nullptr ) {
|
||||
|
||||
@@ -449,6 +449,11 @@ class opengl46_renderer : public gfx_renderer {
|
||||
// queues the sleepers of a track in the gpu culling of the pass (Global.gfx_gpucull_sleepers), with the gates of
|
||||
// Render_Sleepers(), making their records first when they're missing or out of date
|
||||
void Queue_Sleepers( TTrack *Track );
|
||||
// queues a model instance drawn on its own in the gpu culling of the pass (Global.gfx_gpucull_models), as a bucket of
|
||||
// its own, making its record first when it's missing or out of date. Inrange: the cell of the instance lies within the
|
||||
// range of the pass. returns true when the instance isn't drawn by the cpu in the pass: it's drawn from the result of the
|
||||
// culling, or it was culled and recorded by the depth pass of the whole scene, or it lies out of range
|
||||
bool Queue_Model( TAnimModel *Instance, bool const Inrange );
|
||||
bool Render_cab(TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha = false);
|
||||
bool Render_interior( bool const Alpha = false );
|
||||
bool Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha = false );
|
||||
@@ -625,6 +630,22 @@ class opengl46_renderer : public gfx_renderer {
|
||||
unsigned int frame { 0 }; // queued last
|
||||
};
|
||||
std::unordered_map<TTrack const *, sleeper_records> m_sleeperrecords;
|
||||
// the model instances drawn on their own culled on the gpu (Global.gfx_gpucull_models), each one a bucket of its own with
|
||||
// a group of its own: the record made when the instance is queued first, and again when the instance records went out of
|
||||
// date (editor, events: m_instancerecordsversion). the records of instances not queued for a while are let go
|
||||
struct model_records {
|
||||
scene::basic_cell::instance_bucket bucket; // the instance alone
|
||||
scene::basic_cell::instance_bucket_key key; // the model, the skins and the instance
|
||||
scene::bounding_area area; // the location of the instance
|
||||
unsigned int frame { 0 }; // queued last
|
||||
};
|
||||
std::unordered_map<TAnimModel const *, model_records> m_modelrecords;
|
||||
bool m_modelspass { false }; // the models drawn on their own of the pass under way go to its gpu culling
|
||||
bool m_indirectcolourmodels { false }; // with m_indirectcolourpending: the depth pass culled and recorded them too
|
||||
// comparison (Global.gfx_gpucull 2): the key of the model Render( TAnimModel * ) is called for, and the models it drew in
|
||||
// the pass with their scaled squared distances, reported once the culling ran
|
||||
scene::basic_cell::instance_bucket_key const *m_modelcomparekey { nullptr };
|
||||
std::vector<std::pair<scene::basic_cell::instance_bucket_key, float>> m_modelcpudistances;
|
||||
bool m_sleeperspass { false }; // the sleepers of the pass under way go to its gpu culling (the one of the main view)
|
||||
bool m_indirectcoloursleepers { false }; // with m_indirectcolourpending: the depth pass culled and recorded the sleepers too
|
||||
// counts per lod band of the sleepers Render_Sleepers() drew in the pass, by model and skin, for the comparison (they're
|
||||
|
||||
Reference in New Issue
Block a user