diff --git a/Celerity/include/renderer/renderer.hpp b/Celerity/include/renderer/renderer.hpp index b1cf92b..8fd782c 100644 --- a/Celerity/include/renderer/renderer.hpp +++ b/Celerity/include/renderer/renderer.hpp @@ -11,6 +11,7 @@ // Image writer includes #include "imageWriters/imageWriter.hpp" +#include "progressReporter.hpp" namespace Renderer { void singleCoreRender(const int imageWidth, const int imageHeight, const HittableList& world, const int maxRayDepth, @@ -20,5 +21,6 @@ void singleCoreRender(const int imageWidth, const int imageHeight, void multiCoreRender(std::atomic& scanLinesLeft, const int imageWidth, const int imageHeight, const HittableList& world, const int maxRayDepth, const Camera& camera, - const int samplesPerPixel, ImageWriter& iw); + const int samplesPerPixel, ImageWriter& iw, + ProgressReporter& progressReporter); } // namespace Renderer diff --git a/Celerity/src/main.cxx b/Celerity/src/main.cxx index 1e665aa..6bf9061 100644 --- a/Celerity/src/main.cxx +++ b/Celerity/src/main.cxx @@ -22,6 +22,8 @@ // spdlog include #include "spdlog/spdlog.h" +#include "progressReporter.hpp" + int main() { // Image constexpr float aspectRatio = 16.0 / 9.0; @@ -58,12 +60,17 @@ int main() { spdlog::info("Commencing ray tracing..."); + // Create a single ProgressReporter instance + ProgressReporter progressReporter(imageHeight); + progressReporter.start(); + // Kick off each thread with the Renderer::multiCoreRender() task for (std::size_t i = 0; i < numThreads; ++i) { threadPool[i] = std::jthread(Renderer::multiCoreRender, std::ref(scanLinesLeft), imageWidth, imageHeight, std::cref(world), maxRayDepth, - std::cref(camera), samplesPerPixel, std::ref(iw)); + std::cref(camera), samplesPerPixel, std::ref(iw), + std::ref(progressReporter)); } delete[] threadPool; diff --git a/Celerity/src/renderer/renderer.cxx b/Celerity/src/renderer/renderer.cxx index 0651e53..dddbd27 100644 --- a/Celerity/src/renderer/renderer.cxx +++ b/Celerity/src/renderer/renderer.cxx @@ -1,4 +1,5 @@ #include "renderer/renderer.hpp" +#include "progressReporter.hpp" #include #include @@ -66,16 +67,13 @@ void singleCoreRender(const int imageWidth, const int imageHeight, void multiCoreRender(std::atomic& scanLinesLeft, const int imageWidth, const int imageHeight, const HittableList& world, const int maxRayDepth, const Camera& camera, - const int samplesPerPixel, ImageWriter& iw) { + const int samplesPerPixel, ImageWriter& iw, + ProgressReporter& progressReporter) { while (scanLinesLeft >= 0) { int currentImageRow = scanLinesLeft; scanLinesLeft--; int bufferRow = imageHeight - currentImageRow - 1; - std::osyncstream syncedLog(std::clog); // TODO: Make this optional. - syncedLog << "\rScanlines remaining: " << currentImageRow << " " - << std::flush; - for (int i = 0; i < imageWidth; ++i) { color pixelColor; for (int s = 0; s < samplesPerPixel; ++s) { @@ -91,6 +89,9 @@ void multiCoreRender(std::atomic& scanLinesLeft, const int imageWidth, iw.addPixel(3 * (bufferRow * imageWidth + i), pixelColor); } + + progressReporter.scanlineProcessed(); + progressReporter.update(); } } } // namespace Renderer diff --git a/utilities/include/progressReporter.hpp b/utilities/include/progressReporter.hpp index e9d4c43..7a821e6 100644 --- a/utilities/include/progressReporter.hpp +++ b/utilities/include/progressReporter.hpp @@ -1,8 +1,75 @@ #pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + class ProgressReporter { - public: +public: + ProgressReporter(int totalScanLines, int updateInterval = 1) + : totalScanLines(totalScanLines), scanLinesLeft(totalScanLines), + updateInterval(updateInterval), startTime(std::chrono::steady_clock::now()), lastUpdate(startTime) { + auto logger = spdlog::stdout_color_mt("progress_logger"); + logger->set_pattern("%v"); // Set pattern to only show the message + spdlog::set_default_logger(logger); + + spdlog::info("Starting render with {} total scanlines", totalScanLines); + } + + void start() { + startTime = std::chrono::steady_clock::now(); + scanLinesLeft = totalScanLines; + } + + void update() { + using namespace std::chrono; + auto now = steady_clock::now(); + if (duration_cast(now - lastUpdate).count() >= updateInterval) { + lastUpdate = now; + displayProgressBar(); + } + } + + void scanlineProcessed() { + --scanLinesLeft; + } + +private: + int totalScanLines; + std::atomic scanLinesLeft; + std::chrono::steady_clock::time_point startTime; + std::chrono::steady_clock::time_point lastUpdate; + int updateInterval; + std::mutex mtx; + + void displayProgressBar() { + std::lock_guard lock(mtx); + int remaining = scanLinesLeft.load(); + int processed = totalScanLines - remaining; + float progress = static_cast(processed) / totalScanLines; + + int barWidth = 50; + std::ostringstream bar; + bar << "\r["; + int pos = barWidth * progress; + for (int i = 0; i < barWidth; ++i) { + if (i < pos) bar << "="; + else if (i == pos) bar << ">"; + else bar << " "; + } + bar << "] " << int(progress * 100.0) << "%"; + + auto now = std::chrono::steady_clock::now(); + auto elapsed = std::chrono::duration_cast(now - startTime).count(); + auto eta = progress > 0 ? static_cast((elapsed / progress) * (1.0 - progress)) : 0; + bar << " ETA: " << eta << "s "; - private: - -}; \ No newline at end of file + spdlog::default_logger_raw()->log(spdlog::level::info, bar.str()); + } +};