MXVK Vulkan Framework 0.24.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1#include "mxvk/argz.hpp"
2#include "mxvk/mxvk.hpp"
3#include "mxvk/mxvk_cv.hpp"
5#include <array>
6#include <chrono>
7#include <cstdlib>
8#include <format>
9#include <iostream>
10#include <opencv2/videoio.hpp>
11#include <string>
12
13#ifndef opencv_example_ASSET_DIR
14#define opencv_example_ASSET_DIR "."
15#endif
16
17#ifndef opencv_example_SHADER_DIR
18#define opencv_example_SHADER_DIR "."
19#endif
20
21namespace example {
22 class ExampleWindow : public mxvk::VK_Window {
23 std::string current_path = ".";
24 std::string shader_path;
25 std::string input_filename;
26 double fps = 60.0f;
27 int camera_index = 0;
28 bool using_file = false;
29 mxvk::VK_Capture capture{};
30 mxvk::VK_Sprite *camera_sprite = nullptr;
31 int fallback_width = 1280;
32 int fallback_height = 720;
33 double current_fps = 0.0;
34 uint32_t fps_frame_count = 0;
35 std::chrono::steady_clock::time_point fps_sample_time{std::chrono::steady_clock::now()};
36 std::string fps_text = "FPS: --";
37
38 [[nodiscard]] bool openCaptureSource() {
39 if (using_file) {
40 return capture.open(input_filename);
41 }
42 return capture.open(camera_index);
43 }
44
45 [[nodiscard]] double configureCameraFps() {
46 static constexpr std::array<double, 3> fpsChoices = {60.0, 30.0, 24.0};
47
48 for (const double requestedFps : fpsChoices) {
49 capture.set(cv::CAP_PROP_FPS, requestedFps);
50 const double reportedFps = capture.get(cv::CAP_PROP_FPS);
51 if (reportedFps > 0.0 && reportedFps + 0.5 >= requestedFps) {
52 return reportedFps;
53 }
54 }
55
56 capture.set(cv::CAP_PROP_FPS, fpsChoices.back());
57 const double reportedFps = capture.get(cv::CAP_PROP_FPS);
58 return (reportedFps > 0.0) ? reportedFps : fpsChoices.back();
59 }
60
61 [[nodiscard]] bool createOrRefreshCameraSprite() {
62 int frame_width = static_cast<int>(capture.get(cv::CAP_PROP_FRAME_WIDTH));
63 int frame_height = static_cast<int>(capture.get(cv::CAP_PROP_FRAME_HEIGHT));
64 if (frame_width <= 0 || frame_height <= 0) {
65 frame_width = fallback_width;
66 frame_height = fallback_height;
67 }
68
69 const std::string vertex_shader = shader_path + "/vertex.vert.spv";
70 const std::string fragment_shader = shader_path + "/fragment.frag.spv";
71
72 if (camera_sprite == nullptr) {
73 camera_sprite = createSprite(frame_width, frame_height, vertex_shader, fragment_shader);
74 return camera_sprite != nullptr;
75 }
76
77 camera_sprite->createEmptySprite(frame_width, frame_height, vertex_shader, fragment_shader);
78 return true;
79 }
80
81 [[nodiscard]] bool uploadFrameToSprite(cv::Mat &frame) {
82 if (camera_sprite == nullptr || frame.empty()) {
83 return false;
84 }
85
86 cv::Mat rgba;
87 if (frame.channels() == 4) {
88 cv::cvtColor(frame, rgba, cv::COLOR_BGRA2RGBA);
89 } else if (frame.channels() == 3) {
90 cv::cvtColor(frame, rgba, cv::COLOR_BGR2RGBA);
91 } else if (frame.channels() == 1) {
92 cv::cvtColor(frame, rgba, cv::COLOR_GRAY2RGBA);
93 } else {
94 return false;
95 }
96
97 camera_sprite->updateTexture(rgba.ptr(), rgba.cols, rgba.rows, static_cast<int>(rgba.step));
98 return true;
99 }
100
101 void initializeCameraRendering() {
102 if (swapchain == VK_NULL_HANDLE || command_pool == VK_NULL_HANDLE) {
103 createDevice();
104 }
105
106 if (!createOrRefreshCameraSprite()) {
107 throw mxvk::Exception("opencv_example: failed to create capture sprite");
108 }
109
110 if (camera_sprite != nullptr && !capture.readToSprite(*camera_sprite)) {
111 std::cerr << "opencv_example: failed to upload initial camera frame\n";
112 }
113 }
114
115 void updateFpsOverlay() {
116 ++fps_frame_count;
117 const auto now = std::chrono::steady_clock::now();
118 const double elapsed = std::chrono::duration<double>(now - fps_sample_time).count();
119 if (elapsed >= 0.25) {
120 current_fps = static_cast<double>(fps_frame_count) / elapsed;
121 fps_frame_count = 0;
122 fps_sample_time = now;
123 fps_text = std::format("FPS: {:.1f}", current_fps);
124 }
125
126 printText(fps_text, 15, 15, SDL_Color{255, 255, 255, 255});
127 }
128
129 public:
130 ExampleWindow(const Arguments &args, const std::string &text) : mxvk::VK_Window(text, args.width, args.height, args.fullscreen, MXVK_VALIDATION, args.enable_vsync) {
131 current_path = (args.path.empty() || args.path == ".") ? std::string(opencv_example_ASSET_DIR) : args.path;
132 shader_path = args.shaderPath.empty() ? current_path + "/data" : args.shaderPath;
133 setFont(current_path + "/data/font.ttf", 20);
134 input_filename = args.filename;
135 camera_index = args.camera_index;
136 using_file = !input_filename.empty();
137 if (!openCaptureSource()) {
138 if (using_file) {
139 throw mxvk::Exception(std::format("opencv_example: failed to open video file '{}'", input_filename));
140 }
141 throw mxvk::Exception(std::format("opencv_example: failed to open camera index {}", camera_index));
142 }
143 fallback_width = args.width;
144 fallback_height = args.height;
145 if (!using_file) {
146 capture.set(cv::CAP_PROP_FRAME_WIDTH, fallback_width);
147 capture.set(cv::CAP_PROP_FRAME_HEIGHT, fallback_height);
148 fallback_width = capture.get(cv::CAP_PROP_FRAME_WIDTH);
149 fallback_height = capture.get(cv::CAP_PROP_FRAME_HEIGHT);
150 fallback_height = args.height;
151 fps = configureCameraFps();
152 } else {
153 fps = capture.get(cv::CAP_PROP_FPS);
154 }
155 std::cout << "mxvk_cv: Capture opened at: " << fallback_width << "x" << fallback_height << " @ " << fps << " fps\n";
156 initializeCameraRendering();
157 }
158
159 ~ExampleWindow() override {
160 capture.close();
161 }
162
163 void event(SDL_Event &e) override {
164 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
165 exit();
166 }
167 }
168
169 void onSwapchainRecreated() override {
170 initializeCameraRendering();
171 }
172
173 void proc() override {
174 if (camera_sprite == nullptr || !capture.readToSprite(*camera_sprite)) {
175 if (using_file) {
176 capture.close();
177 if (openCaptureSource()) {
178 if (camera_sprite != nullptr && !capture.readToSprite(*camera_sprite)) {
179 std::cerr << "opencv_example: failed to upload restarted stream frame\n";
180 }
181 }
182 } else {
183 fps = configureCameraFps();
184 }
185 return;
186 }
187
188 int target_w = fallback_width;
189 int target_h = fallback_height;
190 if (swapchain_extent.width > 0U && swapchain_extent.height > 0U) {
191 target_w = static_cast<int>(swapchain_extent.width);
192 target_h = static_cast<int>(swapchain_extent.height);
193 }
194
195 if (camera_sprite) {
196 camera_sprite->drawSpriteRect(0, 0, target_w, target_h);
197 }
198 updateFpsOverlay();
199 }
200 };
201} // namespace example
202
203int main(int argc, char **argv) {
204 try {
205 Arguments args = proc_args(argc, argv);
206 example::ExampleWindow ex_window(args, "OpenCV Example");
207 ex_window.loop();
208 } catch (mxvk::Exception &e) {
209 std::cerr << std::format("mxvk: Exception: {}\n", e.text());
210 return EXIT_FAILURE;
211 } catch (ArgException<std::string> &e) {
212 std::cerr << std::format("mxvk: Argument Exception: {}\n", e.text());
213 }
214 return EXIT_SUCCESS;
215}
Lightweight, header-only, template command-line argument parser.
Arguments proc_args(int &argc, char **argv)
Parse standard libmx2 command-line options from main()'s argv.
Definition argz.hpp:872
Exception thrown by Argz::proc() on unrecognised or malformed options.
Definition argz.hpp:178
ExampleWindow(const Arguments &args, const std::string &text)
Definition main.cpp:130
void onSwapchainRecreated() override
Called after swapchain and render resources are recreated.
Definition main.cpp:169
void event(SDL_Event &e) override
Handle one SDL event.
Definition main.cpp:163
void proc() override
Execute one processing/update step.
Definition main.cpp:173
~ExampleWindow() override
Definition main.cpp:159
std::string text() const
Vulkan OpenCV video capture source.
Definition mxvk_cv.hpp:30
bool open(const std::string &filename)
Open a video file.
Definition mxvk_cv.cpp:14
double get(unsigned int option)
Query a VideoCapture property.
Definition mxvk_cv.cpp:485
bool readToSprite(VK_Sprite &targetSprite)
Definition mxvk_cv.cpp:369
void set(unsigned int option, double value)
Set a VideoCapture property.
Definition mxvk_cv.cpp:481
void updateTexture(SDL_Surface *surface)
Replace the sprite texture from an SDL_Surface.
void createEmptySprite(int width, int height, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Create a blank (un-initialised) sprite texture.
Main Vulkan window wrapper for MXVK.
Definition mxvk.hpp:37
VkSwapchainKHR swapchain
Definition mxvk.hpp:492
void loop()
Run the main event/render loop.
Definition mxvk.cpp:600
VK_Sprite * createSprite(const std::string &pngPath, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Create a sprite from a PNG file and register it with this window.
Definition mxvk.cpp:3477
VkExtent2D swapchain_extent
Definition mxvk.hpp:495
void createDevice()
Create final device resources.
Definition mxvk.cpp:1645
void exit()
Request loop termination.
Definition mxvk.cpp:1126
VkCommandPool command_pool
Definition mxvk.hpp:504
VK_Window()=default
Construct an empty window object.
void setFont(const std::string &fontPath, int fontSize=24)
Set the active text-render font.
Definition mxvk.cpp:2991
void printText(const std::string &text, int x, int y, const SDL_Color &col)
Queue a text string for rendering during the current frame.
Definition mxvk.cpp:3018
int main(void)
Definition main.cpp:7
#define opencv_example_ASSET_DIR
Definition main.cpp:14
#define MXVK_VALIDATION
Definition mxvk.hpp:27
OpenCV video-capture integration for the Vulkan backend.
Utilities for loading and saving PNG images.
Definition mxvk.hpp:30
Plain data structure returned by proc_args() with all common libmx2 CLI options.
Definition argz.hpp:730
std::string shaderPath
Optional SPV shader folder path (-S / --shader-path).
Definition argz.hpp:765
int camera_index
Optional camera index.
Definition argz.hpp:767
int height
Viewport height in pixels (default: 720).
Definition argz.hpp:733
std::string filename
Optional input filename (--filename).
Definition argz.hpp:738
std::string path
Asset root; proc_args() defaults it to the executable directory.
Definition argz.hpp:735
int width
Viewport width in pixels (default: 1280).
Definition argz.hpp:732