From 298648bdcaf1c1215c9dc05fdd2b544f1d3c7ac7 Mon Sep 17 00:00:00 2001 From: Tomas Maly Date: Sun, 10 Nov 2024 05:16:33 +0100 Subject: [PATCH] dynamic resolution WIP; remove stalling gpu with glFinish --- .../engine/{blit.glsl => blitPixels.glsl} | 0 data/cage/shader/engine/blitScaled.glsl | 26 ++++++ data/cage/shader/engine/engine.assets | 5 +- data/cage/shader/engine/engine.pack | 5 +- .../shader/engine/{font.glsl => text.glsl} | 0 data/cage/shader/shader.assets | 1 - data/cage/shader/shader.pack | 2 - data/cage/shader/visualize/color.glsl | 14 --- data/cage/shader/visualize/depth.glsl | 20 ----- data/cage/shader/visualize/monochromatic.glsl | 14 --- data/cage/shader/visualize/vertex.glsl | 9 -- data/cage/shader/visualize/visualize.assets | 10 --- data/cage/shader/visualize/visualize.pack | 5 -- sources/include/cage-engine/renderQueue.h | 4 +- sources/include/cage-simple/engine.h | 8 ++ sources/include/cage-simple/statisticsGui.h | 19 ++-- sources/libengine/graphics/renderPipeline.cpp | 53 ++++------- sources/libsimple/engine.h | 4 +- sources/libsimple/externs.cpp | 6 ++ sources/libsimple/gameloop.cpp | 87 +++++++++++------- sources/libsimple/graphics.cpp | 90 ++++++++++++++++--- sources/libsimple/statisticsGui.cpp | 13 +-- 22 files changed, 223 insertions(+), 172 deletions(-) rename data/cage/shader/engine/{blit.glsl => blitPixels.glsl} (100%) create mode 100644 data/cage/shader/engine/blitScaled.glsl rename data/cage/shader/engine/{font.glsl => text.glsl} (100%) delete mode 100644 data/cage/shader/visualize/color.glsl delete mode 100644 data/cage/shader/visualize/depth.glsl delete mode 100644 data/cage/shader/visualize/monochromatic.glsl delete mode 100644 data/cage/shader/visualize/vertex.glsl delete mode 100644 data/cage/shader/visualize/visualize.assets delete mode 100644 data/cage/shader/visualize/visualize.pack diff --git a/data/cage/shader/engine/blit.glsl b/data/cage/shader/engine/blitPixels.glsl similarity index 100% rename from data/cage/shader/engine/blit.glsl rename to data/cage/shader/engine/blitPixels.glsl diff --git a/data/cage/shader/engine/blitScaled.glsl b/data/cage/shader/engine/blitScaled.glsl new file mode 100644 index 00000000..f1f35d0c --- /dev/null +++ b/data/cage/shader/engine/blitScaled.glsl @@ -0,0 +1,26 @@ + +$include ../shaderConventions.h + +$define shader vertex + +layout(location = CAGE_SHADER_ATTRIB_IN_POSITION) in vec3 inPosition; +layout(location = CAGE_SHADER_ATTRIB_IN_UV) in vec3 inUv; +out vec2 varUv; + +void main() +{ + gl_Position = vec4(inPosition.xy * 2 - 1, inPosition.z, 1); + varUv = inUv.xy; + varUv.y = 1 - varUv.y; +} + +$define shader fragment + +layout(binding = 0) uniform sampler2D texColor; +in vec2 varUv; +out vec4 outColor; + +void main() +{ + outColor = textureLod(texColor, varUv, 0); +} diff --git a/data/cage/shader/engine/engine.assets b/data/cage/shader/engine/engine.assets index 13bf6ac7..fbac51c5 100644 --- a/data/cage/shader/engine/engine.assets +++ b/data/cage/shader/engine/engine.assets @@ -5,8 +5,9 @@ engine.pack [] scheme = shader billboard.glsl -blit.glsl +blitPixels.glsl +blitScaled.glsl decal.glsl -font.glsl skybox.glsl standard.glsl +text.glsl diff --git a/data/cage/shader/engine/engine.pack b/data/cage/shader/engine/engine.pack index e4d8369d..2eb66e19 100644 --- a/data/cage/shader/engine/engine.pack +++ b/data/cage/shader/engine/engine.pack @@ -1,4 +1,5 @@ [] -blit.glsl -font.glsl +blitPixels.glsl +blitScaled.glsl standard.glsl +text.glsl diff --git a/data/cage/shader/engine/font.glsl b/data/cage/shader/engine/text.glsl similarity index 100% rename from data/cage/shader/engine/font.glsl rename to data/cage/shader/engine/text.glsl diff --git a/data/cage/shader/shader.assets b/data/cage/shader/shader.assets index 1de8e220..4b9e78d6 100644 --- a/data/cage/shader/shader.assets +++ b/data/cage/shader/shader.assets @@ -1,4 +1,3 @@ - [] scheme = pack shader.pack diff --git a/data/cage/shader/shader.pack b/data/cage/shader/shader.pack index 0fb79153..a9709edd 100644 --- a/data/cage/shader/shader.pack +++ b/data/cage/shader/shader.pack @@ -1,6 +1,4 @@ - [] effects/effects.pack engine/engine.pack -visualize/visualize.pack gui/gui.pack diff --git a/data/cage/shader/visualize/color.glsl b/data/cage/shader/visualize/color.glsl deleted file mode 100644 index 92beeba3..00000000 --- a/data/cage/shader/visualize/color.glsl +++ /dev/null @@ -1,14 +0,0 @@ - -$include vertex.glsl - -$define shader fragment - -layout(location = 0) uniform vec2 uniScale; -layout(binding = 0) uniform sampler2D texColor; -out vec4 outColor; - -void main() -{ - vec2 uv = gl_FragCoord.xy * uniScale; - outColor = textureLod(texColor, uv, 0); -} diff --git a/data/cage/shader/visualize/depth.glsl b/data/cage/shader/visualize/depth.glsl deleted file mode 100644 index 6305d3ae..00000000 --- a/data/cage/shader/visualize/depth.glsl +++ /dev/null @@ -1,20 +0,0 @@ - -$include vertex.glsl - -$define shader fragment - -layout(location = 0) uniform vec2 uniScale; -layout(binding = 0) uniform sampler2D texDepth; -out vec3 outColor; - -void main() -{ - vec2 uv = gl_FragCoord.xy * uniScale; - float v = textureLod(texDepth, uv, 0).r; - if (v < 1e-7) - outColor = vec3(1.0, 0.0, 0.0); - else if (v > 1 - 1e-7) - outColor = vec3(0.0, 1.0, 0.0); - else - outColor = vec3(v); -} diff --git a/data/cage/shader/visualize/monochromatic.glsl b/data/cage/shader/visualize/monochromatic.glsl deleted file mode 100644 index 023d3e0a..00000000 --- a/data/cage/shader/visualize/monochromatic.glsl +++ /dev/null @@ -1,14 +0,0 @@ - -$include vertex.glsl - -$define shader fragment - -layout(location = 0) uniform vec2 uniScale; -layout(binding = 0) uniform sampler2D texColor; -out vec4 outColor; - -void main() -{ - vec2 uv = gl_FragCoord.xy * uniScale; - outColor = textureLod(texColor, uv, 0).rrra; -} diff --git a/data/cage/shader/visualize/vertex.glsl b/data/cage/shader/visualize/vertex.glsl deleted file mode 100644 index 38f61ef4..00000000 --- a/data/cage/shader/visualize/vertex.glsl +++ /dev/null @@ -1,9 +0,0 @@ - -$define shader vertex - -layout(location = 0) in vec3 inPosition; - -void main() -{ - gl_Position = vec4(inPosition.xy * 2.0 - 1.0, inPosition.z, 1.0); -} diff --git a/data/cage/shader/visualize/visualize.assets b/data/cage/shader/visualize/visualize.assets deleted file mode 100644 index fd90ae82..00000000 --- a/data/cage/shader/visualize/visualize.assets +++ /dev/null @@ -1,10 +0,0 @@ - -[] -scheme = pack -visualize.pack - -[] -scheme = shader -color.glsl -depth.glsl -monochromatic.glsl diff --git a/data/cage/shader/visualize/visualize.pack b/data/cage/shader/visualize/visualize.pack deleted file mode 100644 index cb5d1557..00000000 --- a/data/cage/shader/visualize/visualize.pack +++ /dev/null @@ -1,5 +0,0 @@ - -[] -color.glsl -depth.glsl -monochromatic.glsl diff --git a/sources/include/cage-engine/renderQueue.h b/sources/include/cage-engine/renderQueue.h index 453457bd..15eb24d0 100644 --- a/sources/include/cage-engine/renderQueue.h +++ b/sources/include/cage-engine/renderQueue.h @@ -114,13 +114,13 @@ namespace cage { using Fnc = void(void *); static Fnc *fnc = +[](void *d) { (*(T *)d)(); }; - _customCommand(std::move(data).cast(), fnc); + _customCommand(std::move(data).template cast(), fnc); } template void customCommand(Holder data, void fnc(T *)) { using Fnc = void(void *); - _customCommand(std::move(data).cast(), (Fnc *)fnc); + _customCommand(std::move(data).template cast(), (Fnc *)fnc); } [[nodiscard]] struct RenderQueueNamedScope namedScope(StringPointer name); diff --git a/sources/include/cage-simple/engine.h b/sources/include/cage-simple/engine.h index b5abd7e1..9d4598ff 100644 --- a/sources/include/cage-simple/engine.h +++ b/sources/include/cage-simple/engine.h @@ -89,6 +89,14 @@ namespace cage ProvisionalGraphics *engineProvisonalGraphics(); uint64 engineControlTime(); + struct EngineDynamicResolution + { + uint32 targetFps = 30; + Real minimumScale = 0.6; + bool enabled = false; + }; + EngineDynamicResolution &engineDynamicResolution(); + namespace detail { void enginePurgeAssetsOnDemandCache(); diff --git a/sources/include/cage-simple/statisticsGui.h b/sources/include/cage-simple/statisticsGui.h index f9e277d7..8f61aba9 100644 --- a/sources/include/cage-simple/statisticsGui.h +++ b/sources/include/cage-simple/statisticsGui.h @@ -8,15 +8,16 @@ namespace cage enum class StatisticsGuiFlags : uint32 { None = 0, - Utilization = 1 << 0, // % main thread cpu use - Control = 1 << 1, - Sound = 1 << 2, - GraphicsPrepare = 1 << 3, - GraphicsDispatch = 1 << 4, - FrameTime = 1 << 5, - DrawCalls = 1 << 6, - DrawPrimitives = 1 << 7, - Entities = 1 << 8, + CpuUtilization = 1 << 0, // % main thread cpu use + DynamicResolution = 1 << 1, + ControlThreadTime = 1 << 2, + SoundThreadTime = 1 << 3, + PrepareThreadTime = 1 << 4, + GpuTime = 1 << 5, + FrameTime = 1 << 6, + DrawCalls = 1 << 7, + DrawPrimitives = 1 << 8, + Entities = 1 << 9, }; GCHL_ENUM_BITS(StatisticsGuiFlags); diff --git a/sources/libengine/graphics/renderPipeline.cpp b/sources/libengine/graphics/renderPipeline.cpp index 1f2405ab..3e802b36 100644 --- a/sources/libengine/graphics/renderPipeline.cpp +++ b/sources/libengine/graphics/renderPipeline.cpp @@ -40,8 +40,6 @@ namespace cage { const ConfigBool confRenderMissingModels("cage/graphics/renderMissingModels", false); const ConfigBool confRenderSkeletonBones("cage/graphics/renderSkeletonBones", false); - const ConfigBool confNoAmbientOcclusion("cage/graphics/disableAmbientOcclusion", false); - const ConfigBool confNoBloom("cage/graphics/disableBloom", false); struct UniViewport { @@ -344,9 +342,9 @@ namespace cage struct RenderPipelineImpl : RenderPipelineConfig { Holder modelSquare, modelBone; - Holder shaderBlit, shaderDepth, shaderStandard, shaderDepthCutOut, shaderStandardCutOut; - Holder shaderVisualizeColor, shaderVisualizeDepth, shaderVisualizeMonochromatic; - Holder shaderFont; + Holder shaderBlitPixels, shaderBlitScaled; + Holder shaderDepth, shaderStandard, shaderDepthCutOut, shaderStandardCutOut; + Holder shaderText; Holder skeletonPreparatorCollection; EntityComponent *transformComponent = nullptr; @@ -362,17 +360,17 @@ namespace cage modelSquare = assets->get(HashString("cage/model/square.obj")); modelBone = assets->get(HashString("cage/model/bone.obj")); - shaderBlit = defaultProgram(assets->get(HashString("cage/shader/engine/blit.glsl"))); + CAGE_ASSERT(modelSquare && modelBone); + shaderBlitPixels = assets->get(HashString("cage/shader/engine/blitPixels.glsl"))->get(0); + shaderBlitScaled = assets->get(HashString("cage/shader/engine/blitScaled.glsl"))->get(0); + CAGE_ASSERT(shaderBlitPixels && shaderBlitScaled); Holder standard = assets->get(HashString("cage/shader/engine/standard.glsl")); - shaderStandard = defaultProgram(standard, 0); - shaderStandardCutOut = defaultProgram(standard, HashString("CutOut")); - shaderDepth = defaultProgram(standard, HashString("DepthOnly")); - shaderDepthCutOut = defaultProgram(standard, HashString("DepthOnly") + HashString("CutOut")); - shaderVisualizeColor = defaultProgram(assets->get(HashString("cage/shader/visualize/color.glsl"))); - shaderVisualizeDepth = defaultProgram(assets->get(HashString("cage/shader/visualize/depth.glsl"))); - shaderVisualizeMonochromatic = defaultProgram(assets->get(HashString("cage/shader/visualize/monochromatic.glsl"))); - shaderFont = defaultProgram(assets->get(HashString("cage/shader/engine/font.glsl"))); - CAGE_ASSERT(shaderBlit); + CAGE_ASSERT(standard); + shaderDepth = standard->get(HashString("DepthOnly")); + shaderDepthCutOut = standard->get(HashString("DepthOnly") + HashString("CutOut")); + shaderStandard = standard->get(0); + shaderStandardCutOut = standard->get(HashString("CutOut")); + shaderText = assets->get(HashString("cage/shader/engine/text.glsl"))->get(0); transformComponent = scene->component(); prevTransformComponent = scene->componentsByType(detail::typeIndex())[1]; @@ -380,13 +378,6 @@ namespace cage skeletonPreparatorCollection = newSkeletalAnimationPreparatorCollection(assets); } - static Holder defaultProgram(const Holder &multi, uint32 variant = 0) - { - if (multi) - return multi->get(variant); - return {}; - } - Mat4 modelTransform(Entity *e) const { CAGE_ASSERT(e->has(transformComponent)); @@ -507,8 +498,8 @@ namespace cage void renderTextImpl(const CameraData &data, const TextPrepare &text) const { const Holder &renderQueue = data.renderQueue; - renderQueue->uniform(shaderFont, 0, data.viewProj * text.model); - renderQueue->uniform(shaderFont, 4, text.color); + renderQueue->uniform(shaderText, 0, data.viewProj * text.model); + renderQueue->uniform(shaderText, 4, text.color); text.font->render(+renderQueue, modelSquare, text.glyphs, text.format); } @@ -575,7 +566,7 @@ namespace cage renderQueue->culling(true); renderQueue->blending(true); renderQueue->blendFuncAlphaTransparency(); - renderQueue->bind(shaderFont); + renderQueue->bind(shaderText); for (const TextPrepare &text : layer.texts) renderTextImpl(data, text); renderQueue->resetAllState(); @@ -957,11 +948,6 @@ namespace cage Holder &renderQueue = data.renderQueue; const auto graphicsDebugScope = renderQueue->namedScope("camera"); - if (confNoAmbientOcclusion) - data.effects.effects &= ~ScreenSpaceEffectsFlags::AmbientOcclusion; - if (confNoBloom) - data.effects.effects &= ~ScreenSpaceEffectsFlags::Bloom; - data.model = Mat4(data.transform); data.view = inverse(data.model); data.viewProj = data.projection * data.view; @@ -1027,8 +1013,7 @@ namespace cage renderQueue->activeAttachments(renderTarget, 1); renderQueue->checkFrameBuffer(renderTarget); renderQueue->viewport(Vec2i(), ssaoResolution); - renderQueue->bind(shaderVisualizeColor); - renderQueue->uniform(shaderVisualizeColor, 0, 1 / Vec2(ssaoResolution)); + renderQueue->bind(shaderBlitScaled); renderQueue->bind(depthTexture, 0); renderQueue->draw(modelSquare); renderQueue->resetAllState(); @@ -1155,7 +1140,7 @@ namespace cage { renderQueue->viewport(Vec2i(), data.resolution); renderQueue->bind(texSource, 0); - renderQueue->bind(shaderBlit); + renderQueue->bind(shaderBlitPixels); renderQueue->bind(renderTarget); renderQueue->colorTexture(renderTarget, 0, colorTexture); renderQueue->activeAttachments(renderTarget, 1); @@ -1171,7 +1156,7 @@ namespace cage renderQueue->resetAllState(); renderQueue->viewport(Vec2i(), data.resolution); renderQueue->bind(colorTexture, 0); - renderQueue->bind(shaderBlit); + renderQueue->bind(shaderBlitPixels); renderQueue->bind(renderTarget); renderQueue->colorTexture(renderTarget, 0, data.target); renderQueue->depthTexture(renderTarget, {}); diff --git a/sources/libsimple/engine.h b/sources/libsimple/engine.h index 70c56da6..91fee5c6 100644 --- a/sources/libsimple/engine.h +++ b/sources/libsimple/engine.h @@ -11,9 +11,9 @@ namespace cage void graphicsInitialize(); // opengl thread void graphicsFinalize(); // opengl thread void graphicsEmit(uint64 time); // control thread - void graphicsPrepare(uint64 time, uint32 &drawCalls, uint32 &drawPrimitives); // prepare thread + void graphicsPrepare(uint64 time, uint32 &drawCalls, uint32 &drawPrimitives, Real &dynamicResolution); // prepare thread void graphicsDispatch(); // opengl thread - void graphicsSwap(); // opengl thread + void graphicsSwap(uint64 &gpuTime); // opengl thread void soundCreate(const EngineCreateConfig &config); void soundDestroy(); diff --git a/sources/libsimple/externs.cpp b/sources/libsimple/externs.cpp index 96c39ec9..6cc5cf22 100644 --- a/sources/libsimple/externs.cpp +++ b/sources/libsimple/externs.cpp @@ -36,4 +36,10 @@ namespace cage static EventDispatcher instance; return instance; } + + EngineDynamicResolution &engineDynamicResolution() + { + static EngineDynamicResolution instance; + return instance; + } } diff --git a/sources/libsimple/gameloop.cpp b/sources/libsimple/gameloop.cpp index e9b7cfdf..12be1017 100644 --- a/sources/libsimple/gameloop.cpp +++ b/sources/libsimple/gameloop.cpp @@ -61,16 +61,17 @@ namespace cage Semaphore *sem = nullptr; }; + template struct ScopedTimer : private Immovable { - VariableSmoothingBuffer &vsb; - uint64 st; + VariableSmoothingBuffer &vsb; + uint64 st = applicationTime(); - explicit ScopedTimer(VariableSmoothingBuffer &vsb) : vsb(vsb), st(applicationTime()) {} + explicit ScopedTimer(VariableSmoothingBuffer &vsb) : vsb(vsb) {} ~ScopedTimer() { - uint64 et = applicationTime(); + const uint64 et = applicationTime(); vsb.add(et - st); } }; @@ -109,12 +110,13 @@ namespace cage struct EngineData { - VariableSmoothingBuffer profilingBufferGraphicsPrepare; - VariableSmoothingBuffer profilingBufferGraphicsDispatch; - VariableSmoothingBuffer profilingBufferFrameTime; - VariableSmoothingBuffer profilingBufferDrawCalls; - VariableSmoothingBuffer profilingBufferDrawPrimitives; - VariableSmoothingBuffer profilingBufferEntities; + VariableSmoothingBuffer profilingBufferDynamicResolution; + VariableSmoothingBuffer profilingBufferPrepareTime; + VariableSmoothingBuffer profilingBufferGpuTime; + VariableSmoothingBuffer profilingBufferFrameTime; + VariableSmoothingBuffer profilingBufferDrawCalls; + VariableSmoothingBuffer profilingBufferDrawPrimitives; + VariableSmoothingBuffer profilingBufferEntities; Holder assets; Holder window; @@ -171,11 +173,13 @@ namespace cage if (stopping) return; // prevent getting stuck in virtual reality waiting for previous (already terminated) frame dispatch ProfilingScope profiling("graphics prepare run"); - ScopedTimer timing(profilingBufferGraphicsPrepare); + ScopedTimer timing(profilingBufferPrepareTime); uint32 drawCalls = 0, drawPrimitives = 0; - graphicsPrepare(applicationTime(), drawCalls, drawPrimitives); + Real dynamicResolution; + graphicsPrepare(applicationTime(), drawCalls, drawPrimitives, dynamicResolution); profilingBufferDrawCalls.add(drawCalls); profilingBufferDrawPrimitives.add(drawPrimitives); + profilingBufferDynamicResolution.add(dynamicResolution); } } @@ -210,7 +214,6 @@ namespace cage { ScopedSemaphores lockGraphics(graphicsSemaphore2, graphicsSemaphore1); ProfilingScope profiling("graphics dispatch run"); - ScopedTimer timing(profilingBufferGraphicsDispatch); graphicsDispatch(); } { @@ -235,7 +238,9 @@ namespace cage } { ProfilingScope profiling("swap"); - graphicsSwap(); + uint64 gpuTime = 0; + graphicsSwap(gpuTime); + profilingBufferGpuTime.add(gpuTime); } } @@ -355,7 +360,7 @@ namespace cage const uint64 start = applicationTime(); while (assets->processCustomThread(0)) { - if (applicationTime() > start + 10'000) + if (applicationTime() > start + 5'000) break; } } @@ -842,32 +847,51 @@ namespace cage { uint64 result = 0; - if (any(flags & StatisticsGuiFlags::Utilization)) + if (any(flags & StatisticsGuiFlags::CpuUtilization)) result += 100 * engineData->controlScheduler->utilization(); + if (any(flags & StatisticsGuiFlags::DynamicResolution)) + { + switch (mode) + { + case StatisticsGuiModeEnum::Average: + result += numeric_cast(100 * engineData->profilingBufferDynamicResolution.smooth()); + break; + case StatisticsGuiModeEnum::Maximum: + // maximum/worts time corresponds to minimum dynamic resolution + result += numeric_cast(100 * engineData->profilingBufferDynamicResolution.min()); + break; + case StatisticsGuiModeEnum::Latest: + result += numeric_cast(100 * engineData->profilingBufferDynamicResolution.current()); + break; + default: + CAGE_THROW_CRITICAL(Exception, "invalid profiling mode enum"); + } + } + { const auto &add = [&](const auto &s) { switch (mode) { - case StatisticsGuiModeEnum::Latest: - result += s.latestDuration; - break; case StatisticsGuiModeEnum::Average: result += s.avgDuration; break; case StatisticsGuiModeEnum::Maximum: result += s.maxDuration; break; + case StatisticsGuiModeEnum::Latest: + result += s.latestDuration; + break; default: CAGE_THROW_CRITICAL(Exception, "invalid profiling mode enum"); } }; - if (any(flags & StatisticsGuiFlags::Control)) + if (any(flags & StatisticsGuiFlags::ControlThreadTime)) add(engineData->controlUpdateSchedule->statistics()); - if (any(flags & StatisticsGuiFlags::Sound)) + if (any(flags & StatisticsGuiFlags::SoundThreadTime)) add(engineData->soundUpdateSchedule->statistics()); } @@ -890,16 +914,19 @@ namespace cage } }; -#define GCHL_GENERATE(NAME) \ - if (any(flags & StatisticsGuiFlags::NAME)) \ - { \ - auto &buffer = CAGE_JOIN(engineData->profilingBuffer, NAME); \ - add(buffer); \ - } - CAGE_EVAL_SMALL(CAGE_EXPAND_ARGS(GCHL_GENERATE, GraphicsPrepare, GraphicsDispatch, FrameTime, DrawCalls, DrawPrimitives, Entities)); -#undef GCHL_GENERATE + if (any(flags & StatisticsGuiFlags::PrepareThreadTime)) + add(engineData->profilingBufferPrepareTime); + if (any(flags & StatisticsGuiFlags::GpuTime)) + add(engineData->profilingBufferGpuTime); + if (any(flags & StatisticsGuiFlags::FrameTime)) + add(engineData->profilingBufferFrameTime); + if (any(flags & StatisticsGuiFlags::DrawCalls)) + add(engineData->profilingBufferDrawCalls); + if (any(flags & StatisticsGuiFlags::DrawPrimitives)) + add(engineData->profilingBufferDrawPrimitives); + if (any(flags & StatisticsGuiFlags::Entities)) + add(engineData->profilingBufferEntities); } - return result; } } diff --git a/sources/libsimple/graphics.cpp b/sources/libsimple/graphics.cpp index d1a2b9fd..9b0a1129 100644 --- a/sources/libsimple/graphics.cpp +++ b/sources/libsimple/graphics.cpp @@ -1,3 +1,5 @@ +#include +#include #include #include "engine.h" @@ -34,6 +36,30 @@ namespace cage { const ConfigFloat confRenderGamma("cage/graphics/gamma", 2.2); + struct TimeQuery : Noncopyable + { + private: + uint32 id = 0; + + public: + uint64 time = 0; // nanoseconds + + void start() + { + if (id == 0) + glGenQueries(1, &id); + else + glGetQueryObjectui64v(id, GL_QUERY_RESULT, &time); + glBeginQuery(GL_TIME_ELAPSED, id); + } + + void finish() + { + if (id != 0) + glEndQuery(GL_TIME_ELAPSED); + } + }; + struct EmitBuffer : private Immovable { Holder scene = newEntityManager(); @@ -171,13 +197,41 @@ namespace cage } } - void prepareCameras(const RenderPipelineConfig &cfg, Holder vrFrame) + Vec2i applyDynamicResolution(Vec2i in) const { - Holder modelSquare = cfg.assets->get(HashString("cage/model/square.obj")); - CAGE_ASSERT(modelSquare); - Holder shaderBlit = cfg.assets->get(HashString("cage/shader/engine/blit.glsl"))->get(0); - CAGE_ASSERT(shaderBlit); + Vec2i res = Vec2i(Vec2(in) * dynamicResolution); + CAGE_ASSERT(res[0] > 0 && res[1] > 0); + return res; + } + + void updateDynamicResolution() + { + // dynamic resolution may update every 4 frames at most + if ((frameIndex % 4) != 0 || frameIndex < 100) + return; + + if (!engineDynamicResolution().enabled) + { + dynamicResolution = 1; + return; + } + + CAGE_ASSERT(engineDynamicResolution().targetFps > 0); + CAGE_ASSERT(valid(engineDynamicResolution().minimumScale)); + CAGE_ASSERT(engineDynamicResolution().minimumScale > 0 && engineDynamicResolution().minimumScale <= 1); + + const double targetTime = 1'000'000'000 / engineDynamicResolution().targetFps; + const double avgTime = (timeQueries[0].time + timeQueries[0].time + timeQueries[1].time + timeQueries[2].time) / 4; // most recent frame time is more significant + Real k = dynamicResolution * targetTime / avgTime; + if (!valid(k)) + return; + k = clamp(k, engineDynamicResolution().minimumScale, 1); + if (abs(dynamicResolution - k) > 0.02) + dynamicResolution = k; + } + void prepareCameras(const RenderPipelineConfig &cfg, Holder vrFrame) + { std::vector cameras; std::vector vrTargets; TextureHandle windowTarget; @@ -198,9 +252,11 @@ namespace cage data.camera = cam; if (e->has()) data.effects = e->value(); + if (dynamicResolution != 1) + data.effects.effects &= ~ScreenSpaceEffectsFlags::AntiAliasing; data.effects.gamma = Real(confRenderGamma); data.name = Stringizer() + "camera_" + e->id(); - data.resolution = cam.target ? cam.target->resolution() : cfg.resolution; + data.resolution = cam.target ? cam.target->resolution() : applyDynamicResolution(cfg.resolution); data.transform = modelTransform(e, cfg.interpolationFactor); data.projection = initializeProjection(cam, data.resolution); data.lodSelection = initializeLodSelection(cam, data.resolution[1]); @@ -248,9 +304,11 @@ namespace cage if (camEnt->has()) data.effects = camEnt->value(); } + if (dynamicResolution != 1) + data.effects.effects &= ~ScreenSpaceEffectsFlags::AntiAliasing; data.effects.gamma = Real(confRenderGamma); data.name = Stringizer() + "vr_camera_" + index; - data.resolution = it.resolution; + data.resolution = applyDynamicResolution(it.resolution); data.transform = transformByVrOrigin(cfg.scene, it.transform, cfg.interpolationFactor); data.projection = it.projection; data.lodSelection.screenSize = perspectiveScreenSize(it.verticalFov, data.resolution[1]); @@ -276,6 +334,8 @@ namespace cage if (cam.renderQueue) renderQueue->enqueue(std::move(cam.renderQueue)); + Holder shaderBlit = cfg.assets->get(HashString("cage/shader/engine/blitScaled.glsl"))->get(0); + CAGE_ASSERT(shaderBlit); renderQueue->bind(shaderBlit); // blit to window @@ -286,6 +346,8 @@ namespace cage if (windowTarget) { renderQueue->bind(windowTarget, 0); + Holder modelSquare = cfg.assets->get(HashString("cage/model/square.obj")); + CAGE_ASSERT(modelSquare); renderQueue->draw(modelSquare); } else @@ -352,6 +414,7 @@ namespace cage dispatchTime = vrFrame->displayTime(); } + updateDynamicResolution(); const EmitBuffer &eb = emitBuffers[lock.index()]; const Vec2i windowResolution = engineWindow()->resolution(); RenderPipelineConfig cfg; @@ -411,8 +474,11 @@ namespace cage void swap() // opengl thread { CAGE_CHECK_GL_ERROR_DEBUG(); + timeQueries[0].finish(); + std::rotate(timeQueries.begin(), timeQueries.begin() + 1, timeQueries.end()); engineWindow()->swapBuffers(); - glFinish(); // this is where the engine should be waiting for the gpu + // this is where the engine should be waiting for the gpu + timeQueries[0].start(); } Holder renderQueue; @@ -424,6 +490,8 @@ namespace cage uint64 lastDispatchTime = 0; uint32 frameIndex = 0; + Real dynamicResolution = 1; + std::array timeQueries = {}; }; Graphics *graphics; @@ -456,9 +524,10 @@ namespace cage graphics->emit(emitTime); } - void graphicsPrepare(uint64 dispatchTime, uint32 &drawCalls, uint32 &drawPrimitives) + void graphicsPrepare(uint64 dispatchTime, uint32 &drawCalls, uint32 &drawPrimitives, Real &dynamicResolution) { graphics->prepare(dispatchTime, drawCalls, drawPrimitives); + dynamicResolution = graphics->dynamicResolution; } void graphicsDispatch() @@ -466,9 +535,10 @@ namespace cage graphics->dispatch(); } - void graphicsSwap() + void graphicsSwap(uint64 &gpuTime) { graphics->swap(); + gpuTime = graphics->timeQueries[0].time / 1000; } namespace detail diff --git a/sources/libsimple/statisticsGui.cpp b/sources/libsimple/statisticsGui.cpp index 47a6afdc..2047f55f 100644 --- a/sources/libsimple/statisticsGui.cpp +++ b/sources/libsimple/statisticsGui.cpp @@ -34,7 +34,7 @@ namespace cage [this](Entity *e) { const uint64 v = engineStatisticsValues(Flags, this->statisticsMode); - if constexpr (Flags == StatisticsGuiFlags::Utilization) + if constexpr (Flags == StatisticsGuiFlags::CpuUtilization || Flags == StatisticsGuiFlags::DynamicResolution) e->value().value = Stringizer() + v + " %"; else if constexpr (Flags <= StatisticsGuiFlags::FrameTime) e->value().value = Stringizer() + (v / 1000) + " ms"; @@ -75,11 +75,12 @@ namespace cage generate(+g, "Frame time: "); break; case StatisticsGuiScopeEnum::Full: - generate(+g, "Utilization: "); - generate(+g, "Control: "); - generate(+g, "Sound: "); - generate(+g, "Graphics prepare: "); - generate(+g, "Graphics dispatch: "); + generate(+g, "CPU utilization: "); + generate(+g, "Dyn. resolution: "); + generate(+g, "Control time: "); + generate(+g, "Sound time: "); + generate(+g, "Prepare time: "); + generate(+g, "GPU time: "); generate(+g, "Frame time: "); generate(+g, "Draw calls: "); generate(+g, "Draw primitives: ");