mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-19 22:47:29 +02:00
Cull the scenery models drawn on their own on the GPU with the instances
This commit is contained in:
@@ -281,6 +281,7 @@ cab and other views go as before.
|
||||
| `gfx.gpucull.shadows.headlights yes/no` | `yes` | with `gfx.gpucull yes` or `compare`: the models of the shadow maps of headlights too (each map tested against the beam it is drawn with, to its reach, the distances from the real camera, as before) |
|
||||
| `gfx.gpucull.shadows.lamps yes/no` | `yes` | with `gfx.gpucull yes` or `compare`: the models of the scenery part of the shadow maps of scenery lamps too (each map tested against the reach of its lamp, as before); the part with the vehicles has no models of the scenery |
|
||||
| `gfx.gpucull.sleepers yes/no` | `yes` | with `gfx.gpucull yes` or `compare`: the sleepers of the tracks (`sleepermodel`) of the main view too, each one tested against the view and given its level of detail by its distance, as before (the tracks within `sleeperdistance` are chosen as before); they also take the occlusion test, the depth prepass and the two phases of the models |
|
||||
| `gfx.gpucull.models yes/no` | `yes` | with `gfx.gpucull yes` or `compare`: the models drawn on their own too (scenery models with lights, animations, or skins and animations given by events, which the instanced draws leave out), in every pass the instances are culled in: each such model is tested on its own like the others (visibility, the view, its range, the draw range, its size on the screen) and given the level of detail its distance picks from the `mindistance`/`maxdistance` of its submodels; the processor animates and prepares the model only to record what it draws. Models with billboards turned to the camera stay on the processor, and so do vehicles |
|
||||
| `gfx.depthprepass.scene yes/no` | `no` | the depth of the whole opaque scene of the main view (terrain, tracks, buildings, models, vehicles, the cab) is drawn before its colour, see below |
|
||||
| `gfx.depthprepass.replay yes/no` | `yes` | the scene is gone through once for its depth and its colour: what the colour pass draws is recorded and both are drawn from the record, see below. `no`: twice, as before |
|
||||
|
||||
@@ -413,7 +414,9 @@ editor goes through the first `dynamiclights` headlights, the reflections throug
|
||||
slot of the map), and for the scenery of the maps of lamps, `gpu cull lamp shadows, scenery 1:` and `2:` (the first and
|
||||
the second drawn in a frame), each with the numbers of its last drawing and the timings `instance cull headlight shadows 1:`
|
||||
… and `instance cull lamp shadows, scenery 1:` …. With `gfx.gpucull.sleepers` the row
|
||||
`gpu cull:` is followed by how many sleepers of how many tracks were queued (they are counted among the models).
|
||||
`gpu cull:` is followed by how many sleepers of how many tracks were queued (they are counted among the models). With
|
||||
`gfx.gpucull.models` each `gpu cull` row is followed by how many models drawn on their own were queued (also counted
|
||||
among the models).
|
||||
- With `gfx.depthprepass.scene yes` Shift+F12 shows the `scene depth:` row (draws of the depth pass and its processor time)
|
||||
and the timing `scene depth:` (its GPU time). With `gfx.depthprepass.replay yes` the row tells how many of the recorded
|
||||
parts (of models, of the scenery; neighbouring tracks count one by one) had their depth drawn, in how many calls, how many
|
||||
|
||||
@@ -511,6 +511,16 @@ bool submodel_tree_blocks_instancing( TSubModel const *Sub ) {
|
||||
if( submodel_tree_blocks_instancing( Sub->Next ) ) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns true if any submodel of the tree turns to the camera: a billboard is placed from the modelview of the drawn
|
||||
// instance, which a walk started from the origin of the instance doesn't have
|
||||
bool submodel_tree_faces_camera( TSubModel const *Sub ) {
|
||||
if( Sub == nullptr ) { return false; }
|
||||
if( ( Sub->b_Anim == TAnimType::at_Billboard ) || ( Sub->b_aAnim == TAnimType::at_Billboard ) ) { return true; }
|
||||
if( submodel_tree_faces_camera( Sub->Child ) ) { return true; }
|
||||
if( submodel_tree_faces_camera( Sub->Next ) ) { return true; }
|
||||
return false;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
int TAnimModel::s_instanceable_total = 0;
|
||||
@@ -530,6 +540,7 @@ void TAnimModel::update_instanceable_flag() {
|
||||
// refs are intentionally allowed — they're either passed per-instance via
|
||||
// Material() or share global state (season) across all instances.
|
||||
m_instanceable = false;
|
||||
m_cameraindependent = ( pModel != nullptr ) && ( false == submodel_tree_faces_camera( pModel->Root ) );
|
||||
++s_classified_total;
|
||||
if( pModel == nullptr ) { ++s_rejected_no_pmodel; return; }
|
||||
if( iNumLights != 0 ) { ++s_rejected_lights; return; }
|
||||
|
||||
@@ -228,6 +228,10 @@ public:
|
||||
// no lights, no submodel animations, no replacable skins, no seasonal variants.
|
||||
// computed once at end of Load() so the renderer can simply test the flag.
|
||||
bool m_instanceable { false };
|
||||
// true if the walk of the submodel tree gives the same submodel-local transforms whatever modelview it starts from
|
||||
// (no billboards turned to the camera): an instance drawn on its own can then be culled on the gpu like the instanced
|
||||
// ones, its tree walked from the origin of the instance, which the culling places in view
|
||||
bool m_cameraindependent { false };
|
||||
// helper: evaluates current state and updates m_instanceable accordingly.
|
||||
void update_instanceable_flag();
|
||||
// helper: assigns the model and the replacable skin to the instance, and binds light and variant submodels of the model
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -221,9 +221,12 @@ public:
|
||||
struct instance_bucket_key {
|
||||
TModel3d *pModel { nullptr };
|
||||
std::array<material_handle, 5> skins {};
|
||||
// an instance drawn on its own (not instanceable) culled on the gpu as a bucket of its own: it isn't drawn
|
||||
// together with other instances of the model, as its submodel tree is prepared and walked for it alone
|
||||
TAnimModel const *instance { nullptr };
|
||||
|
||||
bool operator==( instance_bucket_key const &other ) const {
|
||||
return pModel == other.pModel && skins == other.skins;
|
||||
return pModel == other.pModel && skins == other.skins && instance == other.instance;
|
||||
}
|
||||
};
|
||||
struct instance_bucket_key_hash {
|
||||
@@ -233,6 +236,7 @@ public:
|
||||
// boost-style hash combine
|
||||
h ^= std::hash<int>()( s ) + 0x9e3779b9 + ( h << 6 ) + ( h >> 2 );
|
||||
}
|
||||
h ^= std::hash<TAnimModel const *>()( k.instance ) + 0x9e3779b9 + ( h << 6 ) + ( h >> 2 );
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1479,6 +1479,11 @@ global_settings::ConfigParse_gfx( cParser &Parser, std::string_view const Token
|
||||
Parser.getTokens(1);
|
||||
Parser >> gfx_gpucull_sleepers;
|
||||
}
|
||||
else if (Token == "gfx.gpucull.models")
|
||||
{
|
||||
Parser.getTokens(1);
|
||||
Parser >> gfx_gpucull_models;
|
||||
}
|
||||
else if (Token == "gfx.depthprepass.scene")
|
||||
{
|
||||
Parser.getTokens(1);
|
||||
@@ -1985,6 +1990,7 @@ global_settings::export_as_text( std::ostream &Output ) const {
|
||||
export_as_text( Output, "gfx.gpucull.shadows.headlights", gfx_gpucull_shadows_headlights );
|
||||
export_as_text( Output, "gfx.gpucull.shadows.lamps", gfx_gpucull_shadows_lamps );
|
||||
export_as_text( Output, "gfx.gpucull.sleepers", gfx_gpucull_sleepers );
|
||||
export_as_text( Output, "gfx.gpucull.models", gfx_gpucull_models );
|
||||
export_as_text( Output, "gfx.depthprepass.scene", gfx_depthprepass_scene );
|
||||
export_as_text( Output, "gfx.depthprepass.replay", gfx_depthprepass_replay );
|
||||
export_as_text( Output, "gfx.bufferstreaming", gfx_bufferstreaming );
|
||||
|
||||
@@ -305,6 +305,10 @@ struct global_settings {
|
||||
// with gfx_gpucull 1 or 2: the sleepers of the tracks (sleepermodel) of the main view are culled with the instances as well,
|
||||
// and with 1 drawn from the result
|
||||
bool gfx_gpucull_sleepers = true;
|
||||
// with gfx_gpucull 1 or 2: the model instances drawn on their own (with lights, animations or skins of their own, which
|
||||
// keep them out of the instanced draws) are culled with the instances as well, each one a group of its own, in every pass
|
||||
// culled on the gpu, and with 1 drawn from the result. models with billboards stay on the cpu
|
||||
bool gfx_gpucull_models = true;
|
||||
// (opengl 4.6) the depth of the whole opaque scene of the main view is drawn before its colour, the instances included
|
||||
bool gfx_depthprepass_scene = false;
|
||||
// (opengl 4.6) the depth pass of the scene (gfx_depthprepass_scene, or the one of the contact shadows) walks the scene for
|
||||
|
||||
Reference in New Issue
Block a user