Index: ps/trunk/source/renderer/Renderer.cpp =================================================================== --- ps/trunk/source/renderer/Renderer.cpp (revision 26185) +++ ps/trunk/source/renderer/Renderer.cpp (revision 26186) @@ -1,862 +1,846 @@ /* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "Renderer.h" #include "graphics/Canvas2D.h" #include "graphics/CinemaManager.h" #include "graphics/GameView.h" #include "graphics/LightEnv.h" #include "graphics/ModelDef.h" #include "graphics/TerrainTextureManager.h" #include "i18n/L10n.h" #include "lib/allocators/shared_ptr.h" #include "lib/ogl.h" #include "lib/tex/tex.h" #include "gui/GUIManager.h" #include "ps/CConsole.h" #include "ps/CLogger.h" #include "ps/ConfigDB.h" #include "ps/CStrInternStatic.h" #include "ps/Game.h" #include "ps/GameSetup/Config.h" #include "ps/GameSetup/GameSetup.h" #include "ps/Globals.h" #include "ps/Loader.h" #include "ps/Profile.h" #include "ps/Filesystem.h" #include "ps/World.h" #include "ps/ProfileViewer.h" #include "graphics/Camera.h" #include "graphics/FontManager.h" #include "graphics/ShaderManager.h" #include "graphics/Terrain.h" #include "graphics/Texture.h" #include "graphics/TextureManager.h" #include "ps/Util.h" #include "ps/VideoMode.h" #include "renderer/backend/gl/Device.h" #include "renderer/DebugRenderer.h" #include "renderer/PostprocManager.h" #include "renderer/RenderingOptions.h" #include "renderer/RenderModifiers.h" #include "renderer/SceneRenderer.h" #include "renderer/TimeManager.h" #include "renderer/VertexBufferManager.h" #include "tools/atlas/GameInterface/GameLoop.h" #include "tools/atlas/GameInterface/View.h" #include namespace { size_t g_NextScreenShotNumber = 0; /////////////////////////////////////////////////////////////////////////////////// // CRendererStatsTable - Profile display of rendering stats /** * Class CRendererStatsTable: Implementation of AbstractProfileTable to * display the renderer stats in-game. * * Accesses CRenderer::m_Stats by keeping the reference passed to the * constructor. */ class CRendererStatsTable : public AbstractProfileTable { NONCOPYABLE(CRendererStatsTable); public: CRendererStatsTable(const CRenderer::Stats& st); // Implementation of AbstractProfileTable interface CStr GetName(); CStr GetTitle(); size_t GetNumberRows(); const std::vector& GetColumns(); CStr GetCellText(size_t row, size_t col); AbstractProfileTable* GetChild(size_t row); private: /// Reference to the renderer singleton's stats const CRenderer::Stats& Stats; /// Column descriptions std::vector columnDescriptions; enum { Row_DrawCalls = 0, Row_TerrainTris, Row_WaterTris, Row_ModelTris, Row_OverlayTris, Row_BlendSplats, Row_Particles, Row_VBReserved, Row_VBAllocated, Row_TextureMemory, Row_ShadersLoaded, // Must be last to count number of rows NumberRows }; }; // Construction CRendererStatsTable::CRendererStatsTable(const CRenderer::Stats& st) : Stats(st) { columnDescriptions.push_back(ProfileColumn("Name", 230)); columnDescriptions.push_back(ProfileColumn("Value", 100)); } // Implementation of AbstractProfileTable interface CStr CRendererStatsTable::GetName() { return "renderer"; } CStr CRendererStatsTable::GetTitle() { return "Renderer statistics"; } size_t CRendererStatsTable::GetNumberRows() { return NumberRows; } const std::vector& CRendererStatsTable::GetColumns() { return columnDescriptions; } CStr CRendererStatsTable::GetCellText(size_t row, size_t col) { char buf[256]; switch(row) { case Row_DrawCalls: if (col == 0) return "# draw calls"; sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_DrawCalls); return buf; case Row_TerrainTris: if (col == 0) return "# terrain tris"; sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_TerrainTris); return buf; case Row_WaterTris: if (col == 0) return "# water tris"; sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_WaterTris); return buf; case Row_ModelTris: if (col == 0) return "# model tris"; sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_ModelTris); return buf; case Row_OverlayTris: if (col == 0) return "# overlay tris"; sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_OverlayTris); return buf; case Row_BlendSplats: if (col == 0) return "# blend splats"; sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_BlendSplats); return buf; case Row_Particles: if (col == 0) return "# particles"; sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_Particles); return buf; case Row_VBReserved: if (col == 0) return "VB reserved"; sprintf_s(buf, sizeof(buf), "%lu kB", (unsigned long)g_VBMan.GetBytesReserved() / 1024); return buf; case Row_VBAllocated: if (col == 0) return "VB allocated"; sprintf_s(buf, sizeof(buf), "%lu kB", (unsigned long)g_VBMan.GetBytesAllocated() / 1024); return buf; case Row_TextureMemory: if (col == 0) return "textures uploaded"; sprintf_s(buf, sizeof(buf), "%lu kB", (unsigned long)g_Renderer.GetTextureManager().GetBytesUploaded() / 1024); return buf; case Row_ShadersLoaded: if (col == 0) return "shader effects loaded"; sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)g_Renderer.GetShaderManager().GetNumEffectsLoaded()); return buf; default: return "???"; } } AbstractProfileTable* CRendererStatsTable::GetChild(size_t UNUSED(row)) { return 0; } } // anonymous namespace /////////////////////////////////////////////////////////////////////////////////// // CRenderer implementation /** * Struct CRendererInternals: Truly hide data that is supposed to be hidden * in this structure so it won't even appear in header files. */ class CRenderer::Internals { NONCOPYABLE(Internals); public: /// true if CRenderer::Open has been called bool IsOpen; /// true if shaders need to be reloaded bool ShadersDirty; /// Table to display renderer stats in-game via profile system CRendererStatsTable profileTable; /// Shader manager CShaderManager shaderManager; /// Texture manager CTextureManager textureManager; /// Time manager CTimeManager timeManager; /// Postprocessing effect manager CPostprocManager postprocManager; CSceneRenderer sceneRenderer; CDebugRenderer debugRenderer; CFontManager fontManager; std::unique_ptr deviceCommandContext; Internals() : IsOpen(false), ShadersDirty(true), profileTable(g_Renderer.m_Stats), textureManager(g_VFS, false, false) { } }; CRenderer::CRenderer() { TIMER(L"InitRenderer"); m = std::make_unique(); g_ProfileViewer.AddRootTable(&m->profileTable); m_Width = 0; m_Height = 0; m_Stats.Reset(); // Create terrain related stuff. new CTerrainTextureManager; Open(g_xres, g_yres); // Setup lighting environment. Since the Renderer accesses the // lighting environment through a pointer, this has to be done before // the first Frame. GetSceneRenderer().SetLightEnv(&g_LightEnv); // I haven't seen the camera affecting GUI rendering and such, but the // viewport has to be updated according to the video mode SViewPort vp; vp.m_X = 0; vp.m_Y = 0; vp.m_Width = g_xres; vp.m_Height = g_yres; SetViewport(vp); ModelDefActivateFastImpl(); ColorActivateFastImpl(); ModelRenderer::Init(); } CRenderer::~CRenderer() { // We no longer UnloadWaterTextures here - // that is the responsibility of the module that asked for // them to be loaded (i.e. CGameView). m.reset(); } void CRenderer::EnumCaps() { // assume support for nothing m_Caps.m_ARBProgram = false; m_Caps.m_ARBProgramShadow = false; m_Caps.m_VertexShader = false; m_Caps.m_FragmentShader = false; m_Caps.m_Shadows = false; m_Caps.m_PrettyWater = false; // now start querying extensions if (0 == ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", NULL)) { m_Caps.m_ARBProgram = true; if (ogl_HaveExtension("GL_ARB_fragment_program_shadow")) m_Caps.m_ARBProgramShadow = true; } // GLSL shaders are in core since GL2.0. if (ogl_HaveVersion(2, 0)) m_Caps.m_VertexShader = m_Caps.m_FragmentShader = true; #if CONFIG2_GLES m_Caps.m_Shadows = true; #else if (0 == ogl_HaveExtensions(0, "GL_ARB_shadow", "GL_ARB_depth_texture", NULL)) { if (ogl_max_tex_units >= 4) m_Caps.m_Shadows = true; } #endif #if CONFIG2_GLES m_Caps.m_PrettyWater = true; #else if (m_Caps.m_VertexShader && m_Caps.m_FragmentShader) m_Caps.m_PrettyWater = true; #endif } void CRenderer::ReloadShaders() { ENSURE(m->IsOpen); m->sceneRenderer.ReloadShaders(); m->ShadersDirty = false; } bool CRenderer::Open(int width, int height) { m->IsOpen = true; // Must query card capabilities before creating renderers that depend // on card capabilities. EnumCaps(); // Dimensions m_Width = width; m_Height = height; // set packing parameters glPixelStorei(GL_PACK_ALIGNMENT,1); glPixelStorei(GL_UNPACK_ALIGNMENT,1); // setup default state glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glCullFace(GL_BACK); glFrontFace(GL_CCW); glEnable(GL_CULL_FACE); // Validate the currently selected render path SetRenderPath(g_RenderingOptions.GetRenderPath()); m->deviceCommandContext = Renderer::Backend::GL::CDeviceCommandContext::Create(); if (g_RenderingOptions.GetPostProc()) m->postprocManager.Initialize(); m->sceneRenderer.Initialize(); return true; } void CRenderer::Resize(int width, int height) { m_Width = width; m_Height = height; m->postprocManager.Resize(); m->sceneRenderer.Resize(width, height); } void CRenderer::SetRenderPath(RenderPath rp) { if (!m->IsOpen) { // Delay until Open() is called. return; } // Renderer has been opened, so validate the selected renderpath if (rp == RenderPath::DEFAULT) { if (m_Caps.m_ARBProgram || (m_Caps.m_VertexShader && m_Caps.m_FragmentShader && g_VideoMode.GetBackend() != CVideoMode::Backend::GL_ARB)) rp = RenderPath::SHADER; else rp = RenderPath::FIXED; } if (rp == RenderPath::SHADER) { if (!(m_Caps.m_ARBProgram || (m_Caps.m_VertexShader && m_Caps.m_FragmentShader && g_VideoMode.GetBackend() != CVideoMode::Backend::GL_ARB))) { LOGWARNING("Falling back to fixed function\n"); rp = RenderPath::FIXED; } } // TODO: remove this once capabilities have been properly extracted and the above checks have been moved elsewhere. g_RenderingOptions.m_RenderPath = rp; MakeShadersDirty(); // We might need to regenerate some render data after changing path if (g_Game) g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_COLOR); } bool CRenderer::ShouldRender() const { return !g_app_minimized && (g_app_has_focus || !g_VideoMode.IsInFullscreen()); } -void CRenderer::RenderFrame(bool needsPresent) +void CRenderer::RenderFrame(const bool needsPresent) { // Do not render if not focused while in fullscreen or minimised, // as that triggers a difficult-to-reproduce crash on some graphic cards. if (!ShouldRender()) return; if (m_ShouldPreloadResourcesBeforeNextFrame) { m_ShouldPreloadResourcesBeforeNextFrame = false; // We don't meed to render logger for the preload. RenderFrameImpl(true, false); } if (m_ScreenShotType == ScreenShotType::BIG) { - RenderBigScreenShot(); + RenderBigScreenShot(needsPresent); } else { if (m_ScreenShotType == ScreenShotType::DEFAULT) RenderScreenShot(); else RenderFrameImpl(true, true); if (needsPresent) g_VideoMode.GetBackendDevice()->Present(); } } -void CRenderer::RenderFrameImpl(bool renderGUI, bool renderLogger) +void CRenderer::RenderFrameImpl(const bool renderGUI, const bool renderLogger) { PROFILE3("render"); g_Profiler2.RecordGPUFrameStart(); ogl_WarnIfError(); g_TexMan.UploadResourcesIfNeeded(m->deviceCommandContext.get()); // prepare before starting the renderer frame if (g_Game && g_Game->IsGameStarted()) g_Game->GetView()->BeginFrame(); if (g_Game) m->sceneRenderer.SetSimulation(g_Game->GetSimulation2()); // start new frame BeginFrame(); ogl_WarnIfError(); if (g_Game && g_Game->IsGameStarted()) { g_Game->GetView()->Render(); ogl_WarnIfError(); } m->sceneRenderer.RenderTextOverlays(); // If we're in Atlas game view, render special tools if (g_AtlasGameLoop && g_AtlasGameLoop->view) { g_AtlasGameLoop->view->DrawCinemaPathTool(); ogl_WarnIfError(); } if (g_Game && g_Game->IsGameStarted()) { g_Game->GetView()->GetCinema()->Render(); ogl_WarnIfError(); } glDisable(GL_DEPTH_TEST); if (renderGUI) { OGL_SCOPED_DEBUG_GROUP("Draw GUI"); // All GUI elements are drawn in Z order to render semi-transparent // objects correctly. g_GUI->Draw(); ogl_WarnIfError(); } // If we're in Atlas game view, render special overlays (e.g. editor bandbox). if (g_AtlasGameLoop && g_AtlasGameLoop->view) { CCanvas2D canvas; g_AtlasGameLoop->view->DrawOverlays(canvas); ogl_WarnIfError(); } g_Console->Render(); ogl_WarnIfError(); if (renderLogger) { g_Logger->Render(); ogl_WarnIfError(); } // Profile information g_ProfileViewer.RenderProfile(); ogl_WarnIfError(); glEnable(GL_DEPTH_TEST); EndFrame(); const Stats& stats = GetStats(); PROFILE2_ATTR("draw calls: %zu", stats.m_DrawCalls); PROFILE2_ATTR("terrain tris: %zu", stats.m_TerrainTris); PROFILE2_ATTR("water tris: %zu", stats.m_WaterTris); PROFILE2_ATTR("model tris: %zu", stats.m_ModelTris); PROFILE2_ATTR("overlay tris: %zu", stats.m_OverlayTris); PROFILE2_ATTR("blend splats: %zu", stats.m_BlendSplats); PROFILE2_ATTR("particles: %zu", stats.m_Particles); ogl_WarnIfError(); g_Profiler2.RecordGPUFrameEnd(); ogl_WarnIfError(); } void CRenderer::RenderScreenShot() { m_ScreenShotType = ScreenShotType::NONE; // get next available numbered filename // note: %04d -> always 4 digits, so sorting by filename works correctly. const VfsPath filenameFormat(L"screenshots/screenshot%04d.png"); VfsPath filename; vfs::NextNumberedFilename(g_VFS, filenameFormat, g_NextScreenShotNumber, filename); const size_t w = (size_t)g_xres, h = (size_t)g_yres; const size_t bpp = 24; GLenum fmt = GL_RGB; int flags = TEX_BOTTOM_UP; // Hide log messages and re-render RenderFrameImpl(true, false); const size_t img_size = w * h * bpp / 8; const size_t hdr_size = tex_hdr_size(filename); std::shared_ptr buf; AllocateAligned(buf, hdr_size + img_size, maxSectorSize); GLvoid* img = buf.get() + hdr_size; Tex t; if (t.wrap(w, h, bpp, flags, buf, hdr_size) < 0) return; glReadPixels(0, 0, (GLsizei)w, (GLsizei)h, fmt, GL_UNSIGNED_BYTE, img); if (tex_write(&t, filename) == INFO::OK) { OsPath realPath; g_VFS->GetRealPath(filename, realPath); LOGMESSAGERENDER(g_L10n.Translate("Screenshot written to '%s'"), realPath.string8()); debug_printf( CStr(g_L10n.Translate("Screenshot written to '%s'") + "\n").c_str(), realPath.string8().c_str()); } else LOGERROR("Error writing screenshot to '%s'", filename.string8()); } -void CRenderer::RenderBigScreenShot() +void CRenderer::RenderBigScreenShot(const bool needsPresent) { m_ScreenShotType = ScreenShotType::NONE; // If the game hasn't started yet then use WriteScreenshot to generate the image. if (!g_Game) return RenderScreenShot(); int tiles = 4, tileWidth = 256, tileHeight = 256; CFG_GET_VAL("screenshot.tiles", tiles); CFG_GET_VAL("screenshot.tilewidth", tileWidth); CFG_GET_VAL("screenshot.tileheight", tileHeight); if (tiles <= 0 || tileWidth <= 0 || tileHeight <= 0 || tileWidth * tiles % 4 != 0 || tileHeight * tiles % 4 != 0) { LOGWARNING("Invalid big screenshot size: tiles=%d tileWidth=%d tileHeight=%d", tiles, tileWidth, tileHeight); return; } // get next available numbered filename // note: %04d -> always 4 digits, so sorting by filename works correctly. const VfsPath filenameFormat(L"screenshots/screenshot%04d.bmp"); VfsPath filename; vfs::NextNumberedFilename(g_VFS, filenameFormat, g_NextScreenShotNumber, filename); // Slightly ugly and inflexible: Always draw 640*480 tiles onto the screen, and // hope the screen is actually large enough for that. ENSURE(g_xres >= tileWidth && g_yres >= tileHeight); - const int img_w = tileWidth * tiles, img_h = tileHeight * tiles; + const int imageWidth = tileWidth * tiles, imageHeight = tileHeight * tiles; const int bpp = 24; // we want writing BMP to be as fast as possible, // so read data from OpenGL in BMP format to obviate conversion. #if CONFIG2_GLES // GLES doesn't support BGR const GLenum fmt = GL_RGB; const int flags = TEX_BOTTOM_UP; #else const GLenum fmt = GL_BGR; const int flags = TEX_BOTTOM_UP | TEX_BGR; #endif - const size_t img_size = img_w * img_h * bpp / 8; - const size_t tile_size = tileWidth * tileHeight * bpp / 8; - const size_t hdr_size = tex_hdr_size(filename); - void* tile_data = malloc(tile_size); - if (!tile_data) + const size_t imageSize = imageWidth * imageHeight * bpp / 8; + const size_t tileSize = tileWidth * tileHeight * bpp / 8; + const size_t headerSize = tex_hdr_size(filename); + void* tileData = malloc(tileSize); + if (!tileData) { WARN_IF_ERR(ERR::NO_MEM); return; } - std::shared_ptr img_buf; - AllocateAligned(img_buf, hdr_size + img_size, maxSectorSize); + std::shared_ptr imageBuffer; + AllocateAligned(imageBuffer, headerSize + imageSize, maxSectorSize); Tex t; - GLvoid* img = img_buf.get() + hdr_size; - if (t.wrap(img_w, img_h, bpp, flags, img_buf, hdr_size) < 0) + GLvoid* img = imageBuffer.get() + headerSize; + if (t.wrap(imageWidth, imageHeight, bpp, flags, imageBuffer, headerSize) < 0) { - free(tile_data); + free(tileData); return; } ogl_WarnIfError(); CCamera oldCamera = *g_Game->GetView()->GetCamera(); // Resize various things so that the sizes and aspect ratios are correct { g_Renderer.Resize(tileWidth, tileHeight); SViewPort vp = { 0, 0, tileWidth, tileHeight }; g_Game->GetView()->SetViewport(vp); } -#if !CONFIG2_GLES - // Temporarily move everything onto the front buffer, so the user can - // see the exciting progress as it renders (and can tell when it's finished). - // (It doesn't just use SwapBuffers, because it doesn't know whether to - // call the SDL version or the Atlas version.) - GLint oldReadBuffer, oldDrawBuffer; - glGetIntegerv(GL_READ_BUFFER, &oldReadBuffer); - glGetIntegerv(GL_DRAW_BUFFER, &oldDrawBuffer); - glDrawBuffer(GL_FRONT); - glReadBuffer(GL_FRONT); -#endif - // Render each tile CMatrix3D projection; projection.SetIdentity(); const float aspectRatio = 1.0f * tileWidth / tileHeight; - for (int tile_y = 0; tile_y < tiles; ++tile_y) + for (int tileY = 0; tileY < tiles; ++tileY) { - for (int tile_x = 0; tile_x < tiles; ++tile_x) + for (int tileX = 0; tileX < tiles; ++tileX) { // Adjust the camera to render the appropriate region if (oldCamera.GetProjectionType() == CCamera::ProjectionType::PERSPECTIVE) { - projection.SetPerspectiveTile(oldCamera.GetFOV(), aspectRatio, oldCamera.GetNearPlane(), oldCamera.GetFarPlane(), tiles, tile_x, tile_y); + projection.SetPerspectiveTile(oldCamera.GetFOV(), aspectRatio, oldCamera.GetNearPlane(), oldCamera.GetFarPlane(), tiles, tileX, tileY); } g_Game->GetView()->GetCamera()->SetProjection(projection); RenderFrameImpl(false, false); // Copy the tile pixels into the main image - glReadPixels(0, 0, tileWidth, tileHeight, fmt, GL_UNSIGNED_BYTE, tile_data); + glReadPixels(0, 0, tileWidth, tileHeight, fmt, GL_UNSIGNED_BYTE, tileData); for (int y = 0; y < tileHeight; ++y) { - void* dest = static_cast(img) + ((tile_y * tileHeight + y) * img_w + (tile_x * tileWidth)) * bpp / 8; - void* src = static_cast(tile_data) + y * tileWidth * bpp / 8; + void* dest = static_cast(img) + ((tileY * tileHeight + y) * imageWidth + (tileX * tileWidth)) * bpp / 8; + void* src = static_cast(tileData) + y * tileWidth * bpp / 8; memcpy(dest, src, tileWidth * bpp / 8); } + if (needsPresent) + g_VideoMode.GetBackendDevice()->Present(); } } -#if !CONFIG2_GLES - // Restore the buffer settings - glDrawBuffer(oldDrawBuffer); - glReadBuffer(oldReadBuffer); -#endif - // Restore the viewport settings { g_Renderer.Resize(g_xres, g_yres); SViewPort vp = { 0, 0, g_xres, g_yres }; g_Game->GetView()->SetViewport(vp); g_Game->GetView()->GetCamera()->SetProjectionFromCamera(oldCamera); } if (tex_write(&t, filename) == INFO::OK) { OsPath realPath; g_VFS->GetRealPath(filename, realPath); LOGMESSAGERENDER(g_L10n.Translate("Screenshot written to '%s'"), realPath.string8()); debug_printf( CStr(g_L10n.Translate("Screenshot written to '%s'") + "\n").c_str(), realPath.string8().c_str()); } else LOGERROR("Error writing screenshot to '%s'", filename.string8()); - free(tile_data); + free(tileData); } void CRenderer::BeginFrame() { PROFILE("begin frame"); // Zero out all the per-frame stats. m_Stats.Reset(); if (m->ShadersDirty) ReloadShaders(); m->sceneRenderer.BeginFrame(); } void CRenderer::EndFrame() { PROFILE3("end frame"); m->sceneRenderer.EndFrame(); BindTexture(0, 0); } void CRenderer::SetViewport(const SViewPort &vp) { m_Viewport = vp; glViewport((GLint)vp.m_X,(GLint)vp.m_Y,(GLsizei)vp.m_Width,(GLsizei)vp.m_Height); } SViewPort CRenderer::GetViewport() { return m_Viewport; } void CRenderer::BindTexture(int unit, GLuint tex) { glActiveTextureARB(GL_TEXTURE0+unit); glBindTexture(GL_TEXTURE_2D, tex); } void CRenderer::MakeShadersDirty() { m->ShadersDirty = true; m->sceneRenderer.MakeShadersDirty(); } CTextureManager& CRenderer::GetTextureManager() { return m->textureManager; } CShaderManager& CRenderer::GetShaderManager() { return m->shaderManager; } CTimeManager& CRenderer::GetTimeManager() { return m->timeManager; } CPostprocManager& CRenderer::GetPostprocManager() { return m->postprocManager; } CSceneRenderer& CRenderer::GetSceneRenderer() { return m->sceneRenderer; } CDebugRenderer& CRenderer::GetDebugRenderer() { return m->debugRenderer; } CFontManager& CRenderer::GetFontManager() { return m->fontManager; } void CRenderer::PreloadResourcesBeforeNextFrame() { m_ShouldPreloadResourcesBeforeNextFrame = true; } void CRenderer::MakeScreenShotOnNextFrame(ScreenShotType screenShotType) { m_ScreenShotType = screenShotType; } Renderer::Backend::GL::CDeviceCommandContext* CRenderer::GetDeviceCommandContext() { return m->deviceCommandContext.get(); } Index: ps/trunk/source/renderer/Renderer.h =================================================================== --- ps/trunk/source/renderer/Renderer.h (revision 26185) +++ ps/trunk/source/renderer/Renderer.h (revision 26186) @@ -1,204 +1,204 @@ /* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #ifndef INCLUDED_RENDERER #define INCLUDED_RENDERER #include "graphics/Camera.h" #include "graphics/ShaderDefines.h" #include "graphics/ShaderProgramPtr.h" #include "ps/Singleton.h" #include "renderer/backend/gl/DeviceCommandContext.h" #include "renderer/RenderingOptions.h" #include "renderer/Scene.h" #include class CDebugRenderer; class CFontManager; class CPostprocManager; class CSceneRenderer; class CShaderManager; class CTextureManager; class CTimeManager; #define g_Renderer CRenderer::GetSingleton() /** * Higher level interface on top of the whole frame rendering. It does know * what should be rendered and via which renderer but shouldn't know how to * render a particular area, like UI or scene. */ class CRenderer : public Singleton { public: // stats class - per frame counts of number of draw calls, poly counts etc struct Stats { // set all stats to zero void Reset() { memset(this, 0, sizeof(*this)); } // number of draw calls per frame - total DrawElements + Begin/End immediate mode loops size_t m_DrawCalls; // number of terrain triangles drawn size_t m_TerrainTris; // number of water triangles drawn size_t m_WaterTris; // number of (non-transparent) model triangles drawn size_t m_ModelTris; // number of overlay triangles drawn size_t m_OverlayTris; // number of splat passes for alphamapping size_t m_BlendSplats; // number of particles size_t m_Particles; }; struct Caps { bool m_ARBProgram; bool m_ARBProgramShadow; bool m_VertexShader; bool m_FragmentShader; bool m_Shadows; bool m_PrettyWater; }; enum class ScreenShotType { NONE, DEFAULT, BIG }; public: CRenderer(); ~CRenderer(); // open up the renderer: performs any necessary initialisation bool Open(int width, int height); // resize renderer view void Resize(int width, int height); // return view width int GetWidth() const { return m_Width; } // return view height int GetHeight() const { return m_Height; } void RenderFrame(bool needsPresent); // signal frame start void BeginFrame(); // signal frame end void EndFrame(); // trigger a reload of shaders (when parameters they depend on have changed) void MakeShadersDirty(); // set the viewport void SetViewport(const SViewPort &); // get the last viewport SViewPort GetViewport(); // bind a GL texture object to active unit void BindTexture(int unit, unsigned int tex); // return stats accumulated for current frame Stats& GetStats() { return m_Stats; } CTextureManager& GetTextureManager(); CShaderManager& GetShaderManager(); CFontManager& GetFontManager(); CTimeManager& GetTimeManager(); CPostprocManager& GetPostprocManager(); CSceneRenderer& GetSceneRenderer(); CDebugRenderer& GetDebugRenderer(); /** * GetCapabilities: Return which OpenGL capabilities are available and enabled. * * @return capabilities structure */ const Caps& GetCapabilities() const { return m_Caps; } /** * Performs a complete frame without presenting to force loading all needed * resources. It's used for the first frame on a game start. * TODO: It might be better to preload resources without a complete frame * rendering. */ void PreloadResourcesBeforeNextFrame(); /** * Makes a screenshot on the next RenderFrame according of the given * screenshot type. */ void MakeScreenShotOnNextFrame(ScreenShotType screenShotType); Renderer::Backend::GL::CDeviceCommandContext* GetDeviceCommandContext(); protected: friend class CPatchRData; friend class CDecalRData; friend class HWLightingModelRenderer; friend class ShaderModelVertexRenderer; friend class InstancingModelRenderer; friend class CRenderingOptions; bool ShouldRender() const; - void RenderFrameImpl(bool renderGUI, bool renderLogger); + void RenderFrameImpl(const bool renderGUI, const bool renderLogger); void RenderScreenShot(); - void RenderBigScreenShot(); + void RenderBigScreenShot(const bool needsPresent); // SetRenderPath: Select the preferred render path. // This may only be called before Open(), because the layout of vertex arrays and other // data may depend on the chosen render path. void SetRenderPath(RenderPath rp); void ReloadShaders(); // Private data that is not needed by inline functions. class Internals; std::unique_ptr m; // view width int m_Width = 0; // view height int m_Height = 0; SViewPort m_Viewport; // card capabilities Caps m_Caps; // build card cap bits void EnumCaps(); // per-frame renderer stats Stats m_Stats; bool m_ShouldPreloadResourcesBeforeNextFrame = false; ScreenShotType m_ScreenShotType = ScreenShotType::NONE; }; #endif // INCLUDED_RENDERER