Skip to content

Commit 15c9cc1

Browse files
committed
Re-show previous frame if nothing has changed
We can detect if nothing has changed from the previous frame by computing a cheap hash of the draw command stream and if it matches we reuse the previous contents of the render target we prepare anyway for resolution scaling.
1 parent 8dfdcf1 commit 15c9cc1

5 files changed

Lines changed: 118 additions & 15 deletions

File tree

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ find_package(fmt CONFIG REQUIRED)
104104
find_package(glfw3 CONFIG REQUIRED)
105105
find_package(LuaJit REQUIRED)
106106
find_package(re2 CONFIG REQUIRED)
107+
find_package(unofficial-sodium CONFIG REQUIRED)
107108
find_package(Threads REQUIRED)
108109
find_package(ZLIB REQUIRED)
109110

@@ -182,6 +183,7 @@ target_link_libraries(SimpleGraphic
182183
imgui
183184
${LUAJIT_LIBRARIES}
184185
re2::re2
186+
unofficial-sodium::sodium
185187
Threads::Threads
186188
ZLIB::ZLIB
187189
)

engine/render/r_main.cpp

Lines changed: 104 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
#define GLAD_GLES2_IMPLEMENTATION
88
#include "r_local.h"
99

10+
#include <array>
1011
#include <fmt/chrono.h>
1112
#include <map>
1213
#include <numeric>
1314
#include <random>
15+
#include <sodium.h>
1416
#include <sstream>
1517
#include <vector>
1618

@@ -424,9 +426,6 @@ void r_layer_c::Render()
424426
bool showStats{};
425427
if (renderer->debugLayers) {
426428
if (ImGui::Begin("Layers", &renderer->debugLayers)) {
427-
GLint maxTextureUnits{};
428-
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
429-
ImGui::Text("Max texture units: %d", maxTextureUnits);
430429
ImGui::BulletText("Layer %d:%d - %d batches", layer, subLayer, numBatches);
431430
std::string heading = fmt::format("Layer {}:{}", layer, subLayer);
432431
showStats = ImGui::CollapsingHeader(heading.c_str(), ImGuiTreeNodeFlags_DefaultOpen);
@@ -509,6 +508,19 @@ void r_layer_c::Render()
509508
}
510509
}
511510

511+
void r_layer_c::Discard()
512+
{
513+
for (int i = 0; i < numCmd; i++) {
514+
r_layerCmd_s* cmd = cmdList[i];
515+
if (renderer->layerCmdBinCount == renderer->layerCmdBinSize) {
516+
renderer->layerCmdBinSize <<= 1;
517+
trealloc(renderer->layerCmdBin, renderer->layerCmdBinSize);
518+
}
519+
renderer->layerCmdBin[renderer->layerCmdBinCount++] = cmd;
520+
}
521+
numCmd = 0;
522+
}
523+
512524
// =====================
513525
// r_IRenderer Interface
514526
// =====================
@@ -531,6 +543,7 @@ r_renderer_c::r_renderer_c(sys_IMain* sysHnd)
531543
r_layerDebug = sys->con->Cvar_Add("r_layerDebug", CV_ARCHIVE, "0");
532544
r_layerOptimize = sys->con->Cvar_Add("r_layerOptimize", CV_ARCHIVE | CV_CLAMP, "0", 0, 2);
533545
r_layerShuffle = sys->con->Cvar_Add("r_layerShuffle", CV_ARCHIVE | CV_CLAMP, "0", 0, 1);
546+
r_elideFrames = sys->con->Cvar_Add("r_elideFrames", CV_ARCHIVE | CV_CLAMP, "1", 0, 1);
534547

535548
Cmd_Add("screenshot", 0, "[<format>]", this, &r_renderer_c::C_Screenshot);
536549
}
@@ -884,9 +897,6 @@ void r_renderer_c::BeginFrame()
884897
}
885898
}
886899

887-
glBindFramebuffer(GL_FRAMEBUFFER, rttMain.framebuffer);
888-
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
889-
890900
curLayer = layerList[0];
891901

892902
SetViewport();
@@ -912,6 +922,22 @@ static int layerCompFunc(const void* va, const void* vb)
912922
}
913923
}
914924

