1
0
forked from mirrors/0ad

Moves frame rendering function to CRenderer and combines with making screenshots.

Tested By: Stan
Differential Revision: https://code.wildfiregames.com/D4414
This was SVN commit r26166.
This commit is contained in:
vladislavbelov
2022-01-04 18:13:45 +00:00
parent 87b5c233c5
commit 5e3426794c
13 changed files with 425 additions and 422 deletions
+3 -15
View File
@@ -219,19 +219,12 @@ static InReaction MainInputHandler(const SDL_Event_* ev)
}
else if (hotkey == "screenshot")
{
WriteScreenshot(L".png");
g_Renderer.MakeScreenShotOnNextFrame(CRenderer::ScreenShotType::DEFAULT);
return IN_HANDLED;
}
else if (hotkey == "bigscreenshot")
{
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)
WriteBigScreenshot(L".bmp", tiles, tileWidth, tileHeight);
else
LOGWARNING("Invalid big screenshot size: tiles=%d tileWidth=%d tileHeight=%d", tiles, tileWidth, tileHeight);
g_Renderer.MakeScreenShotOnNextFrame(CRenderer::ScreenShotType::BIG);
return IN_HANDLED;
}
else if (hotkey == "togglefullscreen")
@@ -456,12 +449,7 @@ static void Frame()
if (g_SoundManager)
g_SoundManager->IdleTask();
if (ShouldRender())
{
Render();
g_VideoMode.GetBackendDevice()->Present();
}
g_Renderer.RenderFrame(true);
g_Profiler.Frame();
+3 -3
View File
@@ -317,11 +317,11 @@ PSRETURN CGame::ReallyStartGame()
// all be invisible)
Interpolate(0, 0);
m_GameStarted=true;
m_GameStarted = true;
// Render a frame to begin loading assets
// Preload resources to avoid blinking on a first game frame.
if (CRenderer::IsInitialised())
Render();
g_Renderer.PreloadResourcesBeforeNextFrame();
if (g_NetClient)
g_NetClient->LoadFinished();
+2 -160
View File
@@ -19,14 +19,8 @@
#include "ps/GameSetup/GameSetup.h"
#include "graphics/Canvas2D.h"
#include "graphics/CinemaManager.h"
#include "graphics/Color.h"
#include "graphics/GameView.h"
#include "graphics/LightEnv.h"
#include "graphics/MapReader.h"
#include "graphics/MaterialManager.h"
#include "graphics/ModelDef.h"
#include "graphics/TerrainTextureManager.h"
#include "gui/CGUI.h"
#include "gui/GUIManager.h"
@@ -41,7 +35,6 @@
#include "lib/res/h_mgr.h"
#include "lib/timer.h"
#include "lobby/IXmppClient.h"
#include "maths/MathUtil.h"
#include "network/NetServer.h"
#include "network/NetClient.h"
#include "network/NetMessage.h"
@@ -74,7 +67,6 @@
#include "ps/VideoMode.h"
#include "ps/VisualReplay.h"
#include "ps/World.h"
#include "renderer/ModelRenderer.h"
#include "renderer/Renderer.h"
#include "renderer/SceneRenderer.h"
#include "renderer/VertexBufferManager.h"
@@ -88,7 +80,6 @@
#include "soundmanager/scripting/JSInterface_Sound.h"
#include "soundmanager/ISoundManager.h"
#include "tools/atlas/GameInterface/GameLoop.h"
#include "tools/atlas/GameInterface/View.h"
#if !(OS_WIN || OS_MACOSX || OS_ANDROID) // assume all other platforms use X11 for wxWidgets
#define MUST_INIT_X11 1
@@ -111,9 +102,6 @@ ERROR_TYPE(System, SDLInitFailed);
ERROR_TYPE(System, VmodeFailed);
ERROR_TYPE(System, RequiredExtensionsMissing);
bool g_DoRenderGui = true;
bool g_DoRenderLogger = true;
thread_local std::shared_ptr<ScriptContext> g_ScriptContext;
static const int SANE_TEX_QUALITY_DEFAULT = 5; // keep in sync with code
@@ -178,112 +166,6 @@ retry:
ogl_tex_set_defaults(q_flags, filter);
}
//----------------------------------------------------------------------------
// GUI integration
//----------------------------------------------------------------------------
bool ShouldRender()
{
return !g_app_minimized && (g_app_has_focus || !g_VideoMode.IsInFullscreen());
}
void Render()
{
// 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;
PROFILE3("render");
g_Profiler2.RecordGPUFrameStart();
ogl_WarnIfError();
// prepare before starting the renderer frame
if (g_Game && g_Game->IsGameStarted())
g_Game->GetView()->BeginFrame();
if (g_Game)
g_Renderer.GetSceneRenderer().SetSimulation(g_Game->GetSimulation2());
// start new frame
g_Renderer.BeginFrame();
ogl_WarnIfError();
if (g_Game && g_Game->IsGameStarted())
{
g_Game->GetView()->Render();
ogl_WarnIfError();
}
g_Renderer.GetSceneRenderer().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 (g_DoRenderGui)
{
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 (g_DoRenderLogger)
{
g_Logger->Render();
ogl_WarnIfError();
}
// Profile information
g_ProfileViewer.RenderProfile();
ogl_WarnIfError();
glEnable(GL_DEPTH_TEST);
g_Renderer.EndFrame();
PROFILE2_ATTR("draw calls: %d", (int)g_Renderer.GetStats().m_DrawCalls);
PROFILE2_ATTR("terrain tris: %d", (int)g_Renderer.GetStats().m_TerrainTris);
PROFILE2_ATTR("water tris: %d", (int)g_Renderer.GetStats().m_WaterTris);
PROFILE2_ATTR("model tris: %d", (int)g_Renderer.GetStats().m_ModelTris);
PROFILE2_ATTR("overlay tris: %d", (int)g_Renderer.GetStats().m_OverlayTris);
PROFILE2_ATTR("blend splats: %d", (int)g_Renderer.GetStats().m_BlendSplats);
PROFILE2_ATTR("particles: %d", (int)g_Renderer.GetStats().m_Particles);
ogl_WarnIfError();
g_Profiler2.RecordGPUFrameEnd();
ogl_WarnIfError();
}
ErrorReactionInternal psDisplayError(const wchar_t* UNUSED(text), size_t UNUSED(flags))
{
// If we're fullscreen, then sometimes (at least on some particular drivers on Linux)
@@ -475,37 +357,6 @@ static void ShutdownPs()
UnloadHotkeys();
}
static void InitRenderer()
{
TIMER(L"InitRenderer");
// create renderer
new CRenderer;
// create terrain related stuff
new CTerrainTextureManager;
g_Renderer.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.
g_Renderer.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;
g_Renderer.SetViewport(vp);
ModelDefActivateFastImpl();
ColorActivateFastImpl();
ModelRenderer::Init();
}
static void InitSDL()
{
#if OS_LINUX
@@ -931,7 +782,8 @@ void InitGraphics(const CmdLineArgs& args, int flags, const std::vector<CStr>& i
g_RenderingOptions.ReadConfigAndSetupHooks();
InitRenderer();
// create renderer
new CRenderer;
InitInput();
@@ -981,16 +833,6 @@ void InitNonVisual(const CmdLineArgs& args)
Autostart(args);
}
void RenderGui(bool RenderingState)
{
g_DoRenderGui = RenderingState;
}
void RenderLogger(bool RenderingState)
{
g_DoRenderLogger = RenderingState;
}
/**
* Temporarily loads a scenario map and retrieves the "ScriptSettings" JSON
* data from it.
-14
View File
@@ -25,14 +25,6 @@
class CmdLineArgs;
class Paths;
//
// GUI integration
//
extern void Render();
extern bool ShouldRender();
/**
* initialize global modules that are be needed before Init.
* must be called from the very beginning of main.
@@ -72,12 +64,6 @@ enum ShutdownFlags
SHUTDOWN_FROM_CONFIG = 1
};
/**
* enable/disable rendering of the GUI (intended mainly for screenshots)
*/
extern void RenderGui(bool RenderingState);
extern void RenderLogger(bool RenderingState);
extern const std::vector<CStr>& GetMods(const CmdLineArgs& args, int flags);
/**
-208
View File
@@ -20,10 +20,7 @@
#include "ps/Util.h"
#include "graphics/GameView.h"
#include "i18n/L10n.h"
#include "lib/allocators/shared_ptr.h"
#include "lib/bits.h" // round_up
#include "lib/ogl.h"
#include "lib/posix/posix_utsname.h"
#include "lib/sysdep/cpu.h"
#include "lib/sysdep/os_cpu.h"
@@ -31,17 +28,13 @@
#include "lib/sysdep/sysdep.h" // sys_OpenFile
#include "lib/tex/tex.h"
#include "lib/timer.h"
#include "lib/utf8.h"
#include "maths/MathUtil.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/Game.h"
#include "ps/GameSetup/Config.h"
#include "ps/GameSetup/GameSetup.h"
#include "ps/Pyrogenesis.h"
#include "ps/VideoMode.h"
#include "renderer/backend/gl/Device.h"
#include "renderer/Renderer.h"
#if CONFIG2_AUDIO
#include "soundmanager/SoundManager.h"
@@ -206,207 +199,6 @@ OsPath createDateIndexSubdirectory(const OsPath& parentDir)
return path;
}
static size_t s_nextScreenshotNumber;
// <extension> identifies the file format that is to be written
// (case-insensitive). examples: "bmp", "png", "jpg".
// BMP is good for quick output at the expense of large files.
void WriteScreenshot(const VfsPath& extension)
{
// get next available numbered filename
// note: %04d -> always 4 digits, so sorting by filename works correctly.
const VfsPath basenameFormat(L"screenshots/screenshot%04d");
const VfsPath filenameFormat = basenameFormat.ChangeExtension(extension);
VfsPath filename;
vfs::NextNumberedFilename(g_VFS, filenameFormat, s_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;
// we want writing BMP to be as fast as possible,
// so read data from OpenGL in BMP format to obviate conversion.
if(extension == L".bmp")
{
#if !CONFIG2_GLES // GLES doesn't support BGR
fmt = GL_BGR;
flags |= TEX_BGR;
#endif
}
// Hide log messages and re-render
RenderLogger(false);
Render();
RenderLogger(true);
const size_t img_size = w * h * bpp/8;
const size_t hdr_size = tex_hdr_size(filename);
std::shared_ptr<u8> 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());
}
// Similar to WriteScreenshot, but generates an image of size tileWidth*tiles x tileHeight*tiles.
void WriteBigScreenshot(const VfsPath& extension, int tiles, int tileWidth, int tileHeight)
{
// If the game hasn't started yet then use WriteScreenshot to generate the image.
if (g_Game == nullptr)
{
WriteScreenshot(L".bmp");
return;
}
// get next available numbered filename
// note: %04d -> always 4 digits, so sorting by filename works correctly.
const VfsPath basenameFormat(L"screenshots/screenshot%04d");
const VfsPath filenameFormat = basenameFormat.ChangeExtension(extension);
VfsPath filename;
vfs::NextNumberedFilename(g_VFS, filenameFormat, s_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 bpp = 24;
GLenum fmt = GL_RGB;
int flags = TEX_BOTTOM_UP;
// we want writing BMP to be as fast as possible,
// so read data from OpenGL in BMP format to obviate conversion.
if(extension == L".bmp")
{
#if !CONFIG2_GLES // GLES doesn't support BGR
fmt = GL_BGR;
flags |= 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)
{
WARN_IF_ERR(ERR::NO_MEM);
return;
}
std::shared_ptr<u8> img_buf;
AllocateAligned(img_buf, hdr_size + img_size, maxSectorSize);
Tex t;
GLvoid* img = img_buf.get() + hdr_size;
if(t.wrap(img_w, img_h, bpp, flags, img_buf, hdr_size) < 0)
{
free(tile_data);
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 tile_x = 0; tile_x < tiles; ++tile_x)
{
// 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);
}
g_Game->GetView()->GetCamera()->SetProjection(projection);
RenderLogger(false);
RenderGui(false);
Render();
RenderGui(true);
RenderLogger(true);
// Copy the tile pixels into the main image
glReadPixels(0, 0, tileWidth, tileHeight, fmt, GL_UNSIGNED_BYTE, tile_data);
for (int y = 0; y < tileHeight; ++y)
{
void* dest = static_cast<char*>(img) + ((tile_y * tileHeight + y) * img_w + (tile_x * tileWidth)) * bpp / 8;
void* src = static_cast<char*>(tile_data) + y * tileWidth * bpp / 8;
memcpy(dest, src, tileWidth * bpp / 8);
}
}
}
#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);
}
std::string Hexify(const std::string& s)
{
std::stringstream str;
+1 -4
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* 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
@@ -29,9 +29,6 @@ const wchar_t* ErrorString(int err);
OsPath createDateIndexSubdirectory(const OsPath& parentDir);
void WriteScreenshot(const VfsPath& extension);
void WriteBigScreenshot(const VfsPath& extension, int tiles = 4, int tileWidth = 640, int tileHeight = 480);
Status tex_write(Tex* t, const VfsPath& filename);
std::string Hexify(const std::string& s);
+371 -6
View File
@@ -15,24 +15,33 @@
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* higher level interface on top of OpenGL to render basic objects:
* terrain, models, sprites, particles etc.
*/
#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/res/graphics/ogl_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/Loader.h"
#include "ps/ProfileViewer.h"
#include "graphics/Camera.h"
#include "graphics/FontManager.h"
@@ -40,7 +49,9 @@
#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"
@@ -48,9 +59,16 @@
#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 <algorithm>
namespace
{
size_t g_NextScreenShotNumber = 0;
///////////////////////////////////////////////////////////////////////////////////
// CRendererStatsTable - Profile display of rendering stats
@@ -212,6 +230,8 @@ AbstractProfileTable* CRendererStatsTable::GetChild(size_t UNUSED(row))
return 0;
}
} // anonymous namespace
///////////////////////////////////////////////////////////////////////////////////
// CRenderer implementation
@@ -258,6 +278,8 @@ public:
CRenderer::CRenderer()
{
TIMER(L"InitRenderer");
m = std::make_unique<Internals>();
g_ProfileViewer.AddRootTable(&m->profileTable);
@@ -266,6 +288,28 @@ CRenderer::CRenderer()
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()
@@ -408,6 +452,317 @@ void CRenderer::SetRenderPath(RenderPath rp)
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)
{
// 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();
}
else
{
if (m_ScreenShotType == ScreenShotType::DEFAULT)
RenderScreenShot();
else
RenderFrameImpl(true, true);
if (needsPresent)
g_VideoMode.GetBackendDevice()->Present();
}
}
void CRenderer::RenderFrameImpl(bool renderGUI, bool renderLogger)
{
PROFILE3("render");
g_Profiler2.RecordGPUFrameStart();
ogl_WarnIfError();
// 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<u8> 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()
{
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 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)
{
WARN_IF_ERR(ERR::NO_MEM);
return;
}
std::shared_ptr<u8> img_buf;
AllocateAligned(img_buf, hdr_size + img_size, maxSectorSize);
Tex t;
GLvoid* img = img_buf.get() + hdr_size;
if (t.wrap(img_w, img_h, bpp, flags, img_buf, hdr_size) < 0)
{
free(tile_data);
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 tile_x = 0; tile_x < tiles; ++tile_x)
{
// 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);
}
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);
for (int y = 0; y < tileHeight; ++y)
{
void* dest = static_cast<char*>(img) + ((tile_y * tileHeight + y) * img_w + (tile_x * tileWidth)) * bpp / 8;
void* src = static_cast<char*>(tile_data) + y * tileWidth * bpp / 8;
memcpy(dest, src, tileWidth * bpp / 8);
}
}
}
#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);
}
void CRenderer::BeginFrame()
{
PROFILE("begin frame");
@@ -488,3 +843,13 @@ CFontManager& CRenderer::GetFontManager()
{
return m->fontManager;
}
void CRenderer::PreloadResourcesBeforeNextFrame()
{
m_ShouldPreloadResourcesBeforeNextFrame = true;
}
void CRenderer::MakeScreenShotOnNextFrame(ScreenShotType screenShotType)
{
m_ScreenShotType = screenShotType;
}
+35 -2
View File
@@ -76,6 +76,13 @@ public:
bool m_PrettyWater;
};
enum class ScreenShotType
{
NONE,
DEFAULT,
BIG
};
public:
CRenderer();
~CRenderer();
@@ -91,6 +98,8 @@ public:
// return view height
int GetHeight() const { return m_Height; }
void RenderFrame(bool needsPresent);
// signal frame start
void BeginFrame();
// signal frame end
@@ -132,6 +141,20 @@ public:
*/
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);
protected:
friend class CPatchRData;
friend class CDecalRData;
@@ -140,6 +163,12 @@ protected:
friend class InstancingModelRenderer;
friend class CRenderingOptions;
bool ShouldRender() const;
void RenderFrameImpl(bool renderGUI, bool renderLogger);
void RenderScreenShot();
void RenderBigScreenShot();
// 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.
@@ -151,9 +180,9 @@ protected:
class Internals;
std::unique_ptr<Internals> m;
// view width
int m_Width;
int m_Width = 0;
// view height
int m_Height;
int m_Height = 0;
SViewPort m_Viewport;
@@ -163,6 +192,10 @@ protected:
void EnumCaps();
// per-frame renderer stats
Stats m_Stats;
bool m_ShouldPreloadResourcesBeforeNextFrame = false;
ScreenShotType m_ScreenShotType = ScreenShotType::NONE;
};
#endif // INCLUDED_RENDERER
@@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* 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
@@ -932,10 +932,10 @@ void ScenarioEditor::OnScreenshot(wxCommandEvent& event)
switch (event.GetId())
{
case ID_BigScreenshot:
POST_MESSAGE(Screenshot, (true, 10));
POST_MESSAGE(Screenshot, (true));
break;
case ID_Screenshot:
POST_MESSAGE(Screenshot, (false, 0));
POST_MESSAGE(Screenshot, (false));
break;
}
}
@@ -499,6 +499,7 @@ float ActorViewer::GetRepeatTimeByAttackType(const std::string& type) const
void ActorViewer::Render()
{
// TODO: ActorViewer should reuse CRenderer code and not duplicate it.
m.Terrain.MakeDirty(RENDERDATA_UPDATE_COLOR);
// Set simulation context for rendering purposes
@@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* 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
@@ -28,9 +28,9 @@
#include "lib/external_libraries/libsdl.h"
#include "maths/MathUtil.h"
#include "ps/Game.h"
#include "ps/Util.h"
#include "ps/GameSetup/Config.h"
#include "ps/GameSetup/GameSetup.h"
#include "renderer/Renderer.h"
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpSoundManager.h"
@@ -48,9 +48,9 @@ MESSAGEHANDLER(MessageTrace)
MESSAGEHANDLER(Screenshot)
{
if (msg->big)
WriteBigScreenshot(L".bmp", msg->tiles);
g_Renderer.MakeScreenShotOnNextFrame(CRenderer::ScreenShotType::BIG);
else
WriteScreenshot(L".png");
g_Renderer.MakeScreenShotOnNextFrame(CRenderer::ScreenShotType::DEFAULT);
}
QUERYHANDLER(Ping)
+1 -2
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* 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
@@ -273,7 +273,6 @@ MESSAGE(MessageTrace,
MESSAGE(Screenshot,
((bool, big))
((int, tiles)) // For big screenshots: the final image will be (640*tiles)x(480*tiles)
);
//////////////////////////////////////////////////////////////////////////
+1 -1
View File
@@ -229,7 +229,7 @@ void AtlasViewGame::Render()
camera.SetProjectionFromCamera(*g_Game->GetView()->GetCamera());
camera.UpdateFrustum();
::Render();
g_Renderer.RenderFrame(false);
Atlas_GLSwapBuffers((void*)g_AtlasGameLoop->glCanvas);
// In case of atlas the device's present will do only internal stuff
// without calling a real backbuffer swap.