diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/Renderer.h b/Minecraft.Client/Windows64/4JLibs/inc/Render/Renderer.h index 599ebda0a..082b341d6 100644 --- a/Minecraft.Client/Windows64/4JLibs/inc/Render/Renderer.h +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/Renderer.h @@ -170,6 +170,23 @@ private: void DeleteInternalBuffer(int index); Renderer::Context &getContext(); void GrowCommandBufferArrays(); + +private: + void ShrinkCommandBufferArrays(); + + static constexpr int kMinCommandBuffers = MAX_COMMAND_BUFFERS; // нижняя граница, никогда не уменьшаем ниже + static constexpr int kShrinkCooldownTicks = 300; // ~5 секунд при 60 тик/с + static constexpr float kShrinkThreshold = 0.75f; // сжимаем если занято < kShrinkThreshold% слотов + static constexpr float kShrinkTarget = kShrinkThreshold; // сжимаем до kShrinkThreshold текущего размера + + int m_shrinkCooldown; // обратный счётчик — сколько тиков осталось до следующей проверки + int m_peakUsageSinceLastShrinkCheck; // пик использования за период cooldown + +#ifdef _DEBUG + DWORD m_peakCommandBuffers; + DWORD m_peakHandles; +#endif + public: enum eTextureSamplerFlags diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCBuff.cpp b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCBuff.cpp index 8bd0dc241..382a01ef9 100644 --- a/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCBuff.cpp +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCBuff.cpp @@ -418,9 +418,9 @@ void Renderer::CBuffTick() { EnterCriticalSection(&m_commandBufferCS); + // --- Удаление одного устаревшего буфера за тик --- if (m_numBuffersToDeallocate > 0) { - // Удаляем ровно один буфер за тик — чтобы не спайкать кадр const int totalSlots = static_cast(m_commandBuffers.size()); const int deleteIdx = totalSlots - static_cast(m_numBuffersToDeallocate); @@ -436,6 +436,30 @@ void Renderer::CBuffTick() --m_numBuffersToDeallocate; } + // --- Отслеживаем пик использования за период --- + const int currentUsage = static_cast(m_currentCommandBuffer); + if (currentUsage > m_peakUsageSinceLastShrinkCheck) + m_peakUsageSinceLastShrinkCheck = currentUsage; + +#ifdef _DEBUG + m_peakCommandBuffers = std::max(m_peakCommandBuffers, (DWORD)m_commandBuffers.size()); + m_peakHandles = std::max(m_peakHandles, (DWORD)m_vertexIdxToBufferIdx.size()); +#endif + + // --- Проверка на сжатие раз в kShrinkCooldownTicks тиков --- + if (--m_shrinkCooldown <= 0) + { + m_shrinkCooldown = kShrinkCooldownTicks; + + // Используем пик за весь период, а не моментальное значение — + // защита от случайных провалов активности + const int capacityToCheck = static_cast(m_commandBuffers.size()); + if (m_peakUsageSinceLastShrinkCheck < static_cast(capacityToCheck * kShrinkThreshold)) + ShrinkCommandBufferArrays(); + + m_peakUsageSinceLastShrinkCheck = 0; + } + LeaveCriticalSection(&m_commandBufferCS); } diff --git a/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCore.cpp b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCore.cpp index 3f4d39f10..f1ebdfc92 100644 --- a/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCore.cpp +++ b/Minecraft.Client/Windows64/4JLibs/inc/Render/RendererCore.cpp @@ -833,6 +833,14 @@ void Renderer::Initialise(ID3D11Device *pDevice, IDXGISwapChain *pSwapChain) m_pDevice->CreateBuffer(&fanIndexDesc, &fanIndexData, &fanIndexBuffer); delete[] fanIndices; + m_shrinkCooldown = kShrinkCooldownTicks; + m_peakUsageSinceLastShrinkCheck = 0; + +#ifdef _DEBUG + m_peakCommandBuffers = 0; + m_peakHandles = 0; +#endif + InitializeCriticalSection(&Renderer::totalAllocCS); } @@ -1075,3 +1083,46 @@ void Renderer::GrowCommandBufferArrays() } } } + +void Renderer::ShrinkCommandBufferArrays() +{ + // Нельзя сжимать пока есть ожидающие удаления — они в хвосте, + // и при изменении размера нарушится их расположение + if (m_numBuffersToDeallocate > 0) + return; + + const int currentSize = static_cast(m_commandBuffers.size()); + const int activeCount = static_cast(m_currentCommandBuffer); + + // Никогда не уменьшаемся ниже минимума + if (currentSize <= kMinCommandBuffers) + return; + + // Проверяем порог: если занято >= 25% — не трогаем + if (activeCount >= static_cast(currentSize * kShrinkThreshold)) + return; + + // Новый размер: 50% от текущего, но не меньше kMinCommandBuffers + // и не меньше чем нужно для активных буферов с запасом x2 + const int newSize = max(kMinCommandBuffers, max(static_cast(currentSize * kShrinkTarget), activeCount * 2)); + + // Если разница незначительна — не трогаем + if (newSize >= currentSize) + return; + + // Активные буферы уже в зоне [0, m_currentCommandBuffer) — + // они все влезают в newSize, просто усекаем хвост + m_commandBuffers.resize(newSize); + m_commandMatrices.resize(newSize); + m_bufferIdxToVertexIdx.resize(newSize, 0); + m_commandPrimitiveTypes.resize(newSize, 0); + m_commandVertexTypes.resize(newSize, 0); + +#ifdef _DEBUG + OutputDebugStringA( + ("Renderer: shrunk command buffer pool " + + std::to_string(currentSize) + " -> " + + std::to_string(newSize) + "\n").c_str() + ); +#endif +} diff --git a/Minecraft.Client/Windows64/4JLibs/libs/4J_Render_PC.lib b/Minecraft.Client/Windows64/4JLibs/libs/4J_Render_PC.lib index 3b9b61dc6..85d2e4c77 100644 Binary files a/Minecraft.Client/Windows64/4JLibs/libs/4J_Render_PC.lib and b/Minecraft.Client/Windows64/4JLibs/libs/4J_Render_PC.lib differ