Files
Vulkan-Samples/app/plugins/batch_mode/batch_mode.cpp
T
2025-09-04 10:54:47 +08:00

233 lines
5.6 KiB
C++

/* 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<std::string> &arguments) const
{
assert(!arguments.empty());
if (arguments[0] == "batch")
{
arguments.pop_front();
return true;
}
return false;
}
bool BatchMode::handle_option(std::deque<std::string> &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<float, vkb::Timer::Seconds>{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<apps::AppInfo *> 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<vkb::VulkanSampleC *>(&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