Index: ps/trunk/source/renderer/Renderer.cpp
===================================================================
--- ps/trunk/source/renderer/Renderer.cpp (revision 27183)
+++ ps/trunk/source/renderer/Renderer.cpp (revision 27184)
@@ -1,793 +1,796 @@
/* 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/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/IDevice.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:
std::unique_ptr deviceCommandContext;
/// 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;
Internals() :
IsOpen(false), ShadersDirty(true), profileTable(g_Renderer.m_Stats),
deviceCommandContext(g_VideoMode.GetBackendDevice()->CreateCommandContext()),
textureManager(g_VFS, false, g_VideoMode.GetBackendDevice())
{
}
};
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()
{
delete &g_TexMan;
// 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::ReloadShaders()
{
ENSURE(m->IsOpen);
m->sceneRenderer.ReloadShaders();
m->ShadersDirty = false;
}
bool CRenderer::Open(int width, int height)
{
m->IsOpen = true;
// Dimensions
m_Width = width;
m_Height = height;
// Validate the currently selected render path
SetRenderPath(g_RenderingOptions.GetRenderPath());
if (m->postprocManager.IsEnabled())
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
const bool hasShadersSupport =
g_VideoMode.GetBackendDevice()->GetCapabilities().ARBShaders ||
g_VideoMode.GetBackendDevice()->GetBackend() != Renderer::Backend::Backend::GL_ARB;
if (rp == RenderPath::DEFAULT)
{
if (hasShadersSupport)
rp = RenderPath::SHADER;
else
rp = RenderPath::FIXED;
}
if (rp == RenderPath::SHADER)
{
if (!hasShadersSupport)
{
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();
}
bool CRenderer::ShouldRender() const
{
return !g_app_minimized && (g_app_has_focus || !g_VideoMode.IsInFullscreen());
}
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_ScreenShotType == ScreenShotType::BIG)
{
RenderBigScreenShot(needsPresent);
}
else if (m_ScreenShotType == ScreenShotType::DEFAULT)
{
RenderScreenShot(needsPresent);
}
else
{
if (needsPresent)
- g_VideoMode.GetBackendDevice()->AcquireNextBackbuffer();
+ {
+ // In case of no acquired backbuffer we have nothing render to.
+ if (!g_VideoMode.GetBackendDevice()->AcquireNextBackbuffer())
+ return;
+ }
if (m_ShouldPreloadResourcesBeforeNextFrame)
{
m_ShouldPreloadResourcesBeforeNextFrame = false;
// We don't need to render logger for the preload.
RenderFrameImpl(true, false);
}
RenderFrameImpl(true, true);
m->deviceCommandContext->Flush();
if (needsPresent)
g_VideoMode.GetBackendDevice()->Present();
}
}
void CRenderer::RenderFrameImpl(const bool renderGUI, const bool renderLogger)
{
PROFILE3("render");
g_Profiler2.RecordGPUFrameStart();
g_TexMan.UploadResourcesIfNeeded(m->deviceCommandContext.get());
m->textureManager.MakeUploadProgress(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();
if (g_Game && g_Game->IsGameStarted())
{
g_Game->GetView()->Render();
}
m->deviceCommandContext->BeginFramebufferPass(
m->deviceCommandContext->GetDevice()->GetCurrentBackbuffer());
// If we're in Atlas game view, render special tools
if (g_AtlasGameLoop && g_AtlasGameLoop->view)
{
g_AtlasGameLoop->view->DrawCinemaPathTool();
}
if (g_Game && g_Game->IsGameStarted())
{
g_Game->GetView()->GetCinema()->Render();
}
RenderFrame2D(renderGUI, renderLogger);
m->deviceCommandContext->EndFramebufferPass();
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);
g_Profiler2.RecordGPUFrameEnd();
}
void CRenderer::RenderFrame2D(const bool renderGUI, const bool renderLogger)
{
CCanvas2D canvas(g_xres, g_yres, g_VideoMode.GetScale(), m->deviceCommandContext.get());
m->sceneRenderer.RenderTextOverlays(canvas);
if (renderGUI)
{
GPU_SCOPED_LABEL(m->deviceCommandContext.get(), "Render GUI");
// All GUI elements are drawn in Z order to render semi-transparent
// objects correctly.
g_GUI->Draw(canvas);
}
// If we're in Atlas game view, render special overlays (e.g. editor bandbox).
if (g_AtlasGameLoop && g_AtlasGameLoop->view)
{
g_AtlasGameLoop->view->DrawOverlays(canvas);
}
{
GPU_SCOPED_LABEL(m->deviceCommandContext.get(), "Render console");
g_Console->Render(canvas);
}
if (renderLogger)
{
GPU_SCOPED_LABEL(m->deviceCommandContext.get(), "Render logger");
g_Logger->Render(canvas);
}
{
GPU_SCOPED_LABEL(m->deviceCommandContext.get(), "Render profiler");
// Profile information
g_ProfileViewer.RenderProfile(canvas);
}
}
void CRenderer::RenderScreenShot(const bool needsPresent)
{
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 width = static_cast(g_xres), height = static_cast(g_yres);
const size_t bpp = 24;
- if (needsPresent)
- g_VideoMode.GetBackendDevice()->AcquireNextBackbuffer();
+ if (needsPresent && !g_VideoMode.GetBackendDevice()->AcquireNextBackbuffer())
+ return;
// Hide log messages and re-render
RenderFrameImpl(true, false);
const size_t img_size = width * height * bpp / 8;
const size_t hdr_size = tex_hdr_size(filename);
std::shared_ptr buf;
AllocateAligned(buf, hdr_size + img_size, maxSectorSize);
void* img = buf.get() + hdr_size;
Tex t;
if (t.wrap(width, height, bpp, TEX_BOTTOM_UP, buf, hdr_size) < 0)
return;
m->deviceCommandContext->ReadbackFramebufferSync(0, 0, width, height, img);
m->deviceCommandContext->Flush();
if (needsPresent)
g_VideoMode.GetBackendDevice()->Present();
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(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(needsPresent);
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 imageWidth = tileWidth * tiles, imageHeight = tileHeight * tiles;
const int bpp = 24;
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 imageBuffer;
AllocateAligned(imageBuffer, headerSize + imageSize, maxSectorSize);
Tex t;
void* img = imageBuffer.get() + headerSize;
if (t.wrap(imageWidth, imageHeight, bpp, TEX_BOTTOM_UP, imageBuffer, headerSize) < 0)
{
free(tileData);
return;
}
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);
}
// Render each tile
CMatrix3D projection;
projection.SetIdentity();
const float aspectRatio = 1.0f * tileWidth / tileHeight;
for (int tileY = 0; tileY < tiles; ++tileY)
{
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, tileX, tileY);
}
g_Game->GetView()->GetCamera()->SetProjection(projection);
- if (needsPresent)
- g_VideoMode.GetBackendDevice()->AcquireNextBackbuffer();
-
- RenderFrameImpl(false, false);
+ if (needsPresent && g_VideoMode.GetBackendDevice()->AcquireNextBackbuffer())
+ {
+ RenderFrameImpl(false, false);
- m->deviceCommandContext->ReadbackFramebufferSync(0, 0, tileWidth, tileHeight, tileData);
- m->deviceCommandContext->Flush();
- if (needsPresent)
+ m->deviceCommandContext->ReadbackFramebufferSync(0, 0, tileWidth, tileHeight, tileData);
+ m->deviceCommandContext->Flush();
g_VideoMode.GetBackendDevice()->Present();
+ }
// Copy the tile pixels into the main image
for (int y = 0; y < tileHeight; ++y)
{
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);
}
}
}
// 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(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();
}
void CRenderer::SetViewport(const SViewPort &vp)
{
m_Viewport = vp;
Renderer::Backend::IDeviceCommandContext::Rect viewportRect;
viewportRect.x = vp.m_X;
viewportRect.y = vp.m_Y;
viewportRect.width = vp.m_Width;
viewportRect.height = vp.m_Height;
m->deviceCommandContext->SetViewports(1, &viewportRect);
}
SViewPort CRenderer::GetViewport()
{
return m_Viewport;
}
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::IDeviceCommandContext* CRenderer::GetDeviceCommandContext()
{
return m->deviceCommandContext.get();
}
Index: ps/trunk/source/renderer/backend/IDevice.h
===================================================================
--- ps/trunk/source/renderer/backend/IDevice.h (revision 27183)
+++ ps/trunk/source/renderer/backend/IDevice.h (revision 27184)
@@ -1,117 +1,117 @@
/* 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_BACKEND_IDEVICE
#define INCLUDED_RENDERER_BACKEND_IDEVICE
#include "graphics/Color.h"
#include "renderer/backend/Backend.h"
#include "renderer/backend/Format.h"
#include "renderer/backend/IBuffer.h"
#include "renderer/backend/IDevice.h"
#include "renderer/backend/IDeviceCommandContext.h"
#include "renderer/backend/IFramebuffer.h"
#include "renderer/backend/IShaderProgram.h"
#include "renderer/backend/ITexture.h"
#include "scriptinterface/ScriptForward.h"
#include
#include
#include
class CShaderDefines;
class CStr;
namespace Renderer
{
namespace Backend
{
class IDevice
{
public:
struct Capabilities
{
bool S3TC;
bool ARBShaders;
bool ARBShadersShadow;
bool computeShaders;
bool debugLabels;
bool debugScopedLabels;
bool multisampling;
bool anisotropicFiltering;
uint32_t maxSampleCount;
float maxAnisotropy;
uint32_t maxTextureSize;
bool instancing;
};
virtual ~IDevice() {}
virtual Backend GetBackend() const = 0;
virtual const std::string& GetName() const = 0;
virtual const std::string& GetVersion() const = 0;
virtual const std::string& GetDriverInformation() const = 0;
virtual const std::vector& GetExtensions() const = 0;
virtual void Report(const ScriptRequest& rq, JS::HandleValue settings) = 0;
virtual IFramebuffer* GetCurrentBackbuffer() = 0;
virtual std::unique_ptr CreateCommandContext() = 0;
virtual std::unique_ptr CreateTexture(
const char* name, const ITexture::Type type, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) = 0;
virtual std::unique_ptr CreateTexture2D(
const char* name, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1) = 0;
virtual std::unique_ptr CreateFramebuffer(
const char* name, ITexture* colorAttachment,
ITexture* depthStencilAttachment) = 0;
virtual std::unique_ptr CreateFramebuffer(
const char* name, ITexture* colorAttachment,
ITexture* depthStencilAttachment, const CColor& clearColor) = 0;
virtual std::unique_ptr CreateBuffer(
const char* name, const IBuffer::Type type, const uint32_t size, const bool dynamic) = 0;
virtual std::unique_ptr CreateShaderProgram(
const CStr& name, const CShaderDefines& defines) = 0;
- virtual void AcquireNextBackbuffer() = 0;
+ virtual bool AcquireNextBackbuffer() = 0;
virtual void Present() = 0;
virtual bool IsTextureFormatSupported(const Format format) const = 0;
virtual bool IsFramebufferFormatSupported(const Format format) const = 0;
virtual const Capabilities& GetCapabilities() const = 0;
};
} // namespace Backend
} // namespace Renderer
#endif // INCLUDED_RENDERER_BACKEND_IDEVICE
Index: ps/trunk/source/renderer/backend/dummy/Device.cpp
===================================================================
--- ps/trunk/source/renderer/backend/dummy/Device.cpp (revision 27183)
+++ ps/trunk/source/renderer/backend/dummy/Device.cpp (revision 27184)
@@ -1,146 +1,147 @@
/* 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 "Device.h"
#include "renderer/backend/dummy/Buffer.h"
#include "renderer/backend/dummy/DeviceCommandContext.h"
#include "renderer/backend/dummy/Framebuffer.h"
#include "renderer/backend/dummy/ShaderProgram.h"
#include "renderer/backend/dummy/Texture.h"
#include "scriptinterface/JSON.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptRequest.h"
namespace Renderer
{
namespace Backend
{
namespace Dummy
{
CDevice::CDevice()
{
m_Name = "Dummy";
m_Version = "Unknown";
m_DriverInformation = "Unknown";
m_Extensions = {};
m_Backbuffer = CFramebuffer::Create(this);
m_Capabilities.S3TC = true;
m_Capabilities.ARBShaders = false;
m_Capabilities.ARBShadersShadow = false;
m_Capabilities.computeShaders = true;
m_Capabilities.debugLabels = true;
m_Capabilities.debugScopedLabels = true;
m_Capabilities.multisampling = true;
m_Capabilities.anisotropicFiltering = true;
m_Capabilities.maxSampleCount = 4u;
m_Capabilities.maxAnisotropy = 16.0f;
m_Capabilities.maxTextureSize = 8192u;
m_Capabilities.instancing = true;
}
CDevice::~CDevice() = default;
void CDevice::Report(const ScriptRequest& rq, JS::HandleValue settings)
{
Script::SetProperty(rq, settings, "name", "dummy");
}
std::unique_ptr CDevice::CreateCommandContext()
{
return CDeviceCommandContext::Create(this);
}
std::unique_ptr CDevice::CreateTexture(
const char* UNUSED(name), const CTexture::Type type, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& UNUSED(defaultSamplerDesc), const uint32_t MIPLevelCount, const uint32_t UNUSED(sampleCount))
{
return CTexture::Create(this, type, usage, format, width, height, MIPLevelCount);
}
std::unique_ptr CDevice::CreateTexture2D(
const char* name, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount)
{
return CreateTexture(name, ITexture::Type::TEXTURE_2D, usage,
format, width, height, defaultSamplerDesc, MIPLevelCount, sampleCount);
}
std::unique_ptr CDevice::CreateFramebuffer(
const char*, ITexture*, ITexture*)
{
return CFramebuffer::Create(this);
}
std::unique_ptr CDevice::CreateFramebuffer(
const char*, ITexture*, ITexture*, const CColor&)
{
return CFramebuffer::Create(this);
}
std::unique_ptr CDevice::CreateBuffer(
const char*, const CBuffer::Type type, const uint32_t size, const bool dynamic)
{
return CBuffer::Create(this, type, size, dynamic);
}
std::unique_ptr CDevice::CreateShaderProgram(
const CStr&, const CShaderDefines&)
{
return CShaderProgram::Create(this);
}
-void CDevice::AcquireNextBackbuffer()
+bool CDevice::AcquireNextBackbuffer()
{
// We have nothing to acquire.
+ return true;
}
void CDevice::Present()
{
// We have nothing to present.
}
bool CDevice::IsTextureFormatSupported(const Format UNUSED(format)) const
{
return true;
}
bool CDevice::IsFramebufferFormatSupported(const Format UNUSED(format)) const
{
return true;
}
std::unique_ptr CreateDevice(SDL_Window* UNUSED(window))
{
return std::make_unique();
}
} // namespace Dummy
} // namespace Backend
} // namespace Renderer
Index: ps/trunk/source/renderer/backend/dummy/Device.h
===================================================================
--- ps/trunk/source/renderer/backend/dummy/Device.h (revision 27183)
+++ ps/trunk/source/renderer/backend/dummy/Device.h (revision 27184)
@@ -1,107 +1,107 @@
/* 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_BACKEND_DUMMY_DEVICE
#define INCLUDED_RENDERER_BACKEND_DUMMY_DEVICE
#include "renderer/backend/dummy/DeviceForward.h"
#include "renderer/backend/IDevice.h"
class CShaderDefines;
namespace Renderer
{
namespace Backend
{
namespace Dummy
{
class CDeviceCommandContext;
class CDevice : public IDevice
{
public:
CDevice();
~CDevice() override;
Backend GetBackend() const override { return Backend::DUMMY; }
const std::string& GetName() const override { return m_Name; }
const std::string& GetVersion() const override { return m_Version; }
const std::string& GetDriverInformation() const override { return m_DriverInformation; }
const std::vector& GetExtensions() const override { return m_Extensions; }
void Report(const ScriptRequest& rq, JS::HandleValue settings) override;
IFramebuffer* GetCurrentBackbuffer() override { return m_Backbuffer.get(); }
std::unique_ptr CreateCommandContext() override;
std::unique_ptr CreateTexture(
const char* name, const ITexture::Type type, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) override;
std::unique_ptr CreateTexture2D(
const char* name, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1) override;
std::unique_ptr CreateFramebuffer(
const char* name, ITexture* colorAttachment,
ITexture* depthStencilAttachment) override;
std::unique_ptr CreateFramebuffer(
const char* name, ITexture* colorAttachment,
ITexture* depthStencilAttachment, const CColor& clearColor) override;
std::unique_ptr CreateBuffer(
const char* name, const IBuffer::Type type, const uint32_t size, const bool dynamic) override;
std::unique_ptr CreateShaderProgram(
const CStr& name, const CShaderDefines& defines) override;
- void AcquireNextBackbuffer() override;
+ bool AcquireNextBackbuffer() override;
void Present() override;
bool IsTextureFormatSupported(const Format format) const override;
bool IsFramebufferFormatSupported(const Format format) const override;
const Capabilities& GetCapabilities() const override { return m_Capabilities; }
protected:
std::string m_Name;
std::string m_Version;
std::string m_DriverInformation;
std::vector m_Extensions;
std::unique_ptr m_Backbuffer;
Capabilities m_Capabilities{};
};
} // namespace Dummy
} // namespace Backend
} // namespace Renderer
#endif // INCLUDED_RENDERER_BACKEND_DUMMY_DEVICE
Index: ps/trunk/source/renderer/backend/gl/Device.cpp
===================================================================
--- ps/trunk/source/renderer/backend/gl/Device.cpp (revision 27183)
+++ ps/trunk/source/renderer/backend/gl/Device.cpp (revision 27184)
@@ -1,1015 +1,1016 @@
/* 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 "Device.h"
#include "lib/external_libraries/libsdl.h"
#include "lib/ogl.h"
#include "ps/CLogger.h"
#include "ps/ConfigDB.h"
#include "ps/Profile.h"
#include "renderer/backend/gl/DeviceCommandContext.h"
#include "renderer/backend/gl/Texture.h"
#include "scriptinterface/JSON.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptRequest.h"
#if OS_WIN
#include "lib/sysdep/os/win/wgfx.h"
// We can't include wutil directly because GL headers conflict with Windows
// until we use a proper GL loader.
extern void* wutil_GetAppHDC();
#endif
#include
#include
#include
#if !CONFIG2_GLES && (defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND))
#if defined(SDL_VIDEO_DRIVER_X11)
#include
#endif
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
#include
#endif
#include
#endif // !CONFIG2_GLES && (defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND))
namespace Renderer
{
namespace Backend
{
namespace GL
{
namespace
{
std::string GetNameImpl()
{
// GL_VENDOR+GL_RENDERER are good enough here, so we don't use WMI to detect the cards.
// On top of that WMI can cause crashes with Nvidia Optimus and some netbooks
// see http://trac.wildfiregames.com/ticket/1952
// http://trac.wildfiregames.com/ticket/1575
char cardName[128];
const char* vendor = reinterpret_cast(glGetString(GL_VENDOR));
const char* renderer = reinterpret_cast(glGetString(GL_RENDERER));
// Happens if called before GL initialization.
if (!vendor || !renderer)
return {};
sprintf_s(cardName, std::size(cardName), "%s %s", vendor, renderer);
// Remove crap from vendor names. (don't dare touch the model name -
// it's too risky, there are too many different strings).
#define SHORTEN(what, charsToKeep) \
if (!strncmp(cardName, what, std::size(what) - 1)) \
memmove(cardName + charsToKeep, cardName + std::size(what) - 1, (strlen(cardName) - (std::size(what) - 1) + 1) * sizeof(char));
SHORTEN("ATI Technologies Inc.", 3);
SHORTEN("NVIDIA Corporation", 6);
SHORTEN("S3 Graphics", 2); // returned by EnumDisplayDevices
SHORTEN("S3 Graphics, Incorporated", 2); // returned by GL_VENDOR
#undef SHORTEN
return cardName;
}
std::string GetVersionImpl()
{
return reinterpret_cast(glGetString(GL_VERSION));
}
std::string GetDriverInformationImpl()
{
const std::string version = GetVersionImpl();
std::string driverInfo;
#if OS_WIN
driverInfo = CStrW(wgfx_DriverInfo()).ToUTF8();
if (driverInfo.empty())
#endif
{
if (!version.empty())
{
// Add "OpenGL" to differentiate this from the real driver version
// (returned by platform-specific detect routines).
driverInfo = std::string("OpenGL ") + version;
}
}
if (driverInfo.empty())
return version;
return version + " " + driverInfo;
}
std::vector GetExtensionsImpl()
{
std::vector extensions;
const std::string exts = ogl_ExtensionString();
boost::split(extensions, exts, boost::algorithm::is_space(), boost::token_compress_on);
std::sort(extensions.begin(), extensions.end());
return extensions;
}
void GLAD_API_PTR OnDebugMessage(
GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei UNUSED(length), const GLchar* message, const void* UNUSED(user_param))
{
std::string debugSource = "unknown";
std::string debugType = "unknown";
std::string debugSeverity = "unknown";
switch (source)
{
case GL_DEBUG_SOURCE_API:
debugSource = "the API";
break;
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
debugSource = "the window system";
break;
case GL_DEBUG_SOURCE_SHADER_COMPILER:
debugSource = "the shader compiler";
break;
case GL_DEBUG_SOURCE_THIRD_PARTY:
debugSource = "a third party";
break;
case GL_DEBUG_SOURCE_APPLICATION:
debugSource = "the application";
break;
case GL_DEBUG_SOURCE_OTHER:
debugSource = "somewhere";
break;
}
switch (type)
{
case GL_DEBUG_TYPE_ERROR:
debugType = "error";
break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
debugType = "deprecated behaviour";
break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
debugType = "undefined behaviour";
break;
case GL_DEBUG_TYPE_PORTABILITY:
debugType = "portability";
break;
case GL_DEBUG_TYPE_PERFORMANCE:
debugType = "performance";
break;
case GL_DEBUG_TYPE_OTHER:
debugType = "other";
break;
case GL_DEBUG_TYPE_MARKER:
debugType = "marker";
break;
case GL_DEBUG_TYPE_PUSH_GROUP:
debugType = "push group";
break;
case GL_DEBUG_TYPE_POP_GROUP:
debugType = "pop group";
break;
}
switch (severity)
{
case GL_DEBUG_SEVERITY_HIGH:
debugSeverity = "high";
break;
case GL_DEBUG_SEVERITY_MEDIUM:
debugSeverity = "medium";
break;
case GL_DEBUG_SEVERITY_LOW:
debugSeverity = "low";
break;
case GL_DEBUG_SEVERITY_NOTIFICATION:
debugSeverity = "notification";
break;
}
if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
{
debug_printf(
"OpenGL | %s: %s source: %s id %u: %s\n", debugSeverity.c_str(), debugType.c_str(), debugSource.c_str(), id, message);
}
else
{
LOGWARNING(
"OpenGL | %s: %s source: %s id %u: %s\n", debugSeverity.c_str(), debugType.c_str(), debugSource.c_str(), id, message);
}
}
} // anonymous namespace
// static
std::unique_ptr CDevice::Create(SDL_Window* window, const bool arb)
{
std::unique_ptr device(new CDevice());
if (window)
{
// According to https://wiki.libsdl.org/SDL_CreateWindow we don't need to
// call SDL_GL_LoadLibrary if we have a window with SDL_WINDOW_OPENGL,
// because it'll be called internally for the first created window.
device->m_Window = window;
device->m_Context = SDL_GL_CreateContext(device->m_Window);
if (!device->m_Context)
{
LOGERROR("SDL_GL_CreateContext failed: '%s'", SDL_GetError());
return nullptr;
}
#if OS_WIN
ogl_Init(SDL_GL_GetProcAddress, wutil_GetAppHDC());
#elif (defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND)) && !CONFIG2_GLES
SDL_SysWMinfo wminfo;
// The info structure must be initialized with the SDL version.
SDL_VERSION(&wminfo.version);
if (!SDL_GetWindowWMInfo(window, &wminfo))
{
LOGERROR("Failed to query SDL WM info: %s", SDL_GetError());
return nullptr;
}
switch (wminfo.subsystem)
{
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
case SDL_SYSWM_WAYLAND:
// TODO: maybe we need to load X11 functions
// dynamically as well.
ogl_Init(SDL_GL_GetProcAddress,
GetWaylandDisplay(device->m_Window),
static_cast(wminfo.subsystem));
break;
#endif
#if defined(SDL_VIDEO_DRIVER_X11)
case SDL_SYSWM_X11:
ogl_Init(SDL_GL_GetProcAddress,
GetX11Display(device->m_Window),
static_cast(wminfo.subsystem));
break;
#endif
default:
ogl_Init(SDL_GL_GetProcAddress, nullptr,
static_cast(wminfo.subsystem));
break;
}
#else
ogl_Init(SDL_GL_GetProcAddress);
#endif
}
else
{
#if OS_WIN
ogl_Init(SDL_GL_GetProcAddress, wutil_GetAppHDC());
#elif (defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND)) && !CONFIG2_GLES
bool initialized = false;
// Currently we don't have access to the backend type without
// the window. So we use hack to detect X11.
#if defined(SDL_VIDEO_DRIVER_X11)
Display* display = XOpenDisplay(NULL);
if (display)
{
ogl_Init(SDL_GL_GetProcAddress, display, static_cast(SDL_SYSWM_X11));
initialized = true;
}
#endif
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
if (!initialized)
{
// glad will find default EGLDisplay internally.
ogl_Init(SDL_GL_GetProcAddress, nullptr, static_cast(SDL_SYSWM_WAYLAND));
initialized = true;
}
#endif
if (!initialized)
{
LOGERROR("Can't initialize GL");
return nullptr;
}
#else
ogl_Init(SDL_GL_GetProcAddress);
#endif
#if OS_WIN || defined(SDL_VIDEO_DRIVER_X11) && !CONFIG2_GLES
// Hack to stop things looking very ugly when scrolling in Atlas.
ogl_SetVsyncEnabled(true);
#endif
}
// If we don't have GL2.0 then we don't have GLSL in core.
if (!arb && !ogl_HaveVersion(2, 0))
return nullptr;
if ((ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", nullptr) // ARB
&& !ogl_HaveVersion(2, 0)) // GLSL
|| !ogl_HaveExtension("GL_ARB_vertex_buffer_object") // VBO
|| ogl_HaveExtensions(0, "GL_ARB_multitexture", "GL_EXT_draw_range_elements", nullptr)
|| (!ogl_HaveExtension("GL_EXT_framebuffer_object") && !ogl_HaveExtension("GL_ARB_framebuffer_object")))
{
// It doesn't make sense to continue working here, because we're not
// able to display anything.
DEBUG_DISPLAY_FATAL_ERROR(
L"Your graphics card doesn't appear to be fully compatible with OpenGL shaders."
L" The game does not support pre-shader graphics cards."
L" You are advised to try installing newer drivers and/or upgrade your graphics card."
L" For more information, please see http://www.wildfiregames.com/forum/index.php?showtopic=16734"
);
}
device->m_ARB = arb;
device->m_Name = GetNameImpl();
device->m_Version = GetVersionImpl();
device->m_DriverInformation = GetDriverInformationImpl();
device->m_Extensions = GetExtensionsImpl();
// Set packing parameters for uploading and downloading data.
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glEnable(GL_TEXTURE_2D);
if (arb)
{
#if !CONFIG2_GLES
glEnable(GL_VERTEX_PROGRAM_ARB);
glEnable(GL_FRAGMENT_PROGRAM_ARB);
#endif
}
device->m_Backbuffer = CFramebuffer::CreateBackbuffer(device.get());
Capabilities& capabilities = device->m_Capabilities;
capabilities.ARBShaders = !ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", nullptr);
if (capabilities.ARBShaders)
capabilities.ARBShadersShadow = ogl_HaveExtension("GL_ARB_fragment_program_shadow");
capabilities.computeShaders = ogl_HaveVersion(4, 3) || ogl_HaveExtension("GL_ARB_compute_shader");
#if CONFIG2_GLES
// Some GLES implementations have GL_EXT_texture_compression_dxt1
// but that only supports DXT1 so we can't use it.
capabilities.S3TC = ogl_HaveExtensions(0, "GL_EXT_texture_compression_s3tc", nullptr) == 0;
#else
// Note: we don't bother checking for GL_S3_s3tc - it is incompatible
// and irrelevant (was never widespread).
capabilities.S3TC = ogl_HaveExtensions(0, "GL_ARB_texture_compression", "GL_EXT_texture_compression_s3tc", nullptr) == 0;
#endif
#if CONFIG2_GLES
capabilities.multisampling = false;
capabilities.maxSampleCount = 1;
#else
capabilities.multisampling =
ogl_HaveVersion(3, 3) &&
ogl_HaveExtension("GL_ARB_multisample") &&
ogl_HaveExtension("GL_ARB_texture_multisample");
if (capabilities.multisampling)
{
// By default GL_MULTISAMPLE should be enabled, but enable it for buggy drivers.
glEnable(GL_MULTISAMPLE);
GLint maxSamples = 1;
glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
capabilities.maxSampleCount = maxSamples;
}
#endif
capabilities.anisotropicFiltering = ogl_HaveExtension("GL_EXT_texture_filter_anisotropic");
if (capabilities.anisotropicFiltering)
{
GLfloat maxAnisotropy = 1.0f;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
capabilities.maxAnisotropy = maxAnisotropy;
}
GLint maxTextureSize = 1024;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
capabilities.maxTextureSize = maxTextureSize;
#if CONFIG2_GLES
const bool isDebugInCore = ogl_HaveVersion(3, 2);
#else
const bool isDebugInCore = ogl_HaveVersion(4, 3);
#endif
const bool hasDebug = isDebugInCore || ogl_HaveExtension("GL_KHR_debug");
if (hasDebug)
{
#ifdef NDEBUG
bool enableDebugMessages = false;
CFG_GET_VAL("renderer.backend.debugmessages", enableDebugMessages);
capabilities.debugLabels = false;
CFG_GET_VAL("renderer.backend.debuglabels", capabilities.debugLabels);
capabilities.debugScopedLabels = false;
CFG_GET_VAL("renderer.backend.debugscopedlabels", capabilities.debugScopedLabels);
#else
const bool enableDebugMessages = true;
capabilities.debugLabels = true;
capabilities.debugScopedLabels = true;
#endif
if (enableDebugMessages)
{
glEnable(GL_DEBUG_OUTPUT);
#if !CONFIG2_GLES
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
#else
#warning GLES without GL_DEBUG_OUTPUT_SYNCHRONOUS might call the callback from different threads which might be unsafe.
#endif
glDebugMessageCallback(OnDebugMessage, nullptr);
// Filter out our own debug group messages
const GLuint id = 0x0AD;
glDebugMessageControl(
GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PUSH_GROUP, GL_DONT_CARE, 1, &id, GL_FALSE);
glDebugMessageControl(
GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_POP_GROUP, GL_DONT_CARE, 1, &id, GL_FALSE);
}
}
#if CONFIG2_GLES
capabilities.instancing = false;
#else
capabilities.instancing =
!device->m_ARB &&
(ogl_HaveVersion(3, 3) ||
(ogl_HaveExtension("GL_ARB_draw_instanced") &&
ogl_HaveExtension("GL_ARB_instanced_arrays")));
#endif
return device;
}
CDevice::CDevice() = default;
CDevice::~CDevice()
{
if (m_Context)
SDL_GL_DeleteContext(m_Context);
}
void CDevice::Report(const ScriptRequest& rq, JS::HandleValue settings)
{
const char* errstr = "(error)";
Script::SetProperty(rq, settings, "name", m_ARB ? "glarb" : "gl");
#define INTEGER(id) do { \
GLint i = -1; \
glGetIntegerv(GL_##id, &i); \
if (ogl_SquelchError(GL_INVALID_ENUM)) \
Script::SetProperty(rq, settings, "GL_" #id, errstr); \
else \
Script::SetProperty(rq, settings, "GL_" #id, i); \
} while (false)
#define INTEGER2(id) do { \
GLint i[2] = { -1, -1 }; \
glGetIntegerv(GL_##id, i); \
if (ogl_SquelchError(GL_INVALID_ENUM)) { \
Script::SetProperty(rq, settings, "GL_" #id "[0]", errstr); \
Script::SetProperty(rq, settings, "GL_" #id "[1]", errstr); \
} else { \
Script::SetProperty(rq, settings, "GL_" #id "[0]", i[0]); \
Script::SetProperty(rq, settings, "GL_" #id "[1]", i[1]); \
} \
} while (false)
#define FLOAT(id) do { \
GLfloat f = std::numeric_limits::quiet_NaN(); \
glGetFloatv(GL_##id, &f); \
if (ogl_SquelchError(GL_INVALID_ENUM)) \
Script::SetProperty(rq, settings, "GL_" #id, errstr); \
else \
Script::SetProperty(rq, settings, "GL_" #id, f); \
} while (false)
#define FLOAT2(id) do { \
GLfloat f[2] = { std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN() }; \
glGetFloatv(GL_##id, f); \
if (ogl_SquelchError(GL_INVALID_ENUM)) { \
Script::SetProperty(rq, settings, "GL_" #id "[0]", errstr); \
Script::SetProperty(rq, settings, "GL_" #id "[1]", errstr); \
} else { \
Script::SetProperty(rq, settings, "GL_" #id "[0]", f[0]); \
Script::SetProperty(rq, settings, "GL_" #id "[1]", f[1]); \
} \
} while (false)
#define STRING(id) do { \
const char* c = (const char*)glGetString(GL_##id); \
if (!c) c = ""; \
if (ogl_SquelchError(GL_INVALID_ENUM)) c = errstr; \
Script::SetProperty(rq, settings, "GL_" #id, std::string(c)); \
} while (false)
#define QUERY(target, pname) do { \
GLint i = -1; \
glGetQueryivARB(GL_##target, GL_##pname, &i); \
if (ogl_SquelchError(GL_INVALID_ENUM)) \
Script::SetProperty(rq, settings, "GL_" #target ".GL_" #pname, errstr); \
else \
Script::SetProperty(rq, settings, "GL_" #target ".GL_" #pname, i); \
} while (false)
#define VERTEXPROGRAM(id) do { \
GLint i = -1; \
glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_##id, &i); \
if (ogl_SquelchError(GL_INVALID_ENUM)) \
Script::SetProperty(rq, settings, "GL_VERTEX_PROGRAM_ARB.GL_" #id, errstr); \
else \
Script::SetProperty(rq, settings, "GL_VERTEX_PROGRAM_ARB.GL_" #id, i); \
} while (false)
#define FRAGMENTPROGRAM(id) do { \
GLint i = -1; \
glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_##id, &i); \
if (ogl_SquelchError(GL_INVALID_ENUM)) \
Script::SetProperty(rq, settings, "GL_FRAGMENT_PROGRAM_ARB.GL_" #id, errstr); \
else \
Script::SetProperty(rq, settings, "GL_FRAGMENT_PROGRAM_ARB.GL_" #id, i); \
} while (false)
#define BOOL(id) INTEGER(id)
ogl_WarnIfError();
// Core OpenGL 1.3:
// (We don't bother checking extension strings for anything older than 1.3;
// it'll just produce harmless warnings)
STRING(VERSION);
STRING(VENDOR);
STRING(RENDERER);
STRING(EXTENSIONS);
#if !CONFIG2_GLES
INTEGER(MAX_CLIP_PLANES);
#endif
INTEGER(SUBPIXEL_BITS);
#if !CONFIG2_GLES
INTEGER(MAX_3D_TEXTURE_SIZE);
#endif
INTEGER(MAX_TEXTURE_SIZE);
INTEGER(MAX_CUBE_MAP_TEXTURE_SIZE);
INTEGER2(MAX_VIEWPORT_DIMS);
#if !CONFIG2_GLES
BOOL(RGBA_MODE);
BOOL(INDEX_MODE);
BOOL(DOUBLEBUFFER);
BOOL(STEREO);
#endif
FLOAT2(ALIASED_POINT_SIZE_RANGE);
FLOAT2(ALIASED_LINE_WIDTH_RANGE);
#if !CONFIG2_GLES
INTEGER(MAX_ELEMENTS_INDICES);
INTEGER(MAX_ELEMENTS_VERTICES);
INTEGER(MAX_TEXTURE_UNITS);
#endif
INTEGER(SAMPLE_BUFFERS);
INTEGER(SAMPLES);
// TODO: compressed texture formats
INTEGER(RED_BITS);
INTEGER(GREEN_BITS);
INTEGER(BLUE_BITS);
INTEGER(ALPHA_BITS);
#if !CONFIG2_GLES
INTEGER(INDEX_BITS);
#endif
INTEGER(DEPTH_BITS);
INTEGER(STENCIL_BITS);
#if !CONFIG2_GLES
// Core OpenGL 2.0 (treated as extensions):
if (ogl_HaveExtension("GL_EXT_texture_lod_bias"))
{
FLOAT(MAX_TEXTURE_LOD_BIAS_EXT);
}
if (ogl_HaveExtension("GL_ARB_occlusion_query"))
{
QUERY(SAMPLES_PASSED, QUERY_COUNTER_BITS);
}
if (ogl_HaveExtension("GL_ARB_shading_language_100"))
{
STRING(SHADING_LANGUAGE_VERSION_ARB);
}
if (ogl_HaveExtension("GL_ARB_vertex_shader"))
{
INTEGER(MAX_VERTEX_ATTRIBS_ARB);
INTEGER(MAX_VERTEX_UNIFORM_COMPONENTS_ARB);
INTEGER(MAX_VARYING_FLOATS_ARB);
INTEGER(MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB);
INTEGER(MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB);
}
if (ogl_HaveExtension("GL_ARB_fragment_shader"))
{
INTEGER(MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB);
}
if (ogl_HaveExtension("GL_ARB_vertex_shader") || ogl_HaveExtension("GL_ARB_fragment_shader") ||
ogl_HaveExtension("GL_ARB_vertex_program") || ogl_HaveExtension("GL_ARB_fragment_program"))
{
INTEGER(MAX_TEXTURE_IMAGE_UNITS_ARB);
INTEGER(MAX_TEXTURE_COORDS_ARB);
}
if (ogl_HaveExtension("GL_ARB_draw_buffers"))
{
INTEGER(MAX_DRAW_BUFFERS_ARB);
}
// Core OpenGL 3.0:
if (ogl_HaveExtension("GL_EXT_gpu_shader4"))
{
INTEGER(MIN_PROGRAM_TEXEL_OFFSET_EXT); // no _EXT version of these in glext.h
INTEGER(MAX_PROGRAM_TEXEL_OFFSET_EXT);
}
if (ogl_HaveExtension("GL_EXT_framebuffer_object"))
{
INTEGER(MAX_COLOR_ATTACHMENTS_EXT);
INTEGER(MAX_RENDERBUFFER_SIZE_EXT);
}
if (ogl_HaveExtension("GL_EXT_framebuffer_multisample"))
{
INTEGER(MAX_SAMPLES_EXT);
}
if (ogl_HaveExtension("GL_EXT_texture_array"))
{
INTEGER(MAX_ARRAY_TEXTURE_LAYERS_EXT);
}
if (ogl_HaveExtension("GL_EXT_transform_feedback"))
{
INTEGER(MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT);
INTEGER(MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT);
INTEGER(MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT);
}
// Other interesting extensions:
if (ogl_HaveExtension("GL_EXT_timer_query") || ogl_HaveExtension("GL_ARB_timer_query"))
{
QUERY(TIME_ELAPSED, QUERY_COUNTER_BITS);
}
if (ogl_HaveExtension("GL_ARB_timer_query"))
{
QUERY(TIMESTAMP, QUERY_COUNTER_BITS);
}
if (ogl_HaveExtension("GL_EXT_texture_filter_anisotropic"))
{
FLOAT(MAX_TEXTURE_MAX_ANISOTROPY_EXT);
}
if (ogl_HaveExtension("GL_ARB_texture_rectangle"))
{
INTEGER(MAX_RECTANGLE_TEXTURE_SIZE_ARB);
}
if (m_ARB)
{
if (ogl_HaveExtension("GL_ARB_vertex_program") || ogl_HaveExtension("GL_ARB_fragment_program"))
{
INTEGER(MAX_PROGRAM_MATRICES_ARB);
INTEGER(MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB);
}
if (ogl_HaveExtension("GL_ARB_vertex_program"))
{
VERTEXPROGRAM(MAX_PROGRAM_ENV_PARAMETERS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_LOCAL_PARAMETERS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_INSTRUCTIONS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_TEMPORARIES_ARB);
VERTEXPROGRAM(MAX_PROGRAM_PARAMETERS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_ATTRIBS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_ADDRESS_REGISTERS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_NATIVE_TEMPORARIES_ARB);
VERTEXPROGRAM(MAX_PROGRAM_NATIVE_PARAMETERS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_NATIVE_ATTRIBS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB);
if (ogl_HaveExtension("GL_ARB_fragment_program"))
{
// The spec seems to say these should be supported, but
// Mesa complains about them so let's not bother
/*
VERTEXPROGRAM(MAX_PROGRAM_ALU_INSTRUCTIONS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_TEX_INSTRUCTIONS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_TEX_INDIRECTIONS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB);
VERTEXPROGRAM(MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB);
*/
}
}
if (ogl_HaveExtension("GL_ARB_fragment_program"))
{
FRAGMENTPROGRAM(MAX_PROGRAM_ENV_PARAMETERS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_LOCAL_PARAMETERS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_INSTRUCTIONS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_ALU_INSTRUCTIONS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_TEX_INSTRUCTIONS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_TEX_INDIRECTIONS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_TEMPORARIES_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_PARAMETERS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_ATTRIBS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_TEMPORARIES_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_PARAMETERS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_ATTRIBS_ARB);
if (ogl_HaveExtension("GL_ARB_vertex_program"))
{
// The spec seems to say these should be supported, but
// Intel drivers on Windows complain about them so let's not bother
/*
FRAGMENTPROGRAM(MAX_PROGRAM_ADDRESS_REGISTERS_ARB);
FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB);
*/
}
}
}
if (ogl_HaveExtension("GL_ARB_geometry_shader4"))
{
INTEGER(MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB);
INTEGER(MAX_GEOMETRY_OUTPUT_VERTICES_ARB);
INTEGER(MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB);
INTEGER(MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB);
INTEGER(MAX_GEOMETRY_VARYING_COMPONENTS_ARB);
INTEGER(MAX_VERTEX_VARYING_COMPONENTS_ARB);
}
#else // CONFIG2_GLES
// Core OpenGL ES 2.0:
STRING(SHADING_LANGUAGE_VERSION);
INTEGER(MAX_VERTEX_ATTRIBS);
INTEGER(MAX_VERTEX_UNIFORM_VECTORS);
INTEGER(MAX_VARYING_VECTORS);
INTEGER(MAX_COMBINED_TEXTURE_IMAGE_UNITS);
INTEGER(MAX_VERTEX_TEXTURE_IMAGE_UNITS);
INTEGER(MAX_FRAGMENT_UNIFORM_VECTORS);
INTEGER(MAX_TEXTURE_IMAGE_UNITS);
INTEGER(MAX_RENDERBUFFER_SIZE);
#endif // CONFIG2_GLES
// TODO: Support OpenGL platforms which don't use GLX as well.
#if defined(SDL_VIDEO_DRIVER_X11) && !CONFIG2_GLES
#define GLXQCR_INTEGER(id) do { \
unsigned int i = UINT_MAX; \
if (glXQueryCurrentRendererIntegerMESA(id, &i)) \
Script::SetProperty(rq, settings, #id, i); \
} while (false)
#define GLXQCR_INTEGER2(id) do { \
unsigned int i[2] = { UINT_MAX, UINT_MAX }; \
if (glXQueryCurrentRendererIntegerMESA(id, i)) { \
Script::SetProperty(rq, settings, #id "[0]", i[0]); \
Script::SetProperty(rq, settings, #id "[1]", i[1]); \
} \
} while (false)
#define GLXQCR_INTEGER3(id) do { \
unsigned int i[3] = { UINT_MAX, UINT_MAX, UINT_MAX }; \
if (glXQueryCurrentRendererIntegerMESA(id, i)) { \
Script::SetProperty(rq, settings, #id "[0]", i[0]); \
Script::SetProperty(rq, settings, #id "[1]", i[1]); \
Script::SetProperty(rq, settings, #id "[2]", i[2]); \
} \
} while (false)
#define GLXQCR_STRING(id) do { \
const char* str = glXQueryCurrentRendererStringMESA(id); \
if (str) \
Script::SetProperty(rq, settings, #id ".string", str); \
} while (false)
SDL_SysWMinfo wminfo;
SDL_VERSION(&wminfo.version);
const int ret = SDL_GetWindowWMInfo(m_Window, &wminfo);
if (ret && wminfo.subsystem == SDL_SYSWM_X11)
{
Display* dpy = wminfo.info.x11.display;
int scrnum = DefaultScreen(dpy);
const char* glxexts = glXQueryExtensionsString(dpy, scrnum);
Script::SetProperty(rq, settings, "glx_extensions", glxexts);
if (strstr(glxexts, "GLX_MESA_query_renderer") && glXQueryCurrentRendererIntegerMESA && glXQueryCurrentRendererStringMESA)
{
GLXQCR_INTEGER(GLX_RENDERER_VENDOR_ID_MESA);
GLXQCR_INTEGER(GLX_RENDERER_DEVICE_ID_MESA);
GLXQCR_INTEGER3(GLX_RENDERER_VERSION_MESA);
GLXQCR_INTEGER(GLX_RENDERER_ACCELERATED_MESA);
GLXQCR_INTEGER(GLX_RENDERER_VIDEO_MEMORY_MESA);
GLXQCR_INTEGER(GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA);
GLXQCR_INTEGER(GLX_RENDERER_PREFERRED_PROFILE_MESA);
GLXQCR_INTEGER2(GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA);
GLXQCR_INTEGER2(GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA);
GLXQCR_INTEGER2(GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA);
GLXQCR_INTEGER2(GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA);
GLXQCR_STRING(GLX_RENDERER_VENDOR_ID_MESA);
GLXQCR_STRING(GLX_RENDERER_DEVICE_ID_MESA);
}
}
#endif // SDL_VIDEO_DRIVER_X11
}
std::unique_ptr CDevice::CreateCommandContext()
{
std::unique_ptr commandContet = CDeviceCommandContext::Create(this);
m_ActiveCommandContext = commandContet.get();
return commandContet;
}
std::unique_ptr CDevice::CreateTexture(
const char* name, const ITexture::Type type, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount)
{
return CTexture::Create(this, name, type, usage,
format, width, height, defaultSamplerDesc, MIPLevelCount, sampleCount);
}
std::unique_ptr CDevice::CreateTexture2D(
const char* name, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount)
{
return CreateTexture(name, CTexture::Type::TEXTURE_2D, usage,
format, width, height, defaultSamplerDesc, MIPLevelCount, sampleCount);
}
std::unique_ptr CDevice::CreateFramebuffer(
const char* name, ITexture* colorAttachment,
ITexture* depthStencilAttachment)
{
return CreateFramebuffer(name, colorAttachment, depthStencilAttachment, CColor(0.0f, 0.0f, 0.0f, 0.0f));
}
std::unique_ptr CDevice::CreateFramebuffer(
const char* name, ITexture* colorAttachment,
ITexture* depthStencilAttachment, const CColor& clearColor)
{
return CFramebuffer::Create(
this, name, colorAttachment->As(), depthStencilAttachment->As(), clearColor);
}
std::unique_ptr CDevice::CreateBuffer(
const char* name, const IBuffer::Type type, const uint32_t size, const bool dynamic)
{
return CBuffer::Create(this, name, type, size, dynamic);
}
std::unique_ptr CDevice::CreateShaderProgram(
const CStr& name, const CShaderDefines& defines)
{
return CShaderProgram::Create(this, name, defines);
}
-void CDevice::AcquireNextBackbuffer()
+bool CDevice::AcquireNextBackbuffer()
{
ENSURE(!m_BackbufferAcquired);
m_BackbufferAcquired = true;
+ return true;
}
void CDevice::Present()
{
ENSURE(m_BackbufferAcquired);
m_BackbufferAcquired = false;
if (m_Window)
{
PROFILE3("swap buffers");
SDL_GL_SwapWindow(m_Window);
ogl_WarnIfError();
}
bool checkGLErrorAfterSwap = false;
CFG_GET_VAL("gl.checkerrorafterswap", checkGLErrorAfterSwap);
#if defined(NDEBUG)
if (!checkGLErrorAfterSwap)
return;
#endif
PROFILE3("error check");
// We have to check GL errors after SwapBuffer to avoid possible
// synchronizations during rendering.
if (GLenum err = glGetError())
ONCE(LOGERROR("GL error %s (0x%04x) occurred", ogl_GetErrorName(err), err));
}
bool CDevice::IsTextureFormatSupported(const Format format) const
{
bool supported = false;
switch (format)
{
case Format::UNDEFINED:
break;
case Format::R8G8B8_UNORM: FALLTHROUGH;
case Format::R8G8B8A8_UNORM: FALLTHROUGH;
case Format::A8_UNORM: FALLTHROUGH;
case Format::L8_UNORM:
supported = true;
break;
case Format::R32_SFLOAT: FALLTHROUGH;
case Format::R32G32_SFLOAT: FALLTHROUGH;
case Format::R32G32B32_SFLOAT: FALLTHROUGH;
case Format::R32G32B32A32_SFLOAT:
break;
case Format::D16: FALLTHROUGH;
case Format::D24: FALLTHROUGH;
case Format::D32:
supported = true;
break;
case Format::D24_S8:
#if !CONFIG2_GLES
supported = true;
#endif
break;
case Format::BC1_RGB_UNORM: FALLTHROUGH;
case Format::BC1_RGBA_UNORM: FALLTHROUGH;
case Format::BC2_UNORM: FALLTHROUGH;
case Format::BC3_UNORM:
supported = m_Capabilities.S3TC;
break;
default:
break;
}
return supported;
}
bool CDevice::IsFramebufferFormatSupported(const Format format) const
{
bool supported = false;
switch (format)
{
case Format::UNDEFINED:
break;
#if !CONFIG2_GLES
case Format::R8_UNORM:
supported = ogl_HaveVersion(3, 0);
break;
#endif
case Format::R8G8B8A8_UNORM:
supported = true;
break;
default:
break;
}
return supported;
}
std::unique_ptr CreateDevice(SDL_Window* window, const bool arb)
{
return GL::CDevice::Create(window, arb);
}
} // namespace GL
} // namespace Backend
} // namespace Renderer
Index: ps/trunk/source/renderer/backend/gl/Device.h
===================================================================
--- ps/trunk/source/renderer/backend/gl/Device.h (revision 27183)
+++ ps/trunk/source/renderer/backend/gl/Device.h (revision 27184)
@@ -1,136 +1,136 @@
/* 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_BACKEND_GL_DEVICE
#define INCLUDED_RENDERER_BACKEND_GL_DEVICE
#include "renderer/backend/Format.h"
#include "renderer/backend/gl/Buffer.h"
#include "renderer/backend/gl/DeviceForward.h"
#include "renderer/backend/gl/Framebuffer.h"
#include "renderer/backend/gl/ShaderProgram.h"
#include "renderer/backend/gl/Texture.h"
#include "renderer/backend/IDevice.h"
#include "scriptinterface/ScriptForward.h"
#include
#include
#include
typedef struct SDL_Window SDL_Window;
typedef void* SDL_GLContext;
namespace Renderer
{
namespace Backend
{
namespace GL
{
class CDeviceCommandContext;
class CDevice final : public IDevice
{
public:
~CDevice() override;
/**
* Creates the GL device and the GL context for the window if it presents.
*/
static std::unique_ptr Create(SDL_Window* window, const bool arb);
Backend GetBackend() const override { return m_ARB ? Backend::GL_ARB : Backend::GL; }
const std::string& GetName() const override { return m_Name; }
const std::string& GetVersion() const override { return m_Version; }
const std::string& GetDriverInformation() const override { return m_DriverInformation; }
const std::vector& GetExtensions() const override { return m_Extensions; }
void Report(const ScriptRequest& rq, JS::HandleValue settings) override;
IFramebuffer* GetCurrentBackbuffer() override { return m_Backbuffer.get(); }
std::unique_ptr CreateCommandContext() override;
CDeviceCommandContext* GetActiveCommandContext() { return m_ActiveCommandContext; }
std::unique_ptr CreateTexture(
const char* name, const ITexture::Type type, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) override;
std::unique_ptr CreateTexture2D(
const char* name, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1) override;
std::unique_ptr CreateFramebuffer(
const char* name, ITexture* colorAttachment,
ITexture* depthStencilAttachment) override;
std::unique_ptr CreateFramebuffer(
const char* name, ITexture* colorAttachment,
ITexture* depthStencilAttachment, const CColor& clearColor) override;
std::unique_ptr CreateBuffer(
const char* name, const IBuffer::Type type, const uint32_t size, const bool dynamic) override;
std::unique_ptr CreateShaderProgram(
const CStr& name, const CShaderDefines& defines) override;
- void AcquireNextBackbuffer() override;
+ bool AcquireNextBackbuffer() override;
void Present() override;
bool IsTextureFormatSupported(const Format format) const override;
bool IsFramebufferFormatSupported(const Format format) const override;
const Capabilities& GetCapabilities() const override { return m_Capabilities; }
private:
CDevice();
SDL_Window* m_Window = nullptr;
SDL_GLContext m_Context = nullptr;
bool m_ARB = false;
std::string m_Name;
std::string m_Version;
std::string m_DriverInformation;
std::vector m_Extensions;
// GL can have the only one command context at once.
// TODO: remove as soon as we have no GL code outside backend, currently
// it's used only as a helper for transition.
CDeviceCommandContext* m_ActiveCommandContext = nullptr;
std::unique_ptr m_Backbuffer;
bool m_BackbufferAcquired = false;
Capabilities m_Capabilities{};
};
} // namespace GL
} // namespace Backend
} // namespace Renderer
#endif // INCLUDED_RENDERER_BACKEND_GL_DEVICE
Index: ps/trunk/source/renderer/backend/vulkan/Device.cpp
===================================================================
--- ps/trunk/source/renderer/backend/vulkan/Device.cpp (revision 27183)
+++ ps/trunk/source/renderer/backend/vulkan/Device.cpp (revision 27184)
@@ -1,182 +1,183 @@
/* 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 "Device.h"
#include "lib/external_libraries/libsdl.h"
#include "scriptinterface/JSON.h"
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptRequest.h"
#if SDL_VERSION_ATLEAST(2, 0, 8)
#include
#endif
namespace Renderer
{
namespace Backend
{
namespace Vulkan
{
// static
std::unique_ptr CDevice::Create(SDL_Window* UNUSED(window))
{
std::unique_ptr device(new CDevice());
return device;
}
CDevice::CDevice() = default;
CDevice::~CDevice() = default;
void CDevice::Report(const ScriptRequest& rq, JS::HandleValue settings)
{
Script::SetProperty(rq, settings, "name", "vulkan");
std::string vulkanSupport = "unsupported";
// According to http://wiki.libsdl.org/SDL_Vulkan_LoadLibrary the following
// functionality is supported since SDL 2.0.8.
#if SDL_VERSION_ATLEAST(2, 0, 8)
if (!SDL_Vulkan_LoadLibrary(nullptr))
{
void* vkGetInstanceProcAddr = SDL_Vulkan_GetVkGetInstanceProcAddr();
if (vkGetInstanceProcAddr)
vulkanSupport = "supported";
else
vulkanSupport = "noprocaddr";
SDL_Vulkan_UnloadLibrary();
}
else
{
vulkanSupport = "cantload";
}
#endif
Script::SetProperty(rq, settings, "status", vulkanSupport);
}
IFramebuffer* CDevice::GetCurrentBackbuffer()
{
return nullptr;
}
std::unique_ptr CDevice::CreateCommandContext()
{
return nullptr;
}
std::unique_ptr CDevice::CreateTexture(
const char* name, const ITexture::Type type, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount)
{
UNUSED2(name);
UNUSED2(type);
UNUSED2(usage);
UNUSED2(format);
UNUSED2(width);
UNUSED2(height);
UNUSED2(defaultSamplerDesc);
UNUSED2(MIPLevelCount);
UNUSED2(sampleCount);
return nullptr;
}
std::unique_ptr CDevice::CreateTexture2D(
const char* name, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount)
{
return CreateTexture(
name, ITexture::Type::TEXTURE_2D, usage, format,
width, height, defaultSamplerDesc, MIPLevelCount, sampleCount);
}
std::unique_ptr CDevice::CreateFramebuffer(
const char* name, ITexture* colorAttachment,
ITexture* depthStencilAttachment)
{
UNUSED2(name);
UNUSED2(colorAttachment);
UNUSED2(depthStencilAttachment);
return nullptr;
}
std::unique_ptr CDevice::CreateFramebuffer(
const char* name, ITexture* colorAttachment,
ITexture* depthStencilAttachment, const CColor& clearColor)
{
UNUSED2(name);
UNUSED2(colorAttachment);
UNUSED2(depthStencilAttachment);
UNUSED2(clearColor);
return nullptr;
}
std::unique_ptr CDevice::CreateBuffer(
const char* name, const IBuffer::Type type, const uint32_t size, const bool dynamic)
{
UNUSED2(name);
UNUSED2(type);
UNUSED2(size);
UNUSED2(dynamic);
return nullptr;
}
std::unique_ptr CDevice::CreateShaderProgram(
const CStr& name, const CShaderDefines& defines)
{
UNUSED2(name);
UNUSED2(defines);
return nullptr;
}
-void CDevice::AcquireNextBackbuffer()
+bool CDevice::AcquireNextBackbuffer()
{
+ return false;
}
void CDevice::Present()
{
}
bool CDevice::IsTextureFormatSupported(const Format format) const
{
UNUSED2(format);
return false;
}
bool CDevice::IsFramebufferFormatSupported(const Format format) const
{
UNUSED2(format);
return false;
}
std::unique_ptr CreateDevice(SDL_Window* window)
{
return Vulkan::CDevice::Create(window);
}
} // namespace Vulkan
} // namespace Backend
} // namespace Renderer
Index: ps/trunk/source/renderer/backend/vulkan/Device.h
===================================================================
--- ps/trunk/source/renderer/backend/vulkan/Device.h (revision 27183)
+++ ps/trunk/source/renderer/backend/vulkan/Device.h (revision 27184)
@@ -1,111 +1,111 @@
/* 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_BACKEND_VULKAN_DEVICE
#define INCLUDED_RENDERER_BACKEND_VULKAN_DEVICE
#include "renderer/backend/IDevice.h"
#include "renderer/backend/vulkan/DeviceForward.h"
#include "scriptinterface/ScriptForward.h"
#include
typedef struct SDL_Window SDL_Window;
namespace Renderer
{
namespace Backend
{
namespace Vulkan
{
class CDevice : public IDevice
{
public:
/**
* Creates the Vulkan device.
*/
static std::unique_ptr Create(SDL_Window* window);
~CDevice() override;
Backend GetBackend() const override { return Backend::VULKAN; }
const std::string& GetName() const override { return m_Name; }
const std::string& GetVersion() const override { return m_Version; }
const std::string& GetDriverInformation() const override { return m_DriverInformation; }
const std::vector& GetExtensions() const override { return m_Extensions; }
void Report(const ScriptRequest& rq, JS::HandleValue settings) override;
IFramebuffer* GetCurrentBackbuffer() override;
std::unique_ptr CreateCommandContext() override;
std::unique_ptr CreateTexture(
const char* name, const ITexture::Type type, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) override;
std::unique_ptr CreateTexture2D(
const char* name, const uint32_t usage,
const Format format, const uint32_t width, const uint32_t height,
const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1) override;
std::unique_ptr CreateFramebuffer(
const char* name, ITexture* colorAttachment,
ITexture* depthStencilAttachment) override;
std::unique_ptr CreateFramebuffer(
const char* name, ITexture* colorAttachment,
ITexture* depthStencilAttachment, const CColor& clearColor) override;
std::unique_ptr CreateBuffer(
const char* name, const IBuffer::Type type, const uint32_t size, const bool dynamic) override;
std::unique_ptr CreateShaderProgram(
const CStr& name, const CShaderDefines& defines) override;
- void AcquireNextBackbuffer() override;
+ bool AcquireNextBackbuffer() override;
void Present() override;
bool IsTextureFormatSupported(const Format format) const override;
bool IsFramebufferFormatSupported(const Format format) const override;
const Capabilities& GetCapabilities() const override { return m_Capabilities; }
private:
CDevice();
std::string m_Name;
std::string m_Version;
std::string m_DriverInformation;
std::vector m_Extensions;
Capabilities m_Capabilities{};
};
} // namespace Vulkan
} // namespace Backend
} // namespace Renderer
#endif // INCLUDED_RENDERER_BACKEND_VULKAN_DEVICE
Index: ps/trunk/source/tools/atlas/GameInterface/View.cpp
===================================================================
--- ps/trunk/source/tools/atlas/GameInterface/View.cpp (revision 27183)
+++ ps/trunk/source/tools/atlas/GameInterface/View.cpp (revision 27184)
@@ -1,463 +1,464 @@
/* 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 "View.h"
#include "ActorViewer.h"
#include "GameLoop.h"
#include "Messages.h"
#include "SimState.h"
#include "graphics/Canvas2D.h"
#include "graphics/CinemaManager.h"
#include "graphics/GameView.h"
#include "graphics/ParticleManager.h"
#include "graphics/UnitManager.h"
#include "lib/timer.h"
#include "lib/utf8.h"
#include "maths/MathUtil.h"
#include "ps/Game.h"
#include "ps/GameSetup/GameSetup.h"
#include "ps/VideoMode.h"
#include "ps/World.h"
#include "renderer/backend/IDevice.h"
#include "renderer/DebugRenderer.h"
#include "renderer/Renderer.h"
#include "renderer/SceneRenderer.h"
#include "simulation2/components/ICmpObstructionManager.h"
#include "simulation2/components/ICmpParticleManager.h"
#include "simulation2/components/ICmpPathfinder.h"
#include "simulation2/Simulation2.h"
#include "soundmanager/ISoundManager.h"
extern void (*Atlas_GLSwapBuffers)(void* context);
extern int g_xres, g_yres;
//////////////////////////////////////////////////////////////////////////
void AtlasView::SetParam(const std::wstring& UNUSED(name), bool UNUSED(value))
{
}
void AtlasView::SetParam(const std::wstring& UNUSED(name), const AtlasMessage::Color& UNUSED(value))
{
}
void AtlasView::SetParam(const std::wstring& UNUSED(name), const std::wstring& UNUSED(value))
{
}
void AtlasView::SetParam(const std::wstring& UNUSED(name), int UNUSED(value))
{
}
//////////////////////////////////////////////////////////////////////////
AtlasViewActor::AtlasViewActor()
: m_SpeedMultiplier(1.f), m_ActorViewer(new ActorViewer())
{
}
AtlasViewActor::~AtlasViewActor()
{
delete m_ActorViewer;
}
void AtlasViewActor::Update(float realFrameLength)
{
m_ActorViewer->Update(realFrameLength * m_SpeedMultiplier, realFrameLength);
}
void AtlasViewActor::Render()
{
SViewPort vp = { 0, 0, g_xres, g_yres };
CCamera& camera = GetCamera();
camera.SetViewPort(vp);
camera.SetPerspectiveProjection(2.f, 512.f, DEGTORAD(20.f));
camera.UpdateFrustum();
m_ActorViewer->Render();
Atlas_GLSwapBuffers((void*)g_AtlasGameLoop->glCanvas);
}
CCamera& AtlasViewActor::GetCamera()
{
return m_Camera;
}
CSimulation2* AtlasViewActor::GetSimulation2()
{
return m_ActorViewer->GetSimulation2();
}
entity_id_t AtlasViewActor::GetEntityId(AtlasMessage::ObjectID UNUSED(obj))
{
return m_ActorViewer->GetEntity();
}
bool AtlasViewActor::WantsHighFramerate()
{
if (m_SpeedMultiplier != 0.f)
return true;
return false;
}
void AtlasViewActor::SetEnabled(bool enabled)
{
m_ActorViewer->SetEnabled(enabled);
}
void AtlasViewActor::SetSpeedMultiplier(float speedMultiplier)
{
m_SpeedMultiplier = speedMultiplier;
}
ActorViewer& AtlasViewActor::GetActorViewer()
{
return *m_ActorViewer;
}
void AtlasViewActor::SetParam(const std::wstring& name, bool value)
{
if (name == L"wireframe")
g_Renderer.GetSceneRenderer().SetModelRenderMode(value ? WIREFRAME : SOLID);
else if (name == L"walk")
m_ActorViewer->SetWalkEnabled(value);
else if (name == L"ground")
m_ActorViewer->SetGroundEnabled(value);
// TODO: this causes corruption of WaterManager's global state
// which should be asociated with terrain or simulation instead
// see http://trac.wildfiregames.com/ticket/2692
//else if (name == L"water")
//m_ActorViewer->SetWaterEnabled(value);
else if (name == L"shadows")
m_ActorViewer->ToggleShadows();
else if (name == L"stats")
m_ActorViewer->SetStatsEnabled(value);
else if (name == L"bounding_box")
m_ActorViewer->SetBoundingBoxesEnabled(value);
else if (name == L"axes_marker")
m_ActorViewer->SetAxesMarkerEnabled(value);
}
void AtlasViewActor::SetParam(const std::wstring& name, int value)
{
if (name == L"prop_points")
m_ActorViewer->SetPropPointsMode(value);
}
void AtlasViewActor::SetParam(const std::wstring& UNUSED(name), const AtlasMessage::Color& UNUSED(value))
{
}
//////////////////////////////////////////////////////////////////////////
AtlasViewGame::AtlasViewGame()
: m_SpeedMultiplier(0.f), m_IsTesting(false), m_DrawMoveTool(false)
{
ENSURE(g_Game);
}
AtlasViewGame::~AtlasViewGame()
{
for (const std::pair& p : m_SavedStates)
delete p.second;
}
CSimulation2* AtlasViewGame::GetSimulation2()
{
return g_Game->GetSimulation2();
}
void AtlasViewGame::Update(float realFrameLength)
{
const float actualFrameLength = realFrameLength * m_SpeedMultiplier;
// Clean up any entities destroyed during UI message processing
g_Game->GetSimulation2()->FlushDestroyedEntities();
if (m_SpeedMultiplier == 0.f)
{
// Update unit interpolation
g_Game->Interpolate(0.0, realFrameLength);
}
else
{
// Update the whole world
// (Tell the game update not to interpolate graphics - we'll do that
// ourselves)
g_Game->Update(actualFrameLength, false);
// Interpolate the graphics - we only want to do this once per visual frame,
// not in every call to g_Game->Update
g_Game->Interpolate(actualFrameLength, realFrameLength);
}
// Run sound idle tasks every frame.
if (g_SoundManager)
g_SoundManager->IdleTask();
// Cinematic motion should be independent of simulation update, so we can
// preview the cinematics by themselves
g_Game->GetView()->GetCinema()->Update(realFrameLength);
}
void AtlasViewGame::Render()
{
- g_VideoMode.GetBackendDevice()->AcquireNextBackbuffer();
+ if (!g_VideoMode.GetBackendDevice()->AcquireNextBackbuffer())
+ return;
SViewPort vp = { 0, 0, g_xres, g_yres };
CCamera& camera = GetCamera();
camera.SetViewPort(vp);
camera.SetProjectionFromCamera(*g_Game->GetView()->GetCamera());
camera.UpdateFrustum();
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.
g_VideoMode.GetBackendDevice()->Present();
}
void AtlasViewGame::DrawCinemaPathTool()
{
if (!m_DrawMoveTool)
return;
const CVector3D focus = m_MoveTool;
const CVector3D camera = GetCamera().GetOrientation().GetTranslation();
const float scale = (focus - camera).Length();
const float axisLength = scale / 10.0f;
const float lineWidth = scale / 1e3f;
g_Renderer.GetDebugRenderer().DrawLine(
focus, focus + CVector3D(axisLength, 0, 0),
CColor(1.0f, 0.0f, 0.0f, 1.0f), lineWidth, false);
g_Renderer.GetDebugRenderer().DrawLine(
focus, focus + CVector3D(0, axisLength, 0),
CColor(0.0f, 1.0f, 0.0f, 1.0f), lineWidth, false);
g_Renderer.GetDebugRenderer().DrawLine(
focus, focus + CVector3D(0, 0, axisLength),
CColor(0.0f, 0.0f, 1.0f, 1.0f), lineWidth, false);
}
void AtlasViewGame::DrawOverlays(CCanvas2D& canvas)
{
if (m_Bandbox.left >= m_Bandbox.right || m_Bandbox.top >= m_Bandbox.bottom)
return;
const std::vector outerPoints = {
m_Bandbox.TopLeft() + CVector2D(-1.0f, -1.0f),
m_Bandbox.TopRight() + CVector2D(1.0f, -1.0f),
m_Bandbox.BottomRight() + CVector2D(1.0f, 1.0f),
m_Bandbox.BottomLeft() + CVector2D(-1.0f, 1.0f),
m_Bandbox.TopLeft() + CVector2D(-1.0f, -1.0f)
};
canvas.DrawLine(outerPoints, 1.5f, CColor(0.0f, 0.0f, 0.0f, 1.0f));
const std::vector innerPoints = {
m_Bandbox.TopLeft(),
m_Bandbox.TopRight(),
m_Bandbox.BottomRight(),
m_Bandbox.BottomLeft(),
m_Bandbox.TopLeft()
};
canvas.DrawLine(innerPoints, 1.5f, CColor(1.0f, 1.0f, 1.0f, 1.0f));
}
void AtlasViewGame::SetParam(const std::wstring& name, bool value)
{
if (name == L"priorities")
g_Renderer.GetSceneRenderer().SetDisplayTerrainPriorities(value);
else if (name == L"movetool")
m_DrawMoveTool = value;
}
void AtlasViewGame::SetParam(const std::wstring& name, float value)
{
if (name == L"movetool_x")
m_MoveTool.X = value;
else if (name == L"movetool_y")
m_MoveTool.Y = value;
else if (name == L"movetool_z")
m_MoveTool.Z = value;
}
void AtlasViewGame::SetParam(const std::wstring& name, const std::wstring& value)
{
if (name == L"passability")
{
m_DisplayPassability = CStrW(value).ToUTF8();
CmpPtr cmpPathfinder(*GetSimulation2(), SYSTEM_ENTITY);
if (cmpPathfinder)
{
if (!value.empty())
cmpPathfinder->SetAtlasOverlay(true, cmpPathfinder->GetPassabilityClass(m_DisplayPassability));
else
cmpPathfinder->SetAtlasOverlay(false);
}
}
}
CCamera& AtlasViewGame::GetCamera()
{
return *g_Game->GetView()->GetCamera();
}
bool AtlasViewGame::WantsHighFramerate()
{
if (g_Game->GetView()->GetCinema()->IsPlaying())
return true;
if (m_SpeedMultiplier != 0.f)
return true;
return false;
}
void AtlasViewGame::SetSpeedMultiplier(float speed)
{
m_SpeedMultiplier = speed;
}
void AtlasViewGame::SetTesting(bool testing)
{
m_IsTesting = testing;
// If we're testing, particles should freeze on pause (like in-game), otherwise they keep going
CmpPtr cmpParticleManager(*GetSimulation2(), SYSTEM_ENTITY);
if (cmpParticleManager)
cmpParticleManager->SetUseSimTime(m_IsTesting);
}
void AtlasViewGame::SaveState(const std::wstring& label)
{
delete m_SavedStates[label]; // in case it already exists
m_SavedStates[label] = SimState::Freeze();
}
void AtlasViewGame::RestoreState(const std::wstring& label)
{
SimState* simState = m_SavedStates[label];
if (! simState)
return;
simState->Thaw();
}
std::wstring AtlasViewGame::DumpState(bool binary)
{
std::stringstream stream;
if (binary)
{
if (! g_Game->GetSimulation2()->SerializeState(stream))
return L"(internal error)";
// We can't return raw binary data, because we want to handle it with wxJS which
// doesn't like \0 bytes in strings, so return it as hex
static const char digits[] = "0123456789abcdef";
std::string str = stream.str();
std::wstring ret;
ret.reserve(str.length()*3);
for (size_t i = 0; i < str.length(); ++i)
{
ret += digits[(unsigned char)str[i] >> 4];
ret += digits[(unsigned char)str[i] & 0x0f];
ret += ' ';
}
return ret;
}
else
{
if (! g_Game->GetSimulation2()->DumpDebugState(stream))
return L"(internal error)";
return wstring_from_utf8(stream.str());
}
}
void AtlasViewGame::SetBandbox(bool visible, float x0, float y0, float x1, float y1)
{
if (visible)
{
// Make sure corners are arranged in correct order
if (x0 > x1)
std::swap(x0, x1);
if (y0 > y1)
std::swap(y0, y1);
m_Bandbox = CRect(x0, y0, x1, y1);
}
else
{
m_Bandbox = CRect{};
}
}
//////////////////////////////////////////////////////////////////////////
AtlasViewNone* view_None = NULL;
AtlasViewGame* view_Game = NULL;
AtlasViewActor* view_Actor = NULL;
AtlasView::~AtlasView()
{
}
AtlasView* AtlasView::GetView(int /*eRenderView*/ view)
{
switch (view)
{
case AtlasMessage::eRenderView::NONE: return AtlasView::GetView_None();
case AtlasMessage::eRenderView::GAME: return AtlasView::GetView_Game();
case AtlasMessage::eRenderView::ACTOR: return AtlasView::GetView_Actor();
default:
debug_warn(L"Invalid view type");
return AtlasView::GetView_None();
}
}
AtlasView* AtlasView::GetView_None()
{
if (! view_None)
view_None = new AtlasViewNone();
return view_None;
}
AtlasViewGame* AtlasView::GetView_Game()
{
if (! view_Game)
view_Game = new AtlasViewGame();
return view_Game;
}
AtlasViewActor* AtlasView::GetView_Actor()
{
if (! view_Actor)
view_Actor = new AtlasViewActor();
return view_Actor;
}
void AtlasView::DestroyViews()
{
delete view_None; view_None = NULL;
delete view_Game; view_Game = NULL;
delete view_Actor; view_Actor = NULL;
}