/* Copyright (c) 2020-2025, Arm Limited and Contributors * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 the "License"; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "batch_mode.h" #include "vulkan_sample.h" namespace plugins { BatchMode::BatchMode() : BatchModeTags("Batch Mode", "Run a collection of samples in sequence.", { vkb::Hook::OnUpdate, vkb::Hook::OnAppError, }, {{"batch", "Enable batch mode"}}, {{"category", "Filter samples by categories"}, {"duration", "The duration which a configuration should run for in seconds"}, {"skip", "Skip a sample by id"}, {"tag", "Filter samples by tags"}, {"wrap-to-start", "Once all configurations have run wrap to the start"}}) { } bool BatchMode::handle_command(std::deque &arguments) const { assert(!arguments.empty()); if (arguments[0] == "batch") { arguments.pop_front(); return true; } return false; } bool BatchMode::handle_option(std::deque &arguments) { assert(!arguments.empty() && (arguments[0].substr(0, 2) == "--")); std::string option = arguments[0].substr(2); if (option == "category") { if (arguments.size() < 2) { LOGE("Option \"category\" is missing the actual category!"); return false; } std::string category = arguments[1]; if (std::ranges::any_of(categories, [&category](auto const &c) { return c == category; })) { LOGW("Option \"category\" lists category \"{}\" multiple times!", category) } else { categories.push_back(category); } arguments.pop_front(); arguments.pop_front(); return true; } else if (option == "duration") { if (arguments.size() < 2) { LOGE("Option \"duration\" is missing the actual duration!"); return false; } duration = std::chrono::duration{std::stof(arguments[1])}; arguments.pop_front(); arguments.pop_front(); return true; } else if (option == "skip") { if (arguments.size() < 2) { LOGE("Option \"skip\" is missing the sample_id to skip!"); return false; } std::string sample_id = arguments[1]; if (!skips.insert(sample_id).second) { LOGW("Option \"skip\" lists sample_id \"{}\" multiple times!", sample_id) } arguments.pop_front(); arguments.pop_front(); return true; } else if (option == "tag") { if (arguments.size() < 2) { LOGE("Option \"tag\" is missing the actual to tag!"); return false; } std::string tag = arguments[1]; if (std::ranges::any_of(tags, [&tag](auto const &t) { return t == tag; })) { LOGW("Option \"tag\" lists tag \"{}\" multiple times!", tag) } else { tags.push_back(tag); } arguments.pop_front(); arguments.pop_front(); return true; } else if (option == "wrap-to-start") { wrap_to_start = true; arguments.pop_front(); return true; } return false; } void BatchMode::trigger_command() { sample_list = apps::get_samples(categories, tags); if (!skips.empty()) { std::vector filtered_list; filtered_list.reserve(sample_list.size() - skips.size()); std::copy_if( sample_list.begin(), sample_list.end(), std::back_inserter(filtered_list), [&](const apps::AppInfo *app) { return !skips.count(app->id); }); if (filtered_list.size() != sample_list.size()) { sample_list.swap(filtered_list); } } if (sample_list.empty()) { LOGE("No samples found") throw std::runtime_error{"Can not continue"}; } sample_iter = sample_list.begin(); vkb::Window::OptionalProperties properties; properties.resizable = false; platform->set_window_properties(properties); platform->disable_input_processing(); platform->force_render(true); request_app(); } void BatchMode::on_update(float delta_time) { elapsed_time += delta_time; // When the runtime for the current configuration is reached, advance to the next config or next sample if (elapsed_time >= duration.count()) { elapsed_time = 0.0f; // Only check and advance the config if the application is a vulkan sample if (auto *vulkan_app = dynamic_cast(&platform->get_app())) { auto &configuration = vulkan_app->get_configuration(); if (configuration.next()) { configuration.set(); return; } } // Cycled through all configs, load next app load_next_app(); } } void BatchMode::on_app_error(const std::string &app_id) { // App failed, load next app load_next_app(); } void BatchMode::request_app() { LOGI("==========================================="); LOGI("Running {}", (*sample_iter)->id); LOGI("==========================================="); platform->request_application((*sample_iter)); } void BatchMode::load_next_app() { // Wrap it around to the start ++sample_iter; if (sample_iter == sample_list.end()) { if (wrap_to_start) { sample_iter = sample_list.begin(); } else { platform->close(); return; } } // App will be started before the next update loop request_app(); } } // namespace plugins