diff --git a/src/openrct2/interface/Screenshot.cpp b/src/openrct2/interface/Screenshot.cpp index 9cbdab5b3e..707cf7dd9b 100644 --- a/src/openrct2/interface/Screenshot.cpp +++ b/src/openrct2/interface/Screenshot.cpp @@ -320,6 +320,34 @@ static int32_t GetTallestVisibleTileTop(int32_t mapSize, int32_t rotation) return minViewY - 256; } +static rct_drawpixelinfo CreateDPI(const rct_viewport& viewport) +{ + rct_drawpixelinfo dpi; + dpi.width = viewport.width; + dpi.height = viewport.height; + dpi.bits = new (std::nothrow) uint8_t[dpi.width * dpi.height]; + if (dpi.bits == nullptr) + { + throw std::runtime_error("Giant screenshot failed, unable to allocate memory for image."); + } + + if (viewport.flags & VIEWPORT_FLAG_TRANSPARENT_BACKGROUND) + { + std::memset(dpi.bits, PALETTE_INDEX_0, (size_t)dpi.width * dpi.height); + } + + return dpi; +} + +static void ReleaseDPI(rct_drawpixelinfo& dpi) +{ + if (dpi.bits != nullptr) + delete[] dpi.bits; + dpi.bits = nullptr; + dpi.width = 0; + dpi.height = 0; +} + static rct_viewport GetGiantViewport(int32_t mapSize, int32_t rotation, int32_t zoom) { // Get the tile coordinates of each corner @@ -349,25 +377,11 @@ static rct_viewport GetGiantViewport(int32_t mapSize, int32_t rotation, int32_t return viewport; } -static rct_drawpixelinfo RenderViewport(IDrawingEngine* drawingEngine, const rct_viewport& viewport) +static void RenderViewport(IDrawingEngine* drawingEngine, const rct_viewport& viewport, rct_drawpixelinfo& dpi) { // Ensure sprites appear regardless of rotation reset_all_sprite_quadrant_placements(); - rct_drawpixelinfo dpi; - dpi.width = viewport.width; - dpi.height = viewport.height; - dpi.bits = (uint8_t*)malloc((size_t)dpi.width * dpi.height); - if (dpi.bits == nullptr) - { - throw std::runtime_error("Giant screenshot failed, unable to allocate memory for image."); - } - - if (viewport.flags & VIEWPORT_FLAG_TRANSPARENT_BACKGROUND) - { - std::memset(dpi.bits, PALETTE_INDEX_0, (size_t)dpi.width * dpi.height); - } - std::unique_ptr tempDrawingEngine; if (drawingEngine == nullptr) { @@ -376,12 +390,11 @@ static rct_drawpixelinfo RenderViewport(IDrawingEngine* drawingEngine, const rct } dpi.DrawingEngine = drawingEngine; viewport_render(&dpi, &viewport, 0, 0, viewport.width, viewport.height); - return dpi; } void screenshot_giant() { - rct_drawpixelinfo dpi; + rct_drawpixelinfo dpi{}; try { auto path = screenshot_get_next_path(); @@ -410,7 +423,9 @@ void screenshot_giant() viewport.flags |= VIEWPORT_FLAG_TRANSPARENT_BACKGROUND; } - dpi = RenderViewport(nullptr, viewport); + dpi = CreateDPI(viewport); + + RenderViewport(nullptr, viewport, dpi); auto renderedPalette = screenshot_get_rendered_palette(); WriteDpiToFile(path->c_str(), &dpi, renderedPalette); @@ -424,7 +439,17 @@ void screenshot_giant() log_error("%s", e.what()); context_show_error(STR_SCREENSHOT_FAILED, STR_NONE); } - free(dpi.bits); + + ReleaseDPI(dpi); +} + +// TODO: Move this at some point into a more appropriate place. +template static inline double MeasureFunctionTime(const FN& fn) +{ + const auto startTime = std::chrono::high_resolution_clock::now(); + fn(); + const auto endTime = std::chrono::high_resolution_clock::now(); + return std::chrono::duration(endTime - startTime).count(); } static void benchgfx_render_screenshots(const char* inputPath, std::unique_ptr& context, uint32_t iterationCount) @@ -437,33 +462,73 @@ static void benchgfx_render_screenshots(const char* inputPath, std::unique_ptr dpis; + std::array viewports; + + for (int32_t zoom = 0; zoom < MAX_ZOOM_LEVEL; zoom++) + { + for (int32_t rotation = 0; rotation < MAX_ROTATIONS; rotation++) + { + auto& viewport = viewports[zoom * MAX_ZOOM_LEVEL + rotation]; + auto& dpi = dpis[zoom * MAX_ZOOM_LEVEL + rotation]; + viewport = GetGiantViewport(gMapSize, rotation, zoom); + dpi = CreateDPI(viewport); + } + } + + const uint32_t totalRenderCount = iterationCount * MAX_ROTATIONS * MAX_ZOOM_LEVEL; + try { - auto startTime = std::chrono::high_resolution_clock::now(); - for (uint32_t i = 0; i < iterationCount; i++) - { - // Render at various zoom levels - auto viewport = GetGiantViewport(gMapSize, get_current_rotation(), 0); - viewport.zoom = i & 3; - dpi = RenderViewport(nullptr, viewport); - free(dpi.bits); - dpi.bits = nullptr; - } - auto endTime = std::chrono::high_resolution_clock::now(); - std::chrono::duration duration = endTime - startTime; + double totalTime = 0.0; - auto engineStringId = DrawingEngineStringIds[DRAWING_ENGINE_SOFTWARE]; - auto engineName = format_string(engineStringId, nullptr); - std::printf( - "Rendering %u times with drawing engine %s took %.2f seconds.", iterationCount, engineName.c_str(), - duration.count()); + std::array zoomAverages; + + // Render at every zoom. + for (int32_t zoom = 0; zoom < MAX_ZOOM_LEVEL; zoom++) + { + double zoomLevelTime = 0.0; + + // Render at every rotation. + for (int32_t rotation = 0; rotation < MAX_ROTATIONS; rotation++) + { + // N iterations. + for (uint32_t i = 0; i < iterationCount; i++) + { + auto& dpi = dpis[zoom * MAX_ZOOM_LEVEL + rotation]; + auto& viewport = viewports[zoom * MAX_ZOOM_LEVEL + rotation]; + double elapsed = MeasureFunctionTime([&viewport, &dpi]() { RenderViewport(nullptr, viewport, dpi); }); + totalTime += elapsed; + zoomLevelTime += elapsed; + } + } + + zoomAverages[zoom] = zoomLevelTime / static_cast(MAX_ROTATIONS * iterationCount); + } + + const double average = totalTime / static_cast(totalRenderCount); + const auto engineStringId = DrawingEngineStringIds[DRAWING_ENGINE_SOFTWARE]; + const auto engineName = format_string(engineStringId, nullptr); + std::printf("Engine: %s\n", engineName.c_str()); + std::printf("Render Count: %u\n", totalRenderCount); + for (int32_t zoom = 0; zoom < MAX_ZOOM_LEVEL; zoom++) + { + const auto zoomAverage = zoomAverages[zoom]; + std::printf("Zoom[%d] average: %.06fs, %.f FPS\n", zoom, zoomAverage, 1.0 / zoomAverage); + } + std::printf("Total average: %.06fs, %.f FPS\n", average, 1.0 / average); + std::printf("Time: %.05fs\n", totalTime); } catch (const std::exception& e) { std::fprintf(stderr, "%s", e.what()); } - free(dpi.bits); + + for (auto& dpi : dpis) + ReleaseDPI(dpi); } int32_t cmdline_for_gfxbench(const char** argv, int32_t argc) @@ -475,7 +540,7 @@ int32_t cmdline_for_gfxbench(const char** argv, int32_t argc) } core_init(); - int32_t iterationCount = 40; + int32_t iterationCount = 5; if (argc == 2) { iterationCount = atoi(argv[1]); @@ -677,7 +742,9 @@ int32_t cmdline_for_screenshot(const char** argv, int32_t argc, ScreenshotOption ApplyOptions(options, viewport); - dpi = RenderViewport(nullptr, viewport); + dpi = CreateDPI(viewport); + + RenderViewport(nullptr, viewport, dpi); auto renderedPalette = screenshot_get_rendered_palette(); WriteDpiToFile(outputPath, &dpi, renderedPalette); } @@ -686,7 +753,8 @@ int32_t cmdline_for_screenshot(const char** argv, int32_t argc, ScreenshotOption std::printf("%s\n", e.what()); exitCode = -1; } - free(dpi.bits); + ReleaseDPI(dpi); + drawing_engine_dispose(); return exitCode;