| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/ui/examples/escher/common/demo.h" |
| |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include <array> |
| #include <chrono> |
| #include <thread> |
| |
| #include "src/ui/lib/escher/impl/command_buffer_pool.h" |
| #include "src/ui/lib/escher/impl/image_cache.h" |
| #include "src/ui/lib/escher/renderer/frame.h" |
| #include "src/ui/lib/escher/util/stopwatch.h" |
| #include "src/ui/lib/escher/util/trace_macros.h" |
| |
| Demo::Demo(escher::EscherWeakPtr escher, const char* name) |
| : name_(name), escher_(escher), vulkan_context_(escher_->vulkan_context()) {} |
| |
| Demo::~Demo() {} |
| |
| bool Demo::HandleKeyPress(std::string key) { |
| if (key.size() > 1) { |
| if (key == "ESCAPE") { |
| return false; |
| } else if (key == "SPACE") { |
| return false; |
| } else if (key == "RETURN") { |
| return false; |
| } |
| // Illegal value. |
| FX_LOGS(ERROR) << "Cannot handle key value: " << key; |
| FX_CHECK(false); |
| return false; |
| } else { |
| char key_char = key[0]; |
| switch (key_char) { |
| case 'T': |
| ToggleTracing(); |
| return true; |
| default: |
| return false; |
| } |
| } |
| } |
| |
| void Demo::ToggleTracing() { |
| #ifdef __linux__ |
| if (tracer_) { |
| tracer_.reset(); |
| FX_LOGS(INFO) << "Tracing disabled."; |
| } else { |
| tracer_ = std::make_unique<escher::Tracer>(); |
| FX_LOGS(INFO) << "Tracing enabled."; |
| } |
| #else |
| FX_LOGS(INFO) << "ToggleTracing() only supported for Escher-Linux."; |
| #endif |
| } |
| |
| void Demo::RunOffscreenBenchmark(Demo* demo, uint32_t framebuffer_width, |
| uint32_t framebuffer_height, vk::Format framebuffer_format, |
| size_t frame_count) { |
| constexpr uint64_t kSecondsToNanoseconds = 1000000000; |
| const char* kTraceLiteral = "RunOffscreenBenchmark"; |
| |
| // Clean up before running benchmark. |
| auto escher = demo->escher(); |
| escher->vk_device().waitIdle(); |
| escher->Cleanup(); |
| |
| uint64_t frame_number = 0; |
| |
| // Create the images that we will render into, and the semaphores that will |
| // prevent us from rendering into the same image concurrently. At the same |
| // time, draw a few throwaway frames, to warm things up before beginning the |
| // benchmark (this also signals the semaphores so that they can be waited |
| // upon in the actual benchmark run). |
| constexpr size_t kSwapchainSize = 2; |
| std::array<escher::SemaphorePtr, kSwapchainSize> semaphores; |
| std::array<escher::ImagePtr, kSwapchainSize> images; |
| { |
| auto image_cache = escher->image_cache(); |
| for (size_t i = 0; i < kSwapchainSize; ++i) { |
| auto im = image_cache->NewImage({framebuffer_format, framebuffer_width, framebuffer_height, 1, |
| vk::ImageUsageFlagBits::eColorAttachment | |
| vk::ImageUsageFlagBits::eTransferSrc | |
| vk::ImageUsageFlagBits::eTransferDst}); |
| images[i] = im; |
| semaphores[i] = escher::Semaphore::New(escher->vk_device()); |
| |
| auto frame = escher->NewFrame(kTraceLiteral, ++frame_number); |
| |
| im->set_swapchain_layout(vk::ImageLayout::eColorAttachmentOptimal); |
| frame->cmds()->ImageBarrier(im, vk::ImageLayout::eUndefined, im->swapchain_layout(), |
| vk::PipelineStageFlagBits::eAllCommands, |
| vk::AccessFlagBits::eColorAttachmentWrite, |
| vk::PipelineStageFlagBits::eColorAttachmentOutput, |
| vk::AccessFlagBits::eColorAttachmentWrite); |
| |
| demo->DrawFrame(frame, images[i], escher::SemaphorePtr()); |
| frame->EndFrame(semaphores[i], []() {}); |
| } |
| |
| // Prepare all semaphores to be waited-upon, and wait for the throwaway |
| // frames to finish. |
| escher->vk_device().waitIdle(); |
| escher->Cleanup(); |
| } |
| |
| // Render the benchmark frames. |
| escher::Stopwatch stopwatch; |
| stopwatch.Start(); |
| |
| uint32_t frames_in_flight = 0; |
| for (size_t current_frame = 0; current_frame < frame_count; ++current_frame) { |
| FX_DCHECK(frames_in_flight <= kMaxOutstandingFrames); |
| while (frames_in_flight == kMaxOutstandingFrames) { |
| std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
| escher->Cleanup(); |
| } |
| |
| // Avoid drawing multiple frames at the same time to the same image by waiting for and signaling |
| // the same semaphore. As described above, all semaphores are guaranteed to have been signaled |
| // the first time they are encountered in this loop. |
| size_t image_index = current_frame % kSwapchainSize; |
| ++frames_in_flight; |
| auto frame = escher->NewFrame(kTraceLiteral, ++frame_number, current_frame == frame_count - 1); |
| demo->DrawFrame(frame, images[image_index], semaphores[image_index]); |
| frame->EndFrame(semaphores[image_index], [&]() { --frames_in_flight; }); |
| |
| escher->Cleanup(); |
| } |
| |
| // Wait for the last frame to finish. |
| escher->vk_device().waitIdle(); |
| stopwatch.Stop(); |
| FX_CHECK(escher->Cleanup()); |
| |
| FX_LOGS(INFO) << "------------------------------------------------------"; |
| FX_LOGS(INFO) << "Offscreen benchmark"; |
| FX_LOGS(INFO) << "Rendered " << frame_count << " " << framebuffer_width << "x" |
| << framebuffer_height << " frames in " << stopwatch.GetElapsedSeconds() |
| << " seconds"; |
| FX_LOGS(INFO) << (frame_count / stopwatch.GetElapsedSeconds()) << " FPS"; |
| FX_LOGS(INFO) << "------------------------------------------------------"; |
| } |