Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 59 additions & 70 deletions engine/render/r_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include <imgui_impl_opengl3.h>
#include <imgui_stdlib.h>

#include <xxh3.h>

static uint64_t MurmurHash64A(void const* data, int len, uint64_t seed);

// =======
Expand Down Expand Up @@ -125,8 +127,9 @@ Mat4 OrthoMatrix(double left, double right, double bottom, double top, double ne
// Layer queue class
// =================

#pragma pack(push, r_layerCmd, 1)
struct r_layerCmd_s {
enum Command {
enum Command : uint8_t {
VIEWPORT,
BLEND,
BIND,
Expand Down Expand Up @@ -165,6 +168,7 @@ struct r_layerCmdQuad_s {
int stackLayer, maskLayer;
} quad;
};
#pragma pack(pop, r_layerCmd)

r_layer_c::r_layer_c(r_renderer_c* renderer, int layer, int subLayer)
: renderer(renderer), layer(layer), subLayer(subLayer)
Expand Down Expand Up @@ -418,6 +422,7 @@ struct RenderStrategy {
virtual void ProcessCommand(r_layerCmd_s* cmd) = 0;
virtual void Flush() = 0;
virtual void SetShowStats(bool showStats) { showStats_ = showStats; }
virtual bool UsedIncompleteTextures() const { return false; }

protected:
bool showStats_{};
Expand Down Expand Up @@ -572,6 +577,8 @@ struct AdjacentMergeStrategy : RenderStrategy {
}
}

bool UsedIncompleteTextures() const override { return usedIncompleteTextures; };

private:
void Dispatch() {
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
Expand Down Expand Up @@ -623,7 +630,10 @@ struct AdjacentMergeStrategy : RenderStrategy {
auto tex = textures[i];
tex->Bind();
if (showStats_) {
ImGui::Text("New tex %d (%s)", tex->texId, tex->fileName.c_str());
ImGui::Text("New tex %d (%s) %d", tex->texId, tex->fileName.c_str(), tex->status.load());
}
if (!usedIncompleteTextures && tex->status != r_tex_c::Status::DONE) {
usedIncompleteTextures = true;
}
}
else {
Expand Down Expand Up @@ -655,7 +665,7 @@ struct AdjacentMergeStrategy : RenderStrategy {

struct TexturedBatch {
explicit TexturedBatch(GLuint prog) : batch(prog) {
textures.reserve(1ull << 20);
textures.reserve(128);
}

BatchKey key{};
Expand All @@ -673,9 +683,11 @@ struct AdjacentMergeStrategy : RenderStrategy {

size_t totalVertexCount_ = 0;
size_t batchIndex = 0;

bool usedIncompleteTextures = false;
};

void r_layer_c::Render()
bool r_layer_c::Render()
{
int const optLevel = renderer->r_layerOptimize->intVal;
bool const shuffle = renderer->r_layerShuffle->intVal == 1;
Expand Down Expand Up @@ -713,6 +725,8 @@ void r_layer_c::Render()
if (renderer->glPopGroupMarkerEXT) {
renderer->glPopGroupMarkerEXT();
}

return strat->UsedIncompleteTextures();
}

void r_layer_c::Discard()
Expand Down Expand Up @@ -1150,12 +1164,6 @@ void r_renderer_c::Shutdown()
void r_renderer_c::PumpShaders()
{
texMan->ProcessPendingTextureUploads();
for (size_t idx = 0; idx < numShader; ++idx)
if (auto* sh = shaderList[idx])
if (auto tex = sh->tex; tex && tex->status != r_tex_c::DONE) {
inhibitElision = true;
break;
}
}

void r_renderer_c::BeginFrame()
Expand Down Expand Up @@ -1344,13 +1352,15 @@ void r_renderer_c::EndFrame()
ImGui::Text("Total dense footprint: %sB", BinaryUnitPrefix(totalDenseFootprint).c_str());

size_t totalCmd{};
if (ImGui::BeginTable("Layer stats", 7, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
if (ImGui::BeginTable("Layer stats", 8, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit)) {
ImGui::TableSetupColumn("Index");
ImGui::TableSetupColumn("Layer");
ImGui::TableSetupColumn("Sublayer");
ImGui::TableSetupColumn("Command count");
ImGui::TableSetupColumn("Dense");
ImGui::TableSetupColumn("Debug");
ImGui::TableSetupColumn("XXH3-64");
ImGui::TableSetupColumn("MH64A");
ImGui::TableHeadersRow();
for (int l = 0; l < numLayer; ++l) {
auto layer = layerSort[l];
Expand All @@ -1372,6 +1382,22 @@ void r_renderer_c::EndFrame()
if (ImGui::Button("Debug")) {
layerBreak = { layer->layer, layer->subLayer };
}

std::chrono::high_resolution_clock::time_point tic;
std::chrono::microseconds dt;

ImGui::TableNextColumn();
tic = std::chrono::high_resolution_clock::now();
volatile auto xxh_hash = XXH3_64bits(layer->cmdStorage.data(), layer->cmdCursor);
dt = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - tic);
ImGui::Text("%d µs", dt.count());

ImGui::TableNextColumn();
tic = std::chrono::high_resolution_clock::now();
volatile auto mh_hash = MurmurHash64A(layer->cmdStorage.data(), (int)layer->cmdCursor, 0ull);
dt = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - tic);
ImGui::Text("%d µs", dt.count());

ImGui::PopID();
ImGui::PopID();
}
Expand All @@ -1383,83 +1409,46 @@ void r_renderer_c::EndFrame()

if (inhibitElision || elideFrames != !!r_elideFrames->intVal) {
elideFrames = !!r_elideFrames->intVal;
lastFrameHash.clear();
lastFrameHash = 0;
}

std::future<std::optional<std::vector<uint8_t>>> elidedFrameHashFut;
auto tic = std::chrono::high_resolution_clock::now();

uint64_t commandDigest = 0;
if (elideFrames) {
elidedFrameHashFut = std::async([&]() -> std::optional<std::vector<uint8_t>> {
std::vector<uint8_t> commandDigest;

for (auto lIdx = 0; lIdx < numLayer; ++lIdx) {
auto layer = layerSort[lIdx];
uint64_t subHash = MurmurHash64A(layer->cmdStorage.data(), (int)layer->cmdCursor, 0ull);
uint8_t const* p = (uint8_t const*)&subHash;
commandDigest.insert(commandDigest.end(), p, p + sizeof(subHash));
}
std::shared_ptr<XXH3_state_t> hashState(XXH3_createState(), XXH3_freeState);
XXH3_64bits_reset(hashState.get());

return commandDigest;
});
}
else {
std::promise<std::optional<std::vector<uint8_t>>> p;
elidedFrameHashFut = p.get_future();
p.set_value({});
}
for (auto lIdx = 0; lIdx < numLayer; ++lIdx) {
auto layer = layerSort[lIdx];
uint64_t subHash = XXH3_64bits(layer->cmdStorage.data(), (int)layer->cmdCursor);
XXH3_64bits_update(hashState.get(), &subHash, sizeof(subHash));
}

elidedFrameHashFut.wait();
commandDigest = XXH3_64bits_digest(hashState.get());
}

++totalFrames;
bool decideDraw = false;
bool elideDraw = false;
const bool elideDraw = lastFrameHash != 0 && lastFrameHash == commandDigest;
if (!elideDraw)
{
glBindFramebuffer(GL_FRAMEBUFFER, GetDrawRenderTarget().framebuffer);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
int l{};
for (l = 0; l < numLayer; l++) {
if (!decideDraw && elidedFrameHashFut.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) {
decideDraw = true;
auto commandDigest = elidedFrameHashFut.get();
if (commandDigest) {
if (*commandDigest == lastFrameHash) {
elideDraw = true;
break;
}
else {
lastFrameHash = *commandDigest;
}
}
else {
lastFrameHash.clear();
}
}
for (int l = 0; l < numLayer; l++) {
auto& layer = layerSort[l];
if (layerBreak && layerBreak->first == layer->layer && layerBreak->second == layer->subLayer) {
#ifdef _WIN32
DebugBreak();
#endif
}
layer->Render();
}
if (!elideDraw) {
presentRtt = 1 - presentRtt;
++drawnFrames;
}
}

if (!decideDraw) {
if (auto commandDigest = elidedFrameHashFut.get()) {
lastFrameHash = *commandDigest;
}
else {
lastFrameHash.clear();
inhibitElision = layer->Render() || inhibitElision;
}
presentRtt = 1 - presentRtt;
++drawnFrames;
}

if (inhibitElision) {
// If we explicitly inhibited elision due to things like incomplete textures, make sure that the next frame is drawn.
lastFrameHash.clear();
}
// If we explicitly inhibited elision due to things like incomplete textures, make sure that the next frame is drawn.
lastFrameHash = inhibitElision ? 0 : commandDigest;

for (int l = 0; l < numLayer; ++l) {
layerSort[l]->Discard();
Expand Down Expand Up @@ -1500,7 +1489,7 @@ void r_renderer_c::EndFrame()
if (ImGui::Begin("Hash")) {
char* b64{};
size_t b64Len{};
Base64UrlEncode((char const*)lastFrameHash.data(), lastFrameHash.size(), &b64, &b64Len);
Base64UrlEncode((char const*)&lastFrameHash, sizeof(lastFrameHash), &b64, &b64Len);
ImGui::Text("%s", b64);
free(b64);
}
Expand Down
4 changes: 2 additions & 2 deletions engine/render/r_main.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class r_layer_c {
void Bind(r_tex_c* tex);
void Color(col4_t col);
void Quad(float s0, float t0, float x0, float y0, float s1, float t1, float x1, float y1, float s2, float t2, float x2, float y2, float s3, float t3, float x3, float y3, int stackLayer = 0, int maskLayer = -1);
void Render();
bool Render();
void Discard();

struct CmdHandle {
Expand Down Expand Up @@ -180,7 +180,7 @@ class r_renderer_c: public r_IRenderer, public conCmdHandler_c {
RenderTarget rttMain[2];
int presentRtt = 0;

std::vector<uint8_t> lastFrameHash{};
uint64_t lastFrameHash{};

uint64_t totalFrames{};
uint64_t drawnFrames{};
Expand Down
1 change: 1 addition & 0 deletions vcpkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"pkgconf",
"re2",
"sol2",
"xxhash",
"zstd",
"zlib"
]
Expand Down
Loading