925+
void CVarSliderInt(char const* label, conVar_c* cvar) {
926+
int curOpt = cvar->intVal;
927+
if (ImGui::SliderInt(label, &curOpt, cvar->min, cvar->max, "%d", ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_NoInput)) {
928+
if (curOpt != cvar->intVal) {
929+
cvar->Set(curOpt);
930+
}
931+
}
932+
}
933+
934+
void CVarCheckbox(char const* label, conVar_c* cvar) {
935+
bool checked = cvar->intVal == 1;
936+
if (ImGui::Checkbox(label, &checked)) {
937+
cvar->intVal = +checked;
938+
}
939+
}
940+
915941
void r_renderer_c::EndFrame()
916942
{
917943
static bool showDemo = false;
@@ -957,12 +983,10 @@ void r_renderer_c::EndFrame()
957983
if (debugLayers) {
958984
if (ImGui::Begin("Layers", &debugLayers)) {
959985
ImGui::Text("Layers: %d", numLayer);
960-
int curOpt = r_layerOptimize->intVal;
961-
if (ImGui::SliderInt("Optimization", &curOpt, r_layerOptimize->min, r_layerOptimize->max, "%d", ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_NoInput)) {
962-
if (curOpt != r_layerOptimize->intVal) {
963-
r_layerOptimize->Set(curOpt);
964-
}
965-
}
986+
ImGui::Text("%d out of %d frames drawn, %d saved.", drawnFrames, totalFrames, savedFrames);
987+
CVarSliderInt("Optimization", r_layerOptimize);
988+
CVarCheckbox("Elide identical frames", r_elideFrames);
989+
966990
int totalCmd{};
967991
if (ImGui::BeginTable("Layer stats", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
968992
ImGui::TableSetupColumn("Index");
@@ -988,8 +1012,74 @@ void r_renderer_c::EndFrame()
9881012
}
9891013
ImGui::End();
9901014
}
991-
for (int l = 0; l < numLayer; l++) {
992-
layerSort[l]->Render();
1015+
1016+
if (elideFrames != !!r_elideFrames->intVal) {
1017+
elideFrames = !!r_elideFrames->intVal;
1018+
lastFrameHash.clear();
1019+
}
1020+
1021+
std::vector<uint8_t> commandDigest(crypto_shorthash_bytes());
1022+
if (elideFrames) {
1023+
{
1024+
std::vector<char> commandBytes;
1025+
commandBytes.reserve(1ull << 24);
1026+
auto hash_primitive = [&](auto& x) {
1027+
auto p = (uint8_t const*)&x;
1028+
commandBytes.insert(commandBytes.end(), p, p + sizeof(x));
1029+
};
1030+
for (auto lIdx = 0; lIdx < numLayer; ++lIdx) {
1031+
auto layer = layerSort[lIdx];
1032+
hash_primitive(layer->layer);
1033+
hash_primitive(layer->subLayer);
1034+
for (auto cIdx = 0; cIdx < layer->numCmd; ++cIdx) {
1035+
auto cmd = layer->cmdList[cIdx];
1036+
hash_primitive(cmd->cmd);
1037+
switch (cmd->cmd) {
1038+
case r_layerCmd_s::VIEWPORT: {
1039+
hash_primitive(cmd->viewport);
1040+
} break;
1041+
case r_layerCmd_s::BLEND: {
1042+
hash_primitive(cmd->blendMode);
1043+
} break;
1044+
case r_layerCmd_s::BIND: {
1045+
// not safe around handle/ID reuse but probably good enough
1046+
hash_primitive(cmd->tex);
1047+
hash_primitive(cmd->tex->texId);
1048+
auto status = cmd->tex->status.load();
1049+
hash_primitive(status);
1050+
} break;
1051+
case r_layerCmd_s::COLOR: {
1052+
for (auto comp : cmd->col) {
1053+
hash_primitive(comp);
1054+
}
1055+
} break;
1056+
case r_layerCmd_s::QUAD: {
1057+
hash_primitive(cmd->quad);
1058+
} break;
1059+
}
1060+
}
1061+
}
1062+
static std::vector<uint8_t> const commandKey(crypto_shorthash_keybytes());
1063+
crypto_shorthash((unsigned char*)commandDigest.data(), (unsigned char const*)commandBytes.data(), commandBytes.size(), (unsigned char const*)commandKey.data());
1064+
}
1065+
}
1066+
1067+
++totalFrames;
1068+
if (!elideFrames || lastFrameHash != commandDigest) {
1069+
lastFrameHash = commandDigest;
1070+
++drawnFrames;
1071+
1072+
glBindFramebuffer(GL_FRAMEBUFFER, rttMain.framebuffer);
1073+
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1074+
for (int l = 0; l < numLayer; l++) {
1075+
layerSort[l]->Render();
1076+
}
1077+
}
1078+
else {
1079+
++savedFrames;
1080+
for (int l = 0; l < numLayer; l++) {
1081+
layerSort[l]->Discard();
1082+
}
9931083
}
9941084
delete[] layerSort;
9951085

engine/render/r_main.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#define R_MAXSHADERS 65536
1212

13+
#include <array>
1314
#include <imgui.h>
1415

1516
// =======
@@ -43,6 +44,7 @@ class r_layer_c {
4344
void Color(col4_t col);
4445
void Quad(double s0, double t0, double x0, double y0, double s1, double t1, double x1, double y1, double s2, double t2, double x2, double y2, double s3, double t3, double x3, double y3);
4546
void Render();
47+
void Discard();
4648

4749
private:
4850
r_renderer_c* renderer;
@@ -117,6 +119,7 @@ class r_renderer_c: public r_IRenderer, public conCmdHandler_c {
117119
conVar_c* r_layerDebug = nullptr;
118120
conVar_c* r_layerOptimize = nullptr;
119121
conVar_c* r_layerShuffle = nullptr;
122+
conVar_c* r_elideFrames = nullptr;
120123

121124
r_shaderHnd_c* whiteImage = nullptr; // White image
122125

@@ -156,6 +159,13 @@ class r_renderer_c: public r_IRenderer, public conCmdHandler_c {
156159

157160
RenderTarget rttMain;
158161

162+
std::vector<uint8_t> lastFrameHash{};
163+
164+
uint64_t totalFrames{};
165+
uint64_t drawnFrames{};
166+
uint64_t savedFrames{};
167+
168+
bool elideFrames = false;
159169
bool debugImGui = true;
160170
bool debugLayers = true;
161171

engine/render/r_texture.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ t_manager_c::~t_manager_c()
109109
int t_manager_c::GetAsyncCount()
110110
{
111111
std::lock_guard<std::mutex> lock ( mutex );
112-
return textureQueue.size();
112+
return (int)textureQueue.size();
113113
}
114114

115115
bool t_manager_c::GetImageInfo(const char* fileName, imageInfo_s* info)

vcpkg.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"libjpeg-turbo",
1111
"liblzma",
1212
"libpng",
13+
"libsodium",
1314
"luajit",
1415
"pkgconf",
1516
"re2",

0 commit comments

Comments
 (0)