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

Draw the depth of the whole scene before its colour

This commit is contained in:
2026-10-17 02:15:50 +02:00
parent f352718e4a
commit 97112b143b
8 changed files with 274 additions and 63 deletions

View File

@@ -240,12 +240,22 @@ views go as before.
| `gfx.gpucull` | `no` | `no`: on the processor, as before. `yes`: on the graphics card, and the models are drawn from its result. `compare`: on the processor, which draws, and on the graphics card too, for the results to be compared (debugging) |
| `gfx.gpucull.occlusion yes/no` | `yes` | with `gfx.gpucull yes`: models hidden whole behind what was drawn before them (the cab, the terrain, buildings and other models drawn on their own, vehicles) are left out |
| `gfx.depthprepass yes/no` | `yes` | with `gfx.gpucull yes`: the depth of the models is drawn first, so the colour is worked out only for what stays in view (dense foliage) |
| `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 |
The occlusion test does not take models drawn this way as covering each other: a tree hidden behind another tree is still
drawn (its colour is not worked out with the depth prepass on). Models whose material is not one of the usual ones (parallax,
water and the like) are left out of the depth prepass and drawn as before. All three can be changed while the simulator
runs, in the **Instance culling** debug window.
With `gfx.depthprepass.scene yes` the scene is gone through twice: first only its depth is drawn, then its colour, worked
out once per pixel for the surface which stays in view. That costs the processor a second walk through the scene (fewer
draws than the colour pass: what the depth programs can't draw, like parallax materials and faded parts, is left to the
colour pass) and saves the graphics card the shading of hidden surfaces. It also gives the depth of the scene before its
colour, which the effects that need it build on. The models drawn from the GPU (`gfx.gpucull yes`) are culled in the first
walk and drawn from the same result in the second. The picture is the same either way. In rain and snow (when the cab is
drawn last, after the precipitation) the cab isn't in the depth pass. Can be changed while the simulator runs, in the
**Instance culling** debug window.
### Model data of the draws
Before every draw the renderer hands the graphics card the data of the drawn part of the model (its position in the view, its
@@ -317,6 +327,8 @@ editor goes through the first `dynamiclights` headlights, the reflections throug
of detail, the draw calls with those of the depth prepass, and the processor time of the whole step) and the timings
`instance cull:`, `occlusion depth:` (the depth pyramid of the test) and `depth prepass:`. With `compare` it shows how many
models both counted and how many levels of detail differ.
- 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).
- Shift+F12 shows the `instance buffer:` row (positions of copies written in the frame and the size of the buffer) and the
`model blocks:` row: how many blocks of model data the frame wrote, with `gfx.bufferstreaming yes` also how much of the
buffer of the frame they took, how many times a frame had to wait for the graphics card to finish with a buffer before

View File

@@ -374,6 +374,14 @@ geometry_bank::vertices( gfx::geometry_handle const &Geometry ) const {
return geometry_bank::chunk( Geometry ).vertices;
}
// primitive type of specified chunk, 0 if there's no such chunk
unsigned int
geometry_bank::type( gfx::geometry_handle const &Geometry ) const {
if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return 0; }
return geometry_bank::chunk( Geometry ).type;
}
// provides direct access to vertex user data of specfied chunk
userdata_array const &
geometry_bank::userdata( gfx::geometry_handle const &Geometry ) const {
@@ -521,6 +529,14 @@ geometrybank_manager::vertices( gfx::geometry_handle const &Geometry ) const {
return bank( Geometry ).first->vertices( Geometry );
}
// primitive type of specified chunk, 0 if there's no such chunk
unsigned int
geometrybank_manager::type( gfx::geometry_handle const &Geometry ) const {
if( false == valid( Geometry ) ) { return 0; }
return bank( Geometry ).first->type( Geometry );
}
// provides direct access to vertex user data of specfied chunk
gfx::userdata_array const &
geometrybank_manager::userdata( gfx::geometry_handle const &Geometry ) const {

View File

@@ -153,6 +153,8 @@ public:
auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &;
// provides direct access to vertex user data of specfied chunk
auto userdata( gfx::geometry_handle const &Geometry ) const -> gfx::userdata_array const &;
// primitive type of specified chunk, 0 if there's no such chunk
auto type( gfx::geometry_handle const &Geometry ) const -> unsigned int;
protected:
// types:
@@ -260,6 +262,8 @@ public:
auto vertices( gfx::geometry_handle const &Geometry ) const -> gfx::vertex_array const &;
// provides direct access to vertex data of specfied chunk
auto userdata( gfx::geometry_handle const &Geometry ) const -> gfx::userdata_array const &;
// primitive type of specified chunk, 0 if there's no such chunk
auto type( gfx::geometry_handle const &Geometry ) const -> unsigned int;
// sets target texture unit for the texture data stream
auto units() -> gfx::stream_units & { return m_units; }
// provides access to primitives count

View File

@@ -116,6 +116,15 @@ class opengl46_instance_cull
void bind_draw();
// counts of the last result which came back from the gpu, with the draws of the pass, for the debug panel
void draw_result(std::uint32_t const Draws, std::uint32_t const Prepassdraws, double const Cputime);
// what draw_result() was given last
std::uint32_t prepass_draws() const
{
return m_prepassdraws;
}
double cpu_time() const
{
return m_cputime;
}
// instances which passed in the last result which came back from the gpu, a few frames old
std::uint32_t last_count() const
{

View File

@@ -740,6 +740,10 @@ void opengl46_renderer::SwapBuffers()
m_debugtimestext += m_exposure.info_times();
m_debugtimestext += m_ao.info_times();
m_debugtimestext += m_instancecull.info_times();
if( Global.gfx_depthprepass_scene ) {
if( auto const scenedepthtime = m_scenedepthtimer.result() )
m_debugtimestext += " scene depth: " + to_string( static_cast<double>( *scenedepthtime ) / 1000000.0, 3 ) + " ms\n";
}
if( Global.gfx_gpucull == 1 ) {
m_debugtimestext += m_hiz.info_times();
if( auto const prepasstime = m_prepasstimer.result() )
@@ -766,6 +770,9 @@ void opengl46_renderer::SwapBuffers()
+ " instanced:" + to_string( m_colorpass.draw_stats.instances, 7 ) + " +" + to_string( shadowstats.instances, 7 )
+ " =" + to_string( m_colorpass.draw_stats.instances + shadowstats.instances, 7 )
+ " (" + std::to_string( m_colorpass.draw_stats.instanced_drawcalls + shadowstats.instanced_drawcalls ) + " batches)\n"
+ ( Global.gfx_depthprepass_scene ?
" scene depth: " + std::to_string( m_scenedepthstats.drawcalls ) + " draws, cpu " + to_string( m_scenedepthcputime, 2 ) + " ms\n" :
std::string{} )
+ " instance buffer: " + std::to_string( m_instancestream->used() / static_cast<GLsizeiptr>( sizeof( glm::mat4 ) ) ) + " matrices, "
+ std::to_string( m_instancestream->size() / 1024 ) + " KiB\n"
+ " model blocks: " + std::to_string( model_ubo->updates() )
@@ -874,6 +881,7 @@ void opengl46_renderer::draw_debug_ui()
ImGui::Checkbox("leave out instances hidden behind the depth drawn before them (gfx.gpucull.occlusion)", &Global.gfx_gpucull_occlusion);
ImGui::Checkbox("draw the depth of the instances before their colour (gfx.depthprepass)", &Global.gfx_depthprepass);
}
ImGui::Checkbox("draw the depth of the whole scene before its colour (gfx.depthprepass.scene)", &Global.gfx_depthprepass_scene);
auto const info{m_instancecull.info()};
if (!info.empty())
ImGui::TextUnformatted(info.c_str() + 1);
@@ -1116,6 +1124,15 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
if( Global.gfx_shadowmap_enabled )
setup_shadow_bind_map();
// the depth of the whole opaque scene before its colour
m_scenedepthdrawn = false;
m_indirectcolourpending = false;
if (Global.gfx_depthprepass_scene && vp.main && (false == Global.bWireFrame))
{
glDebug("render scene depth");
Render_Scene_Depth(future);
}
// opaque parts...
setup_drawing(false);
@@ -1140,6 +1157,8 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
model_ubs.future = future;
Render(simulation::Region);
m_scenedepthdrawn = false;
m_indirectcolourpending = false;
Render_vr_models();
@@ -2508,6 +2527,31 @@ void opengl46_renderer::Bind_Material( material_handle const Material, TSubModel
material.textures :
material.GetTextures(TSubModel::VariantId(sm))};
if (m_scenedepthpass)
{
// the depth of the whole scene: the programs of the depth prepass, the same choice as for the instances in
// Render_Instances_Indirect(). what they can't draw (blended, materials of other programs) is left to the colour pass
m_scenedepthskip = true;
if (m_blendingenabled || (model_ubs.opacity < 0.f))
return;
auto const prepassclass{Prepass_Class(Material)};
if (prepassclass == 0)
return;
if (prepassclass == 2)
{
// a texture which isn't there (yet) would leave the one bound before to the alpha test
if ((materialtextures[0] == null_handle) || (false == m_textures.mark_as_used(materialtextures[0]).bind(0)))
return;
m_prepass_alpha_shader->bind();
}
else
{
m_prepass_shader->bind();
}
m_scenedepthskip = false;
return;
}
GLuint lastdiff = 0;
size_t i;
for (i = 0; i < gl::MAX_TEXTURES; i++)
@@ -2702,10 +2746,11 @@ void opengl46_renderer::Render(scene::basic_region *Region)
{
Render(std::begin(m_sectionqueue), std::end(m_sectionqueue));
// draw queue is filled while rendering sections
if (EditorModeFlag && m_current_viewport->main)
if (EditorModeFlag && m_current_viewport->main && (false == m_scenedepthdrawn))
{
// when editor mode is active calculate world position of the cursor
// at this stage the z-buffer is filled with only ground geometry
// (with the depth pass of the whole scene it's done there, at the same stage)
Update_Mouse_Position();
}
// draw opaque cells front-to-back: with the depth test enabled this
@@ -3099,6 +3144,24 @@ void opengl46_renderer::Render(cell_sequence::iterator First, cell_sequence::ite
// still happens inside Render_Instanced(), so correctness is unchanged -- only
// the number of calls and glBufferSubData round-trips drops sharply.
// (For pickscenery/pickcontrols modes the map stays empty, so this is a no-op.)
if( m_indirectcolourpending ) {
// the instances culled and recorded by the depth pass of the whole scene, the buckets weren't queued for the cpu
m_indirectcolourpending = false;
if( false == m_indirectdraws.empty() ) {
auto const cpustart { std::chrono::steady_clock::now() };
m_instancecull.bind_draw();
Render_Instances_Indirect_Colour();
::glBindBuffer( GL_DRAW_INDIRECT_BUFFER, 0 );
Bind_Instance_Stream();
m_instancecull.draw_result(
static_cast<std::uint32_t>( m_indirectdraws.size() ),
m_instancecull.prepass_draws(),
m_instancecull.cpu_time() + std::chrono::duration<double, std::milli>( std::chrono::steady_clock::now() - cpustart ).count() );
}
m_renderpass.draw_stats.instanced_drawcalls += static_cast<int>( m_indirectdraws.size() );
m_renderpass.draw_stats.instances += static_cast<int>( m_instancecull.last_count() );
m_renderpass.draw_stats.models += static_cast<int>( m_instancecull.last_count() );
}
if( m_instancecullpass ) {
if( m_instancecull.drawing() ) {
// the buckets of the pass weren't queued for the cpu, which leaves the loop below empty
@@ -3151,6 +3214,9 @@ void opengl46_renderer::draw(const gfx::geometry_handle &handle)
}
return;
}
if( m_scenedepthpass && ( false == Scene_Depth_Draws( handle ) ) ) {
return;
}
model_ubo->update(model_ubs);
if( m_current_instance_count > 0 ) {
@@ -3166,6 +3232,9 @@ void opengl46_renderer::draw(const gfx::geometry_handle &handle)
void opengl46_renderer::draw(std::vector<gfx::geometrybank_handle>::iterator it, std::vector<gfx::geometrybank_handle>::iterator end)
{
model_ubs.set_modelview(OpenGLMatrices.data(GL_MODELVIEW));
if( m_scenedepthpass && ( ( it == end ) || ( false == Scene_Depth_Draws( *it ) ) ) ) {
return;
}
model_ubo->update(model_ubs);
Draw_Geometry(it, end);
@@ -3370,6 +3439,8 @@ void opengl46_renderer::Queue_Instances( scene::basic_cell const *Cell )
// and when the instances are drawn from its result, the cpu has nothing more to do with them
if( m_instancecull.drawing() ) { continue; }
}
// the depth pass of the whole scene culled them already, the colour pass draws what it recorded
if( m_indirectcolourpending ) { continue; }
auto const &records { instancebucket.records };
std::vector<queued_instance> *dest { nullptr }; // looked up when there's something to put in it
for( std::size_t idx = 0; idx < records.size(); ++idx ) {
@@ -3672,7 +3743,7 @@ int opengl46_renderer::Prepass_Class( material_handle const Material )
"detail_normalmap", "detail_normalmap_specgloss", "normalmap", "normalmap_specgloss",
"reflmap", "reflmap_specgloss", "shadowlessnormalmap", "shadowlessnormalmap_specgloss",
"sunlessnormalmap", "sunlessnormalmap_specgloss" };
static std::unordered_set<std::string> const solid { "colored", "default_0" };
static std::unordered_set<std::string> const solid { "colored", "default_0", "terrain", "water", "water_specgloss", "invalid" };
auto prepassclass { 0 };
for( auto const &shader : m_shaders ) {
if( shader.second.get() != material.shader.get() ) { continue; }
@@ -3743,77 +3814,149 @@ void opengl46_renderer::Render_Instances_Indirect()
std::uint32_t prepassdraws { 0 };
if( false == m_indirectdraws.empty() ) {
m_instancecull.bind_draw();
auto const commandoffset = []( std::size_t const Index ) {
return static_cast<std::intptr_t>( Index * sizeof( gl::instance_draw_command ) ); };
if( Global.gfx_depthprepass && ( false == Global.bWireFrame ) ) {
// the depth first: the colour pass shades only what stays in view. the depth goes a little farther than the
// surface, so the colour pass, which tests and writes its depth as usual, is sure to pass where it's the same
m_prepasstimer.begin();
setup_drawing( false );
::glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
::glEnable( GL_POLYGON_OFFSET_FILL );
::glPolygonOffset( -1.f, -1.f );
// the draws without the alpha test first, then the ones with it, one program each
for( auto prepassclass = 1; prepassclass <= 2; ++prepassclass ) {
auto &program { prepassclass == 1 ? *m_prepass_shader : *m_prepass_alpha_shader };
auto bound { false };
for( std::size_t index = 0; index < m_indirectdraws.size(); ++index ) {
auto const &draw { m_indirectdraws[ index ] };
if( ( draw.blending ) || ( draw.ubs.opacity < 0.f ) ) { continue; }
if( ( draw.type != GL_TRIANGLES ) && ( draw.type != GL_TRIANGLE_STRIP ) && ( draw.type != GL_TRIANGLE_FAN ) ) { continue; }
if( Prepass_Class( draw.material ) != prepassclass ) { continue; }
if( prepassclass == 2 ) {
// the diffuse texture of the colour pass, the same choice as Bind_Material() makes
auto const &material { m_materials.material( draw.material ) };
auto const &textures {
material.texture_variants.empty() ?
material.textures :
material.GetTextures( TSubModel::VariantId( draw.submodel ) ) };
if( textures[ 0 ] == null_handle ) { continue; }
m_textures.bind( 0, textures[ 0 ] );
}
if( false == bound ) {
program.bind();
bound = true;
}
model_ubs = draw.ubs;
model_ubo->update( model_ubs );
m_geometry.draw_indirect( draw.geometry, commandoffset( index ) );
++prepassdraws;
}
if( m_scenedepthpass ) {
// the depth pass of the whole scene, which has its state set: only the depth here, the colour pass draws the
// colour of the same draws (m_indirectcolourpending)
prepassdraws = Render_Instances_Indirect_Depth();
}
else {
if( Global.gfx_depthprepass && ( false == Global.bWireFrame ) ) {
// the depth first: the colour pass shades only what stays in view. the depth goes a little farther than the
// surface, so the colour pass, which tests and writes its depth as usual, is sure to pass where it's the same
m_prepasstimer.begin();
setup_drawing( false );
::glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
::glEnable( GL_POLYGON_OFFSET_FILL );
::glPolygonOffset( -1.f, -1.f );
prepassdraws = Render_Instances_Indirect_Depth();
::glDisable( GL_POLYGON_OFFSET_FILL );
::glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
m_prepasstimer.end();
}
::glDisable( GL_POLYGON_OFFSET_FILL );
::glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
m_prepasstimer.end();
Render_Instances_Indirect_Colour();
}
for( std::size_t index = 0; index < m_indirectdraws.size(); ++index ) {
auto const &draw { m_indirectdraws[ index ] };
if( draw.blending ) { setup_drawing( true ); }
Bind_Material( draw.material, draw.submodel );
model_ubs = draw.ubs;
model_ubo->update( model_ubs );
m_geometry.draw_indirect( draw.geometry, commandoffset( index ) );
if( draw.blending ) { setup_drawing( false ); }
}
model_ubs.emission = 0.0f;
::glBindBuffer( GL_DRAW_INDIRECT_BUFFER, 0 );
Bind_Instance_Stream();
}
m_renderpass.draw_stats.instanced_drawcalls += static_cast<int>( m_indirectdraws.size() );
m_renderpass.draw_stats.drawcalls += static_cast<int>( prepassdraws );
// the count of the frame the gpu reported last
m_renderpass.draw_stats.instances += static_cast<int>( m_instancecull.last_count() );
m_renderpass.draw_stats.models += static_cast<int>( m_instancecull.last_count() );
if( m_scenedepthpass ) {
// the colour pass takes the draws from here, and counts them
m_indirectcolourpending = true;
m_renderpass.draw_stats.drawcalls += static_cast<int>( prepassdraws );
}
else {
m_renderpass.draw_stats.instanced_drawcalls += static_cast<int>( m_indirectdraws.size() );
m_renderpass.draw_stats.drawcalls += static_cast<int>( prepassdraws );
// the count of the frame the gpu reported last
m_renderpass.draw_stats.instances += static_cast<int>( m_instancecull.last_count() );
m_renderpass.draw_stats.models += static_cast<int>( m_instancecull.last_count() );
}
m_instancecull.draw_result(
static_cast<std::uint32_t>( m_indirectdraws.size() ),
prepassdraws,
std::chrono::duration<double, std::milli>( std::chrono::steady_clock::now() - cpustart ).count() );
}
std::uint32_t opengl46_renderer::Render_Instances_Indirect_Depth()
{
auto const commandoffset = []( std::size_t const Index ) {
return static_cast<std::intptr_t>( Index * sizeof( gl::instance_draw_command ) ); };
std::uint32_t draws { 0 };
// the draws without the alpha test first, then the ones with it, one program each
for( auto prepassclass = 1; prepassclass <= 2; ++prepassclass ) {
auto &program { prepassclass == 1 ? *m_prepass_shader : *m_prepass_alpha_shader };
auto bound { false };
for( std::size_t index = 0; index < m_indirectdraws.size(); ++index ) {
auto const &draw { m_indirectdraws[ index ] };
if( ( draw.blending ) || ( draw.ubs.opacity < 0.f ) ) { continue; }
if( ( draw.type != GL_TRIANGLES ) && ( draw.type != GL_TRIANGLE_STRIP ) && ( draw.type != GL_TRIANGLE_FAN ) ) { continue; }
if( Prepass_Class( draw.material ) != prepassclass ) { continue; }
if( prepassclass == 2 ) {
// the diffuse texture of the colour pass, the same choice as Bind_Material() makes
auto const &material { m_materials.material( draw.material ) };
auto const &textures {
material.texture_variants.empty() ?
material.textures :
material.GetTextures( TSubModel::VariantId( draw.submodel ) ) };
if( textures[ 0 ] == null_handle ) { continue; }
m_textures.bind( 0, textures[ 0 ] );
}
if( false == bound ) {
program.bind();
bound = true;
}
model_ubs = draw.ubs;
model_ubo->update( model_ubs );
m_geometry.draw_indirect( draw.geometry, commandoffset( index ) );
++draws;
}
}
return draws;
}
void opengl46_renderer::Render_Instances_Indirect_Colour()
{
auto const commandoffset = []( std::size_t const Index ) {
return static_cast<std::intptr_t>( Index * sizeof( gl::instance_draw_command ) ); };
for( std::size_t index = 0; index < m_indirectdraws.size(); ++index ) {
auto const &draw { m_indirectdraws[ index ] };
if( draw.blending ) { setup_drawing( true ); }
Bind_Material( draw.material, draw.submodel );
model_ubs = draw.ubs;
model_ubo->update( model_ubs );
m_geometry.draw_indirect( draw.geometry, commandoffset( index ) );
if( draw.blending ) { setup_drawing( false ); }
}
model_ubs.emission = 0.0f;
}
// depth of the whole opaque scene of the colour pass, before its colour. the same walk as the colour pass makes (the cab drawn
// early, then the region), so the same things are seen at the same levels of detail; the programs of the depth prepass draw
// them (Bind_Material(), draw()), with the depth a little farther than the surface like the prepass of the instances, so the
// colour pass, which tests and writes its depth as usual, passes where it's the same surface. the gpu culling of the
// instances runs here, against the same depth as without this pass, and the colour pass draws what it recorded
void opengl46_renderer::Render_Scene_Depth( glm::mat4 const &Future )
{
auto const cpustart { std::chrono::steady_clock::now() };
m_scenedepthtimer.begin();
auto const colourstats { m_renderpass.draw_stats };
m_renderpass.draw_stats = {};
m_scenedepthpass = true;
m_scenedepthskip = true;
setup_drawing( false );
::glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
::glEnable( GL_POLYGON_OFFSET_FILL );
::glPolygonOffset( -1.f, -1.f );
// the conditions of the cab drawn early by the colour pass
if( ( false == FreeFlyModeFlag ) && ( Global.Overcast <= 1.0f ) && ( Global.render_cab ) ) {
model_ubs.future = glm::mat4();
auto const *vehicle { simulation::Train->Dynamic() };
Render_cab( vehicle, vehicle->InteriorLightLevel, false );
}
model_ubs.future = Future;
Render( simulation::Region );
::glDisable( GL_POLYGON_OFFSET_FILL );
::glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
m_scenedepthpass = false;
m_scenedepthskip = true;
m_scenedepthdrawn = true;
m_scenedepthstats = m_renderpass.draw_stats;
m_renderpass.draw_stats = colourstats;
m_scenedepthtimer.end();
m_scenedepthcputime = std::chrono::duration<double, std::milli>( std::chrono::steady_clock::now() - cpustart ).count();
}
bool opengl46_renderer::Scene_Depth_Draws( gfx::geometry_handle const &Geometry ) const
{
if( m_scenedepthskip ) { return false; }
// the program of the prepass doesn't size points, and lines aren't surfaces
auto const type { m_geometry.type( Geometry ) };
return ( ( type == GL_TRIANGLES ) || ( type == GL_TRIANGLE_STRIP ) || ( type == GL_TRIANGLE_FAN ) );
}
// Renders the per-track sleeper instances (TTrack::m_sleeper_local_transforms) using the
// existing GPU-instanced submodel pipeline. The track owns a vector of pre-baked
// local-space matrices; we compose each with `view * translate(track_origin - camera)`

View File

@@ -349,10 +349,21 @@ class opengl46_renderer : public gfx_renderer {
// draws the instances queued in the pass from the result of the gpu culling (Global.gfx_gpucull 1): makes the depth
// pyramid of the occlusion test, runs the culling, walks the submodel trees of the groups once per lod band to record
// the draws (m_indirectdraws, m_indirectcommands), has their instances filled in, then draws them with indirect draws,
// their depth first (Global.gfx_depthprepass)
// their depth first (Global.gfx_depthprepass). inside the depth pass of the whole scene only their depth is drawn, their
// colour waits for the colour pass (Render_Instances_Indirect_Colour())
void Render_Instances_Indirect();
// the recorded draws of the instances: their depth with the programs of the depth prepass, with the state of the prepass
// already set (returns the number of draws), and their colour. the command buffer and the instances are bound by the caller
std::uint32_t Render_Instances_Indirect_Depth();
void Render_Instances_Indirect_Colour();
// how the depth prepass treats the material: 0 left out, 1 drawn without alpha test, 2 with the alpha test of the diffuse texture
int Prepass_Class( material_handle const Material );
// depth of the whole opaque scene of the colour pass, before its colour (Global.gfx_depthprepass_scene): the colour pass
// walks the scene a second time, Bind_Material() binds the programs of the depth prepass and draw() leaves out what they
// can't draw. Future: the motion matrix the colour pass draws the region with
void Render_Scene_Depth( glm::mat4 const &Future );
// whether the depth pass of the whole scene draws the material bound last (Bind_Material()) and the geometry
bool Scene_Depth_Draws( gfx::geometry_handle const &Geometry ) const;
bool Render(TDynamicObject *Dynamic);
bool Render(TModel3d *Model, material_data const *Material, float const Squaredistance, glm::dvec3 const &Position, glm::vec3 const &Angle);
bool Render(TModel3d *Model, material_data const *Material, float const Squaredistance);
@@ -534,6 +545,14 @@ class opengl46_renderer : public gfx_renderer {
std::unique_ptr<gl::program> m_prepass_alpha_shader;
std::unordered_map<gl::program const *, int> m_prepassclasses;
gl::gpu_timer m_prepasstimer;
// depth pass of the whole scene (Render_Scene_Depth())
bool m_scenedepthpass { false }; // inside it: Bind_Material() and draw() work for the depth
bool m_scenedepthskip { true }; // the material bound last isn't drawn in it
bool m_scenedepthdrawn { false }; // it was drawn for the colour pass under way, which skips what was done in it already
bool m_indirectcolourpending { false }; // the instances were culled in it, the colour pass draws them from the recorded draws
debug_stats m_scenedepthstats; // of its last run
double m_scenedepthcputime { 0.0 }; // ms
gl::gpu_timer m_scenedepthtimer;
renderpass_config m_colorpass; // parametrs of most recent color pass
std::array<renderpass_config, 3> m_shadowpass; // parametrs of most recent shadowmap pass for each of csm stages
std::vector<TSubModel const *> m_pickcontrolsitems;

View File

@@ -1444,6 +1444,11 @@ global_settings::ConfigParse_gfx( cParser &Parser, std::string_view const Token
Parser.getTokens(1);
Parser >> gfx_depthprepass;
}
else if (Token == "gfx.depthprepass.scene")
{
Parser.getTokens(1);
Parser >> gfx_depthprepass_scene;
}
else if (Token == "gfx.bufferstreaming")
{
Parser.getTokens(1);
@@ -1918,6 +1923,7 @@ global_settings::export_as_text( std::ostream &Output ) const {
export_as_text( Output, "gfx.gpucull", std::string{ gfx_gpucull == 1 ? "yes" : gfx_gpucull == 2 ? "compare" : "no" } );
export_as_text( Output, "gfx.gpucull.occlusion", gfx_gpucull_occlusion );
export_as_text( Output, "gfx.depthprepass", gfx_depthprepass );
export_as_text( Output, "gfx.depthprepass.scene", gfx_depthprepass_scene );
export_as_text( Output, "gfx.bufferstreaming", gfx_bufferstreaming );
export_as_text( Output, "gfx.postfx.motionblur.enabled", gfx_postfx_motionblur_enabled );
export_as_text( Output, "gfx.postfx.motionblur.shutter", gfx_postfx_motionblur_shutter );

View File

@@ -290,6 +290,8 @@ struct global_settings {
int gfx_gpucull = 0;
bool gfx_gpucull_occlusion = true; // with gfx_gpucull 1: instances hidden behind the depth drawn before them are left out
bool gfx_depthprepass = true; // with gfx_gpucull 1: the depth of the instances is drawn before their colour
// (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 model block of every draw and the matrices of instances are written to persistently mapped buffers,
// one for each of the last frames, instead of with glBufferSubData. read at the start
bool gfx_bufferstreaming = true;