LCOV - code coverage report
Current view: top level - source/renderer - Renderer.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 82 391 21.0 %
Date: 2023-01-19 00:18:29 Functions: 18 40 45.0 %

          Line data    Source code
       1             : /* Copyright (C) 2023 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "Renderer.h"
      21             : 
      22             : #include "graphics/Canvas2D.h"
      23             : #include "graphics/CinemaManager.h"
      24             : #include "graphics/GameView.h"
      25             : #include "graphics/LightEnv.h"
      26             : #include "graphics/ModelDef.h"
      27             : #include "graphics/TerrainTextureManager.h"
      28             : #include "i18n/L10n.h"
      29             : #include "lib/allocators/shared_ptr.h"
      30             : #include "lib/hash.h"
      31             : #include "lib/tex/tex.h"
      32             : #include "gui/GUIManager.h"
      33             : #include "ps/CConsole.h"
      34             : #include "ps/CLogger.h"
      35             : #include "ps/ConfigDB.h"
      36             : #include "ps/CStrInternStatic.h"
      37             : #include "ps/Game.h"
      38             : #include "ps/GameSetup/Config.h"
      39             : #include "ps/GameSetup/GameSetup.h"
      40             : #include "ps/Globals.h"
      41             : #include "ps/Loader.h"
      42             : #include "ps/Profile.h"
      43             : #include "ps/Filesystem.h"
      44             : #include "ps/World.h"
      45             : #include "ps/ProfileViewer.h"
      46             : #include "graphics/Camera.h"
      47             : #include "graphics/FontManager.h"
      48             : #include "graphics/ShaderManager.h"
      49             : #include "graphics/Terrain.h"
      50             : #include "graphics/Texture.h"
      51             : #include "graphics/TextureManager.h"
      52             : #include "ps/Util.h"
      53             : #include "ps/VideoMode.h"
      54             : #include "renderer/backend/IDevice.h"
      55             : #include "renderer/DebugRenderer.h"
      56             : #include "renderer/PostprocManager.h"
      57             : #include "renderer/RenderingOptions.h"
      58             : #include "renderer/RenderModifiers.h"
      59             : #include "renderer/SceneRenderer.h"
      60             : #include "renderer/TimeManager.h"
      61             : #include "renderer/VertexBufferManager.h"
      62             : #include "tools/atlas/GameInterface/GameLoop.h"
      63             : #include "tools/atlas/GameInterface/View.h"
      64             : 
      65             : #include <algorithm>
      66             : 
      67             : namespace
      68             : {
      69             : 
      70             : size_t g_NextScreenShotNumber = 0;
      71             : 
      72             : ///////////////////////////////////////////////////////////////////////////////////
      73             : // CRendererStatsTable - Profile display of rendering stats
      74             : 
      75             : /**
      76             :  * Class CRendererStatsTable: Implementation of AbstractProfileTable to
      77             :  * display the renderer stats in-game.
      78             :  *
      79             :  * Accesses CRenderer::m_Stats by keeping the reference passed to the
      80             :  * constructor.
      81             :  */
      82           6 : class CRendererStatsTable : public AbstractProfileTable
      83             : {
      84             :     NONCOPYABLE(CRendererStatsTable);
      85             : public:
      86             :     CRendererStatsTable(const CRenderer::Stats& st);
      87             : 
      88             :     // Implementation of AbstractProfileTable interface
      89             :     CStr GetName() override;
      90             :     CStr GetTitle() override;
      91             :     size_t GetNumberRows() override;
      92             :     const std::vector<ProfileColumn>& GetColumns() override;
      93             :     CStr GetCellText(size_t row, size_t col) override;
      94             :     AbstractProfileTable* GetChild(size_t row) override;
      95             : 
      96             : private:
      97             :     /// Reference to the renderer singleton's stats
      98             :     const CRenderer::Stats& Stats;
      99             : 
     100             :     /// Column descriptions
     101             :     std::vector<ProfileColumn> columnDescriptions;
     102             : 
     103             :     enum
     104             :     {
     105             :         Row_DrawCalls = 0,
     106             :         Row_TerrainTris,
     107             :         Row_WaterTris,
     108             :         Row_ModelTris,
     109             :         Row_OverlayTris,
     110             :         Row_BlendSplats,
     111             :         Row_Particles,
     112             :         Row_VBReserved,
     113             :         Row_VBAllocated,
     114             :         Row_TextureMemory,
     115             :         Row_ShadersLoaded,
     116             : 
     117             :         // Must be last to count number of rows
     118             :         NumberRows
     119             :     };
     120             : };
     121             : 
     122             : // Construction
     123           6 : CRendererStatsTable::CRendererStatsTable(const CRenderer::Stats& st)
     124           6 :     : Stats(st)
     125             : {
     126           6 :     columnDescriptions.push_back(ProfileColumn("Name", 230));
     127           6 :     columnDescriptions.push_back(ProfileColumn("Value", 100));
     128           6 : }
     129             : 
     130             : // Implementation of AbstractProfileTable interface
     131           0 : CStr CRendererStatsTable::GetName()
     132             : {
     133           0 :     return "renderer";
     134             : }
     135             : 
     136           0 : CStr CRendererStatsTable::GetTitle()
     137             : {
     138           0 :     return "Renderer statistics";
     139             : }
     140             : 
     141           0 : size_t CRendererStatsTable::GetNumberRows()
     142             : {
     143           0 :     return NumberRows;
     144             : }
     145             : 
     146           0 : const std::vector<ProfileColumn>& CRendererStatsTable::GetColumns()
     147             : {
     148           0 :     return columnDescriptions;
     149             : }
     150             : 
     151           0 : CStr CRendererStatsTable::GetCellText(size_t row, size_t col)
     152             : {
     153             :     char buf[256];
     154             : 
     155           0 :     switch(row)
     156             :     {
     157           0 :     case Row_DrawCalls:
     158           0 :         if (col == 0)
     159           0 :             return "# draw calls";
     160           0 :         sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_DrawCalls);
     161           0 :         return buf;
     162             : 
     163           0 :     case Row_TerrainTris:
     164           0 :         if (col == 0)
     165           0 :             return "# terrain tris";
     166           0 :         sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_TerrainTris);
     167           0 :         return buf;
     168             : 
     169           0 :     case Row_WaterTris:
     170           0 :         if (col == 0)
     171           0 :             return "# water tris";
     172           0 :         sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_WaterTris);
     173           0 :         return buf;
     174             : 
     175           0 :     case Row_ModelTris:
     176           0 :         if (col == 0)
     177           0 :             return "# model tris";
     178           0 :         sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_ModelTris);
     179           0 :         return buf;
     180             : 
     181           0 :     case Row_OverlayTris:
     182           0 :         if (col == 0)
     183           0 :             return "# overlay tris";
     184           0 :         sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_OverlayTris);
     185           0 :         return buf;
     186             : 
     187           0 :     case Row_BlendSplats:
     188           0 :         if (col == 0)
     189           0 :             return "# blend splats";
     190           0 :         sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_BlendSplats);
     191           0 :         return buf;
     192             : 
     193           0 :     case Row_Particles:
     194           0 :         if (col == 0)
     195           0 :             return "# particles";
     196           0 :         sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_Particles);
     197           0 :         return buf;
     198             : 
     199           0 :     case Row_VBReserved:
     200           0 :         if (col == 0)
     201           0 :             return "VB reserved";
     202           0 :         sprintf_s(buf, sizeof(buf), "%lu kB", (unsigned long)g_VBMan.GetBytesReserved() / 1024);
     203           0 :         return buf;
     204             : 
     205           0 :     case Row_VBAllocated:
     206           0 :         if (col == 0)
     207           0 :             return "VB allocated";
     208           0 :         sprintf_s(buf, sizeof(buf), "%lu kB", (unsigned long)g_VBMan.GetBytesAllocated() / 1024);
     209           0 :         return buf;
     210             : 
     211           0 :     case Row_TextureMemory:
     212           0 :         if (col == 0)
     213           0 :             return "textures uploaded";
     214           0 :         sprintf_s(buf, sizeof(buf), "%lu kB", (unsigned long)g_Renderer.GetTextureManager().GetBytesUploaded() / 1024);
     215           0 :         return buf;
     216             : 
     217           0 :     case Row_ShadersLoaded:
     218           0 :         if (col == 0)
     219           0 :             return "shader effects loaded";
     220           0 :         sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)g_Renderer.GetShaderManager().GetNumEffectsLoaded());
     221           0 :         return buf;
     222             : 
     223           0 :     default:
     224           0 :         return "???";
     225             :     }
     226             : }
     227             : 
     228           0 : AbstractProfileTable* CRendererStatsTable::GetChild(size_t UNUSED(row))
     229             : {
     230           0 :     return 0;
     231             : }
     232             : 
     233             : } // anonymous namespace
     234             : 
     235             : ///////////////////////////////////////////////////////////////////////////////////
     236             : // CRenderer implementation
     237             : 
     238             : /**
     239             :  * Struct CRendererInternals: Truly hide data that is supposed to be hidden
     240             :  * in this structure so it won't even appear in header files.
     241             :  */
     242           6 : class CRenderer::Internals
     243             : {
     244             :     NONCOPYABLE(Internals);
     245             : public:
     246             :     std::unique_ptr<Renderer::Backend::IDeviceCommandContext> deviceCommandContext;
     247             : 
     248             :     /// true if CRenderer::Open has been called
     249             :     bool IsOpen;
     250             : 
     251             :     /// true if shaders need to be reloaded
     252             :     bool ShadersDirty;
     253             : 
     254             :     /// Table to display renderer stats in-game via profile system
     255             :     CRendererStatsTable profileTable;
     256             : 
     257             :     /// Shader manager
     258             :     CShaderManager shaderManager;
     259             : 
     260             :     /// Texture manager
     261             :     CTextureManager textureManager;
     262             : 
     263             :     /// Time manager
     264             :     CTimeManager timeManager;
     265             : 
     266             :     /// Postprocessing effect manager
     267             :     CPostprocManager postprocManager;
     268             : 
     269             :     CSceneRenderer sceneRenderer;
     270             : 
     271             :     CDebugRenderer debugRenderer;
     272             : 
     273             :     CFontManager fontManager;
     274             : 
     275             :     struct VertexAttributesHash
     276             :     {
     277             :         size_t operator()(const std::vector<Renderer::Backend::SVertexAttributeFormat>& attributes) const;
     278             :     };
     279             : 
     280             :     std::unordered_map<
     281             :         std::vector<Renderer::Backend::SVertexAttributeFormat>,
     282             :         std::unique_ptr<Renderer::Backend::IVertexInputLayout>, VertexAttributesHash> vertexInputLayouts;
     283             : 
     284           6 :     Internals() :
     285           6 :         IsOpen(false), ShadersDirty(true), profileTable(g_Renderer.m_Stats),
     286           6 :         deviceCommandContext(g_VideoMode.GetBackendDevice()->CreateCommandContext()),
     287          12 :         textureManager(g_VFS, false, g_VideoMode.GetBackendDevice())
     288             :     {
     289           6 :     }
     290             : };
     291             : 
     292          96 : size_t CRenderer::Internals::VertexAttributesHash::operator()(
     293             :     const std::vector<Renderer::Backend::SVertexAttributeFormat>& attributes) const
     294             : {
     295          96 :     size_t seed = 0;
     296          96 :     hash_combine(seed, attributes.size());
     297         318 :     for (const Renderer::Backend::SVertexAttributeFormat& attribute : attributes)
     298             :     {
     299         222 :         hash_combine(seed, attribute.stream);
     300         222 :         hash_combine(seed, attribute.format);
     301         222 :         hash_combine(seed, attribute.offset);
     302         222 :         hash_combine(seed, attribute.stride);
     303         222 :         hash_combine(seed, attribute.rate);
     304         222 :         hash_combine(seed, attribute.bindingSlot);
     305             :     }
     306          96 :     return seed;
     307             : }
     308             : 
     309           6 : CRenderer::CRenderer()
     310             : {
     311          12 :     TIMER(L"InitRenderer");
     312             : 
     313           6 :     m = std::make_unique<Internals>();
     314             : 
     315           6 :     g_ProfileViewer.AddRootTable(&m->profileTable);
     316             : 
     317           6 :     m_Width = 0;
     318           6 :     m_Height = 0;
     319             : 
     320           6 :     m_Stats.Reset();
     321             : 
     322             :     // Create terrain related stuff.
     323           6 :     new CTerrainTextureManager;
     324             : 
     325           6 :     Open(g_xres, g_yres);
     326             : 
     327             :     // Setup lighting environment. Since the Renderer accesses the
     328             :     // lighting environment through a pointer, this has to be done before
     329             :     // the first Frame.
     330           6 :     GetSceneRenderer().SetLightEnv(&g_LightEnv);
     331             : 
     332           6 :     ModelDefActivateFastImpl();
     333           6 :     ColorActivateFastImpl();
     334           6 :     ModelRenderer::Init();
     335           6 : }
     336             : 
     337          12 : CRenderer::~CRenderer()
     338             : {
     339           6 :     delete &g_TexMan;
     340             : 
     341             :     // We no longer UnloadWaterTextures here -
     342             :     // that is the responsibility of the module that asked for
     343             :     // them to be loaded (i.e. CGameView).
     344           6 :     m.reset();
     345           6 : }
     346             : 
     347           0 : void CRenderer::ReloadShaders()
     348             : {
     349           0 :     ENSURE(m->IsOpen);
     350             : 
     351           0 :     m->sceneRenderer.ReloadShaders();
     352           0 :     m->ShadersDirty = false;
     353           0 : }
     354             : 
     355           6 : bool CRenderer::Open(int width, int height)
     356             : {
     357           6 :     m->IsOpen = true;
     358             : 
     359             :     // Dimensions
     360           6 :     m_Width = width;
     361           6 :     m_Height = height;
     362             : 
     363             :     // Validate the currently selected render path
     364           6 :     SetRenderPath(g_RenderingOptions.GetRenderPath());
     365             : 
     366           6 :     m->debugRenderer.Initialize();
     367             : 
     368           6 :     if (m->postprocManager.IsEnabled())
     369           0 :         m->postprocManager.Initialize();
     370             : 
     371           6 :     m->sceneRenderer.Initialize();
     372             : 
     373           6 :     return true;
     374             : }
     375             : 
     376           0 : void CRenderer::Resize(int width, int height)
     377             : {
     378           0 :     m_Width = width;
     379           0 :     m_Height = height;
     380             : 
     381           0 :     m->postprocManager.Resize();
     382             : 
     383           0 :     m->sceneRenderer.Resize(width, height);
     384           0 : }
     385             : 
     386           6 : void CRenderer::SetRenderPath(RenderPath rp)
     387             : {
     388           6 :     if (!m->IsOpen)
     389             :     {
     390             :         // Delay until Open() is called.
     391           0 :         return;
     392             :     }
     393             : 
     394             :     // Renderer has been opened, so validate the selected renderpath
     395             :     const bool hasShadersSupport =
     396          12 :         g_VideoMode.GetBackendDevice()->GetCapabilities().ARBShaders ||
     397          12 :         g_VideoMode.GetBackendDevice()->GetBackend() != Renderer::Backend::Backend::GL_ARB;
     398           6 :     if (rp == RenderPath::DEFAULT)
     399             :     {
     400           1 :         if (hasShadersSupport)
     401           1 :             rp = RenderPath::SHADER;
     402             :         else
     403           0 :             rp = RenderPath::FIXED;
     404             :     }
     405             : 
     406           6 :     if (rp == RenderPath::SHADER)
     407             :     {
     408           6 :         if (!hasShadersSupport)
     409             :         {
     410           0 :             LOGWARNING("Falling back to fixed function\n");
     411           0 :             rp = RenderPath::FIXED;
     412             :         }
     413             :     }
     414             : 
     415             :     // TODO: remove this once capabilities have been properly extracted and the above checks have been moved elsewhere.
     416           6 :     g_RenderingOptions.m_RenderPath = rp;
     417             : 
     418           6 :     MakeShadersDirty();
     419             : }
     420             : 
     421           0 : bool CRenderer::ShouldRender() const
     422             : {
     423           0 :     return !g_app_minimized && (g_app_has_focus || !g_VideoMode.IsInFullscreen());
     424             : }
     425             : 
     426           0 : void CRenderer::RenderFrame(const bool needsPresent)
     427             : {
     428             :     // Do not render if not focused while in fullscreen or minimised,
     429             :     // as that triggers a difficult-to-reproduce crash on some graphic cards.
     430           0 :     if (!ShouldRender())
     431           0 :         return;
     432             : 
     433           0 :     if (m_ScreenShotType == ScreenShotType::BIG)
     434             :     {
     435           0 :         RenderBigScreenShot(needsPresent);
     436             :     }
     437           0 :     else if (m_ScreenShotType == ScreenShotType::DEFAULT)
     438             :     {
     439           0 :         RenderScreenShot(needsPresent);
     440             :     }
     441             :     else
     442             :     {
     443           0 :         if (needsPresent)
     444             :         {
     445             :             // In case of no acquired backbuffer we have nothing render to.
     446           0 :             if (!g_VideoMode.GetBackendDevice()->AcquireNextBackbuffer())
     447           0 :                 return;
     448             :         }
     449             : 
     450           0 :         if (m_ShouldPreloadResourcesBeforeNextFrame)
     451             :         {
     452           0 :             m_ShouldPreloadResourcesBeforeNextFrame = false;
     453             :             // We don't need to render logger for the preload.
     454           0 :             RenderFrameImpl(true, false);
     455             :         }
     456             : 
     457           0 :         RenderFrameImpl(true, true);
     458             : 
     459           0 :         m->deviceCommandContext->Flush();
     460           0 :         if (needsPresent)
     461           0 :             g_VideoMode.GetBackendDevice()->Present();
     462             :     }
     463             : }
     464             : 
     465           0 : void CRenderer::RenderFrameImpl(const bool renderGUI, const bool renderLogger)
     466             : {
     467           0 :     PROFILE3("render");
     468             : 
     469           0 :     g_Profiler2.RecordGPUFrameStart();
     470             : 
     471           0 :     g_TexMan.UploadResourcesIfNeeded(m->deviceCommandContext.get());
     472             : 
     473           0 :     m->textureManager.MakeUploadProgress(m->deviceCommandContext.get());
     474             : 
     475             :     // prepare before starting the renderer frame
     476           0 :     if (g_Game && g_Game->IsGameStarted())
     477           0 :         g_Game->GetView()->BeginFrame();
     478             : 
     479           0 :     if (g_Game)
     480           0 :         m->sceneRenderer.SetSimulation(g_Game->GetSimulation2());
     481             : 
     482             :     // start new frame
     483           0 :     BeginFrame();
     484             : 
     485           0 :     if (g_Game && g_Game->IsGameStarted())
     486             :     {
     487           0 :         g_Game->GetView()->Prepare(m->deviceCommandContext.get());
     488             : 
     489           0 :         Renderer::Backend::IFramebuffer* framebuffer = nullptr;
     490             : 
     491           0 :         CPostprocManager& postprocManager = g_Renderer.GetPostprocManager();
     492           0 :         if (postprocManager.IsEnabled())
     493             :         {
     494             :             // We have to update the post process manager with real near/far planes
     495             :             // that we use for the scene rendering.
     496           0 :             postprocManager.SetDepthBufferClipPlanes(
     497           0 :                 m->sceneRenderer.GetViewCamera().GetNearPlane(),
     498           0 :                 m->sceneRenderer.GetViewCamera().GetFarPlane()
     499             :             );
     500           0 :             postprocManager.Initialize();
     501           0 :             framebuffer = postprocManager.PrepareAndGetOutputFramebuffer();
     502             :         }
     503             :         else
     504             :         {
     505             :             // We don't need to clear the color attachment of the framebuffer as the sky
     506             :             // is going to be rendered anyway.
     507           0 :             framebuffer =
     508           0 :                 m->deviceCommandContext->GetDevice()->GetCurrentBackbuffer(
     509             :                     Renderer::Backend::AttachmentLoadOp::DONT_CARE,
     510             :                     Renderer::Backend::AttachmentStoreOp::STORE,
     511             :                     Renderer::Backend::AttachmentLoadOp::CLEAR,
     512           0 :                     Renderer::Backend::AttachmentStoreOp::DONT_CARE);
     513             :         }
     514             : 
     515           0 :         m->deviceCommandContext->BeginFramebufferPass(framebuffer);
     516             : 
     517           0 :         Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
     518           0 :         viewportRect.width = framebuffer->GetWidth();
     519           0 :         viewportRect.height = framebuffer->GetHeight();
     520           0 :         m->deviceCommandContext->SetViewports(1, &viewportRect);
     521             : 
     522           0 :         g_Game->GetView()->Render(m->deviceCommandContext.get());
     523             : 
     524           0 :         if (postprocManager.IsEnabled())
     525             :         {
     526           0 :             m->deviceCommandContext->EndFramebufferPass();
     527             : 
     528           0 :             if (postprocManager.IsMultisampleEnabled())
     529           0 :                 postprocManager.ResolveMultisampleFramebuffer(m->deviceCommandContext.get());
     530             : 
     531           0 :             postprocManager.ApplyPostproc(m->deviceCommandContext.get());
     532             : 
     533             :             Renderer::Backend::IFramebuffer* backbuffer =
     534           0 :                 m->deviceCommandContext->GetDevice()->GetCurrentBackbuffer(
     535             :                     Renderer::Backend::AttachmentLoadOp::LOAD,
     536             :                     Renderer::Backend::AttachmentStoreOp::STORE,
     537             :                     Renderer::Backend::AttachmentLoadOp::LOAD,
     538           0 :                     Renderer::Backend::AttachmentStoreOp::DONT_CARE);
     539           0 :             postprocManager.BlitOutputFramebuffer(
     540           0 :                 m->deviceCommandContext.get(), backbuffer);
     541             : 
     542           0 :             m->deviceCommandContext->BeginFramebufferPass(backbuffer);
     543             : 
     544           0 :             Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
     545           0 :             viewportRect.width = backbuffer->GetWidth();
     546           0 :             viewportRect.height = backbuffer->GetHeight();
     547           0 :             m->deviceCommandContext->SetViewports(1, &viewportRect);
     548             :         }
     549             : 
     550           0 :         g_Game->GetView()->RenderOverlays(m->deviceCommandContext.get());
     551             : 
     552           0 :         g_Game->GetView()->GetCinema()->Render();
     553             :     }
     554             :     else
     555             :     {
     556             :         // We have a fullscreen background in our UI so we don't need
     557             :         // to clear the color attachment.
     558             :         // We don't need a depth test to render so we don't care about the
     559             :         // depth-stencil attachment content.
     560             :         // In case of Atlas we don't have g_Game, so we still need to clear depth.
     561           0 :         const Renderer::Backend::AttachmentLoadOp depthStencilLoadOp =
     562           0 :             g_AtlasGameLoop && g_AtlasGameLoop->view
     563           0 :                 ? Renderer::Backend::AttachmentLoadOp::CLEAR
     564             :                 : Renderer::Backend::AttachmentLoadOp::DONT_CARE;
     565             :         Renderer::Backend::IFramebuffer* backbuffer =
     566           0 :             m->deviceCommandContext->GetDevice()->GetCurrentBackbuffer(
     567             :                 Renderer::Backend::AttachmentLoadOp::DONT_CARE,
     568             :                 Renderer::Backend::AttachmentStoreOp::STORE,
     569             :                 depthStencilLoadOp,
     570           0 :                 Renderer::Backend::AttachmentStoreOp::DONT_CARE);
     571           0 :         m->deviceCommandContext->BeginFramebufferPass(backbuffer);
     572             : 
     573           0 :         Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
     574           0 :         viewportRect.width = backbuffer->GetWidth();
     575           0 :         viewportRect.height = backbuffer->GetHeight();
     576           0 :         m->deviceCommandContext->SetViewports(1, &viewportRect);
     577             :     }
     578             : 
     579             :     // If we're in Atlas game view, render special tools
     580           0 :     if (g_AtlasGameLoop && g_AtlasGameLoop->view)
     581             :     {
     582           0 :         g_AtlasGameLoop->view->DrawCinemaPathTool();
     583             :     }
     584             : 
     585           0 :     RenderFrame2D(renderGUI, renderLogger);
     586             : 
     587           0 :     m->deviceCommandContext->EndFramebufferPass();
     588             : 
     589           0 :     EndFrame();
     590             : 
     591           0 :     const Stats& stats = GetStats();
     592           0 :     PROFILE2_ATTR("draw calls: %zu", stats.m_DrawCalls);
     593           0 :     PROFILE2_ATTR("terrain tris: %zu", stats.m_TerrainTris);
     594           0 :     PROFILE2_ATTR("water tris: %zu", stats.m_WaterTris);
     595           0 :     PROFILE2_ATTR("model tris: %zu", stats.m_ModelTris);
     596           0 :     PROFILE2_ATTR("overlay tris: %zu", stats.m_OverlayTris);
     597           0 :     PROFILE2_ATTR("blend splats: %zu", stats.m_BlendSplats);
     598           0 :     PROFILE2_ATTR("particles: %zu", stats.m_Particles);
     599             : 
     600           0 :     g_Profiler2.RecordGPUFrameEnd();
     601           0 : }
     602             : 
     603           0 : void CRenderer::RenderFrame2D(const bool renderGUI, const bool renderLogger)
     604             : {
     605           0 :     CCanvas2D canvas(g_xres, g_yres, g_VideoMode.GetScale(), m->deviceCommandContext.get());
     606             : 
     607           0 :     m->sceneRenderer.RenderTextOverlays(canvas);
     608             : 
     609           0 :     if (renderGUI)
     610             :     {
     611           0 :         GPU_SCOPED_LABEL(m->deviceCommandContext.get(), "Render GUI");
     612             :         // All GUI elements are drawn in Z order to render semi-transparent
     613             :         // objects correctly.
     614           0 :         g_GUI->Draw(canvas);
     615             :     }
     616             : 
     617             :     // If we're in Atlas game view, render special overlays (e.g. editor bandbox).
     618           0 :     if (g_AtlasGameLoop && g_AtlasGameLoop->view)
     619             :     {
     620           0 :         g_AtlasGameLoop->view->DrawOverlays(canvas);
     621             :     }
     622             : 
     623             :     {
     624           0 :         GPU_SCOPED_LABEL(m->deviceCommandContext.get(), "Render console");
     625           0 :         g_Console->Render(canvas);
     626             :     }
     627             : 
     628           0 :     if (renderLogger)
     629             :     {
     630           0 :         GPU_SCOPED_LABEL(m->deviceCommandContext.get(), "Render logger");
     631           0 :         g_Logger->Render(canvas);
     632             :     }
     633             : 
     634             :     {
     635           0 :         GPU_SCOPED_LABEL(m->deviceCommandContext.get(), "Render profiler");
     636             :         // Profile information
     637           0 :         g_ProfileViewer.RenderProfile(canvas);
     638             :     }
     639           0 : }
     640             : 
     641           0 : void CRenderer::RenderScreenShot(const bool needsPresent)
     642             : {
     643           0 :     m_ScreenShotType = ScreenShotType::NONE;
     644             : 
     645             :     // get next available numbered filename
     646             :     // note: %04d -> always 4 digits, so sorting by filename works correctly.
     647           0 :     const VfsPath filenameFormat(L"screenshots/screenshot%04d.png");
     648           0 :     VfsPath filename;
     649           0 :     vfs::NextNumberedFilename(g_VFS, filenameFormat, g_NextScreenShotNumber, filename);
     650             : 
     651           0 :     const size_t width = static_cast<size_t>(g_xres), height = static_cast<size_t>(g_yres);
     652           0 :     const size_t bpp = 24;
     653             : 
     654           0 :     if (needsPresent && !g_VideoMode.GetBackendDevice()->AcquireNextBackbuffer())
     655           0 :         return;
     656             : 
     657             :     // Hide log messages and re-render
     658           0 :     RenderFrameImpl(true, false);
     659             : 
     660           0 :     const size_t img_size = width * height * bpp / 8;
     661           0 :     const size_t hdr_size = tex_hdr_size(filename);
     662           0 :     std::shared_ptr<u8> buf;
     663           0 :     AllocateAligned(buf, hdr_size + img_size, maxSectorSize);
     664           0 :     void* img = buf.get() + hdr_size;
     665           0 :     Tex t;
     666           0 :     if (t.wrap(width, height, bpp, TEX_BOTTOM_UP, buf, hdr_size) < 0)
     667           0 :         return;
     668             : 
     669           0 :     m->deviceCommandContext->ReadbackFramebufferSync(0, 0, width, height, img);
     670           0 :     m->deviceCommandContext->Flush();
     671           0 :     if (needsPresent)
     672           0 :         g_VideoMode.GetBackendDevice()->Present();
     673             : 
     674           0 :     if (tex_write(&t, filename) == INFO::OK)
     675             :     {
     676           0 :         OsPath realPath;
     677           0 :         g_VFS->GetRealPath(filename, realPath);
     678             : 
     679           0 :         LOGMESSAGERENDER(g_L10n.Translate("Screenshot written to '%s'"), realPath.string8());
     680             : 
     681           0 :         debug_printf(
     682           0 :             CStr(g_L10n.Translate("Screenshot written to '%s'") + "\n").c_str(),
     683           0 :             realPath.string8().c_str());
     684             :     }
     685             :     else
     686           0 :         LOGERROR("Error writing screenshot to '%s'", filename.string8());
     687             : }
     688             : 
     689           0 : void CRenderer::RenderBigScreenShot(const bool needsPresent)
     690             : {
     691           0 :     m_ScreenShotType = ScreenShotType::NONE;
     692             : 
     693             :     // If the game hasn't started yet then use WriteScreenshot to generate the image.
     694           0 :     if (!g_Game)
     695           0 :         return RenderScreenShot(needsPresent);
     696             : 
     697           0 :     int tiles = 4, tileWidth = 256, tileHeight = 256;
     698           0 :     CFG_GET_VAL("screenshot.tiles", tiles);
     699           0 :     CFG_GET_VAL("screenshot.tilewidth", tileWidth);
     700           0 :     CFG_GET_VAL("screenshot.tileheight", tileHeight);
     701           0 :     if (tiles <= 0 || tileWidth <= 0 || tileHeight <= 0 || tileWidth * tiles % 4 != 0 || tileHeight * tiles % 4 != 0)
     702             :     {
     703           0 :         LOGWARNING("Invalid big screenshot size: tiles=%d tileWidth=%d tileHeight=%d", tiles, tileWidth, tileHeight);
     704           0 :         return;
     705             :     }
     706             : 
     707             :     // get next available numbered filename
     708             :     // note: %04d -> always 4 digits, so sorting by filename works correctly.
     709           0 :     const VfsPath filenameFormat(L"screenshots/screenshot%04d.bmp");
     710           0 :     VfsPath filename;
     711           0 :     vfs::NextNumberedFilename(g_VFS, filenameFormat, g_NextScreenShotNumber, filename);
     712             : 
     713             :     // Slightly ugly and inflexible: Always draw 640*480 tiles onto the screen, and
     714             :     // hope the screen is actually large enough for that.
     715           0 :     ENSURE(g_xres >= tileWidth && g_yres >= tileHeight);
     716             : 
     717           0 :     const int imageWidth = tileWidth * tiles, imageHeight = tileHeight * tiles;
     718           0 :     const int bpp = 24;
     719             : 
     720           0 :     const size_t imageSize = imageWidth * imageHeight * bpp / 8;
     721           0 :     const size_t tileSize = tileWidth * tileHeight * bpp / 8;
     722           0 :     const size_t headerSize = tex_hdr_size(filename);
     723           0 :     void* tileData = malloc(tileSize);
     724           0 :     if (!tileData)
     725             :     {
     726           0 :         WARN_IF_ERR(ERR::NO_MEM);
     727           0 :         return;
     728             :     }
     729           0 :     std::shared_ptr<u8> imageBuffer;
     730           0 :     AllocateAligned(imageBuffer, headerSize + imageSize, maxSectorSize);
     731             : 
     732           0 :     Tex t;
     733           0 :     void* img = imageBuffer.get() + headerSize;
     734           0 :     if (t.wrap(imageWidth, imageHeight, bpp, TEX_BOTTOM_UP, imageBuffer, headerSize) < 0)
     735             :     {
     736           0 :         free(tileData);
     737           0 :         return;
     738             :     }
     739             : 
     740           0 :     CCamera oldCamera = *g_Game->GetView()->GetCamera();
     741             : 
     742             :     // Resize various things so that the sizes and aspect ratios are correct
     743             :     {
     744           0 :         g_Renderer.Resize(tileWidth, tileHeight);
     745           0 :         SViewPort vp = { 0, 0, tileWidth, tileHeight };
     746           0 :         g_Game->GetView()->SetViewport(vp);
     747             :     }
     748             : 
     749             :     // Render each tile
     750           0 :     CMatrix3D projection;
     751           0 :     projection.SetIdentity();
     752           0 :     const float aspectRatio = 1.0f * tileWidth / tileHeight;
     753           0 :     for (int tileY = 0; tileY < tiles; ++tileY)
     754             :     {
     755           0 :         for (int tileX = 0; tileX < tiles; ++tileX)
     756             :         {
     757             :             // Adjust the camera to render the appropriate region
     758           0 :             if (oldCamera.GetProjectionType() == CCamera::ProjectionType::PERSPECTIVE)
     759             :             {
     760           0 :                 projection.SetPerspectiveTile(oldCamera.GetFOV(), aspectRatio, oldCamera.GetNearPlane(), oldCamera.GetFarPlane(), tiles, tileX, tileY);
     761             :             }
     762           0 :             g_Game->GetView()->GetCamera()->SetProjection(projection);
     763             : 
     764           0 :             if (!needsPresent || g_VideoMode.GetBackendDevice()->AcquireNextBackbuffer())
     765             :             {
     766           0 :                 RenderFrameImpl(false, false);
     767             : 
     768           0 :                 m->deviceCommandContext->ReadbackFramebufferSync(0, 0, tileWidth, tileHeight, tileData);
     769           0 :                 m->deviceCommandContext->Flush();
     770             : 
     771           0 :                 if (needsPresent)
     772           0 :                     g_VideoMode.GetBackendDevice()->Present();
     773             :             }
     774             : 
     775             :             // Copy the tile pixels into the main image
     776           0 :             for (int y = 0; y < tileHeight; ++y)
     777             :             {
     778           0 :                 void* dest = static_cast<char*>(img) + ((tileY * tileHeight + y) * imageWidth + (tileX * tileWidth)) * bpp / 8;
     779           0 :                 void* src = static_cast<char*>(tileData) + y * tileWidth * bpp / 8;
     780           0 :                 memcpy(dest, src, tileWidth * bpp / 8);
     781             :             }
     782             :         }
     783             :     }
     784             : 
     785             :     // Restore the viewport settings
     786             :     {
     787           0 :         g_Renderer.Resize(g_xres, g_yres);
     788           0 :         SViewPort vp = { 0, 0, g_xres, g_yres };
     789           0 :         g_Game->GetView()->SetViewport(vp);
     790           0 :         g_Game->GetView()->GetCamera()->SetProjectionFromCamera(oldCamera);
     791             :     }
     792             : 
     793           0 :     if (tex_write(&t, filename) == INFO::OK)
     794             :     {
     795           0 :         OsPath realPath;
     796           0 :         g_VFS->GetRealPath(filename, realPath);
     797             : 
     798           0 :         LOGMESSAGERENDER(g_L10n.Translate("Screenshot written to '%s'"), realPath.string8());
     799             : 
     800           0 :         debug_printf(
     801           0 :             CStr(g_L10n.Translate("Screenshot written to '%s'") + "\n").c_str(),
     802           0 :             realPath.string8().c_str());
     803             :     }
     804             :     else
     805           0 :         LOGERROR("Error writing screenshot to '%s'", filename.string8());
     806             : 
     807           0 :     free(tileData);
     808             : }
     809             : 
     810           0 : void CRenderer::BeginFrame()
     811             : {
     812           0 :     PROFILE("begin frame");
     813             : 
     814             :     // Zero out all the per-frame stats.
     815           0 :     m_Stats.Reset();
     816             : 
     817           0 :     if (m->ShadersDirty)
     818           0 :         ReloadShaders();
     819             : 
     820           0 :     m->sceneRenderer.BeginFrame();
     821           0 : }
     822             : 
     823           0 : void CRenderer::EndFrame()
     824             : {
     825           0 :     PROFILE3("end frame");
     826             : 
     827           0 :     m->sceneRenderer.EndFrame();
     828           0 : }
     829             : 
     830           6 : void CRenderer::MakeShadersDirty()
     831             : {
     832           6 :     m->ShadersDirty = true;
     833           6 :     m->sceneRenderer.MakeShadersDirty();
     834           6 : }
     835             : 
     836           5 : CTextureManager& CRenderer::GetTextureManager()
     837             : {
     838           5 :     return m->textureManager;
     839             : }
     840             : 
     841          60 : CShaderManager& CRenderer::GetShaderManager()
     842             : {
     843          60 :     return m->shaderManager;
     844             : }
     845             : 
     846           0 : CTimeManager& CRenderer::GetTimeManager()
     847             : {
     848           0 :     return m->timeManager;
     849             : }
     850             : 
     851           0 : CPostprocManager& CRenderer::GetPostprocManager()
     852             : {
     853           0 :     return m->postprocManager;
     854             : }
     855             : 
     856           6 : CSceneRenderer& CRenderer::GetSceneRenderer()
     857             : {
     858           6 :     return m->sceneRenderer;
     859             : }
     860             : 
     861           0 : CDebugRenderer& CRenderer::GetDebugRenderer()
     862             : {
     863           0 :     return m->debugRenderer;
     864             : }
     865             : 
     866        3083 : CFontManager& CRenderer::GetFontManager()
     867             : {
     868        3083 :     return m->fontManager;
     869             : }
     870             : 
     871           0 : void CRenderer::PreloadResourcesBeforeNextFrame()
     872             : {
     873           0 :     m_ShouldPreloadResourcesBeforeNextFrame = true;
     874           0 : }
     875             : 
     876           0 : void CRenderer::MakeScreenShotOnNextFrame(ScreenShotType screenShotType)
     877             : {
     878           0 :     m_ScreenShotType = screenShotType;
     879           0 : }
     880             : 
     881           6 : Renderer::Backend::IDeviceCommandContext* CRenderer::GetDeviceCommandContext()
     882             : {
     883           6 :     return m->deviceCommandContext.get();
     884             : }
     885             : 
     886          96 : Renderer::Backend::IVertexInputLayout* CRenderer::GetVertexInputLayout(
     887             :     const PS::span<const Renderer::Backend::SVertexAttributeFormat> attributes)
     888             : {
     889          96 :     const auto [it, inserted] = m->vertexInputLayouts.emplace(
     890          96 :         std::vector<Renderer::Backend::SVertexAttributeFormat>{attributes.begin(), attributes.end()}, nullptr);
     891          96 :     if (inserted)
     892          78 :         it->second = g_VideoMode.GetBackendDevice()->CreateVertexInputLayout(attributes);
     893          96 :     return it->second.get();
     894           3 : }

Generated by: LCOV version 1.13