diff --git a/docs/opengl46-renderer.md b/docs/opengl46-renderer.md index 9d6619d8..5b5af057 100644 --- a/docs/opengl46-renderer.md +++ b/docs/opengl46-renderer.md @@ -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 diff --git a/rendering/geometrybank.cpp b/rendering/geometrybank.cpp index ae59716d..4b35bf09 100644 --- a/rendering/geometrybank.cpp +++ b/rendering/geometrybank.cpp @@ -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 { diff --git a/rendering/geometrybank.h b/rendering/geometrybank.h index 29c62449..9ee21019 100644 --- a/rendering/geometrybank.h +++ b/rendering/geometrybank.h @@ -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 diff --git a/rendering/opengl46/opengl46instancecull.h b/rendering/opengl46/opengl46instancecull.h index ecc79a5d..71e0e718 100644 --- a/rendering/opengl46/opengl46instancecull.h +++ b/rendering/opengl46/opengl46instancecull.h @@ -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 { diff --git a/rendering/opengl46/opengl46renderer.cpp b/rendering/opengl46/opengl46renderer.cpp index a0275944..b0953a55 100644 --- a/rendering/opengl46/opengl46renderer.cpp +++ b/rendering/opengl46/opengl46renderer.cpp @@ -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( *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( 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( m_indirectdraws.size() ), + m_instancecull.prepass_draws(), + m_instancecull.cpu_time() + std::chrono::duration( std::chrono::steady_clock::now() - cpustart ).count() ); + } + m_renderpass.draw_stats.instanced_drawcalls += static_cast( m_indirectdraws.size() ); + m_renderpass.draw_stats.instances += static_cast( m_instancecull.last_count() ); + m_renderpass.draw_stats.models += static_cast( 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::iterator it, std::vector::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 *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 const solid { "colored", "default_0" }; + static std::unordered_set 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( 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( m_indirectdraws.size() ); - m_renderpass.draw_stats.drawcalls += static_cast( prepassdraws ); - // the count of the frame the gpu reported last - m_renderpass.draw_stats.instances += static_cast( m_instancecull.last_count() ); - m_renderpass.draw_stats.models += static_cast( 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( prepassdraws ); + } + else { + m_renderpass.draw_stats.instanced_drawcalls += static_cast( m_indirectdraws.size() ); + m_renderpass.draw_stats.drawcalls += static_cast( prepassdraws ); + // the count of the frame the gpu reported last + m_renderpass.draw_stats.instances += static_cast( m_instancecull.last_count() ); + m_renderpass.draw_stats.models += static_cast( m_instancecull.last_count() ); + } m_instancecull.draw_result( static_cast( m_indirectdraws.size() ), prepassdraws, std::chrono::duration( 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( 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( 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( 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)` diff --git a/rendering/opengl46/opengl46renderer.h b/rendering/opengl46/opengl46renderer.h index 98d4a1eb..6cb0c0f1 100644 --- a/rendering/opengl46/opengl46renderer.h +++ b/rendering/opengl46/opengl46renderer.h @@ -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 m_prepass_alpha_shader; std::unordered_map 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 m_shadowpass; // parametrs of most recent shadowmap pass for each of csm stages std::vector m_pickcontrolsitems; diff --git a/utilities/Globals.cpp b/utilities/Globals.cpp index 73ff7a24..c8ca9d26 100644 --- a/utilities/Globals.cpp +++ b/utilities/Globals.cpp @@ -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 ); diff --git a/utilities/Globals.h b/utilities/Globals.h index 24b73715..35b815f1 100644 --- a/utilities/Globals.h +++ b/utilities/Globals.h @@ -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;