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:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)`
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user