MXVK Vulkan Framework 0.24.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
mxvk::VK_Capture Class Reference

Vulkan OpenCV video capture source. More...

#include <mxvk/include/mxvk/mxvk_cv.hpp>

Public Member Functions

void close ()
 Close the capture device.
bool createImage (VkDevice device, VkPhysicalDevice physDev, VkQueue gQueue, VkCommandPool cmdPool, size_t width, size_t height, const std::string &vert, const std::string &frag)
 Allocate the VKSprite and upload the initial texture.
void draw (int x, int y)
 Draw using the sprite's native dimensions.
void draw (int x, int y, int width, int height)
 Draw the current frame at a specified position and size.
double get (unsigned int option)
 Query a VideoCapture property.
VK_SpritegetSprite ()
 Access the backing sprite.
bool is_open () const
 Check whether the capture device is open.
bool open (const std::string &filename)
 Open a video file.
bool open (int id, int mode=0)
 Open a camera device.
VK_Captureoperator= (const VK_Capture &)=delete
VK_Captureoperator= (VK_Capture &&)=delete
bool read ()
 Capture and upload the next video frame.
bool read (cv::Mat &frame)
 Capture the next frame into a cv::Mat.
bool readRgba (cv::Mat &rgba, bool flipY=false)
bool readToModelTexture (VKAbstractModel &model, bool flipY=false)
bool readToSprite (VK_Sprite &targetSprite)
bool readToSprite (VK_Sprite &targetSprite, bool flipY)
bool reload (size_t width, size_t height, const std::string &vert, const std::string &frag)
 Replace the vertex/fragment shaders.
void resetSprite ()
 Destroy the current sprite and its Vulkan resources.
void set (unsigned int option, double value)
 Set a VideoCapture property.
 VK_Capture ()=default
 Default constructor.
 VK_Capture (const VK_Capture &)=delete
 VK_Capture (VK_Capture &&)=delete
 ~VK_Capture ()=default
 Destructor.

Detailed Description

Vulkan OpenCV video capture source.

Opens a video file or camera, decodes frames, and uploads them to a VKSprite texture for Vulkan-accelerated rendering inside a VKWindow.

Definition at line 30 of file mxvk_cv.hpp.

Constructor & Destructor Documentation

◆ VK_Capture() [1/3]

mxvk::VK_Capture::VK_Capture ( )
default

Default constructor.

◆ ~VK_Capture()

mxvk::VK_Capture::~VK_Capture ( )
default

Destructor.

◆ VK_Capture() [2/3]

mxvk::VK_Capture::VK_Capture ( const VK_Capture & )
delete

◆ VK_Capture() [3/3]

mxvk::VK_Capture::VK_Capture ( VK_Capture && )
delete

Member Function Documentation

◆ close()

void mxvk::VK_Capture::close ( )

Close the capture device.

Definition at line 37 of file mxvk_cv.cpp.

37 {
38 cap.release();
39 std::cout << "mxvk_cv: Capture closed\n";
40 }

◆ createImage()

bool mxvk::VK_Capture::createImage ( VkDevice device,
VkPhysicalDevice physDev,
VkQueue gQueue,
VkCommandPool cmdPool,
size_t width,
size_t height,
const std::string & vert,
const std::string & frag )

Allocate the VKSprite and upload the initial texture.

Parameters
deviceLogical Vulkan device.
physDevPhysical device.
gQueueGraphics queue.
cmdPoolCommand pool.
widthDisplay width.
heightDisplay height.
vertVertex shader path.
fragFragment shader path.
Returns
true on success.

Definition at line 46 of file mxvk_cv.cpp.

48 {
49 sprite = std::make_unique<VK_Sprite>(device, physDev, gQueue, cmdPool);
50 sprite->createEmptySprite(static_cast<int>(width), static_cast<int>(height), vert, frag);
51 sprite->enableExtendedUBO();
52 sprite->rebuildPipeline();
53 std::cout << std::format("mxvk_cv: Sprite created: {}x{}\n", width, height);
54 return true;
55 }

◆ draw() [1/2]

void mxvk::VK_Capture::draw ( int x,
int y )

Draw using the sprite's native dimensions.

Parameters
xDestination X.
yDestination Y.

Definition at line 476 of file mxvk_cv.cpp.

476 {
477 if (sprite)
478 sprite->drawSprite(x, y);
479 }

◆ draw() [2/2]

void mxvk::VK_Capture::draw ( int x,
int y,
int width,
int height )

Draw the current frame at a specified position and size.

Parameters
xWidth destination X.
yWidth destination Y.
widthDisplay width.
heightDisplay height.

Definition at line 471 of file mxvk_cv.cpp.

471 {
472 if (sprite)
473 sprite->drawSpriteRect(x, y, width, height);
474 }

◆ get()

double mxvk::VK_Capture::get ( unsigned int option)

Query a VideoCapture property.

Parameters
optioncv::VideoCaptureProperties constant.
Returns
Current value.

Definition at line 485 of file mxvk_cv.cpp.

485 {
486 return cap.get(option);
487 }

◆ getSprite()

VK_Sprite * mxvk::VK_Capture::getSprite ( )
inline

Access the backing sprite.

Returns
Pointer to the VKSprite (may be null before createImage).

Definition at line 93 of file mxvk_cv.hpp.

93{ return sprite.get(); }

◆ is_open()

bool mxvk::VK_Capture::is_open ( ) const
inline

Check whether the capture device is open.

Returns
true if the VideoCapture is opened.

Definition at line 71 of file mxvk_cv.hpp.

71{ return cap.isOpened(); }

◆ open() [1/2]

bool mxvk::VK_Capture::open ( const std::string & filename)

Open a video file.

Parameters
filenamePath to the video file.
Returns
true on success.

Definition at line 14 of file mxvk_cv.cpp.

14 {
15 const bool ok = cap.open(filename);
16 if (ok)
17 std::cout << std::format("mxvk_cv: Opened file: {}\n", filename);
18 else
19 std::cout << std::format("mxvk_cv: Failed to open file: {}\n", filename);
20 return ok;
21 }

◆ open() [2/2]

bool mxvk::VK_Capture::open ( int id,
int mode = 0 )

Open a camera device.

Parameters
idCamera index.
modeBackend hint (0 = auto).
Returns
true on success.

Definition at line 23 of file mxvk_cv.cpp.

23 {
24#ifdef __linux__
25 if (mode == 0)
26 mode = cv::CAP_V4L2;
27#endif
28 if (cap.open(id, mode)) {
29 cap.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M', 'J', 'P', 'G'));
30 std::cout << std::format("mxvk_cv: Opened device: {}\n", id);
31 return true;
32 }
33 std::cout << std::format("mxvk_cv: Failed to open device: {}\n", id);
34 return false;
35 }

◆ operator=() [1/2]

VK_Capture & mxvk::VK_Capture::operator= ( const VK_Capture & )
delete

◆ operator=() [2/2]

VK_Capture & mxvk::VK_Capture::operator= ( VK_Capture && )
delete

◆ read() [1/2]

bool mxvk::VK_Capture::read ( )

Capture and upload the next video frame.

Returns
true if a frame was available.

Definition at line 456 of file mxvk_cv.cpp.

456 {
457#ifdef MXVK_CUDA
458 if (initializeCuda()) {
459 return readCuda();
460 }
461#endif
462 if (!cap.read(frame)) {
463 return false;
464 }
465 cv::Mat rgba;
466 cv::cvtColor(frame, rgba, cv::COLOR_BGR2RGBA);
467 sprite->updateTexture(rgba.ptr(), rgba.cols, rgba.rows, static_cast<int>(rgba.step));
468 return true;
469 }

◆ read() [2/2]

bool mxvk::VK_Capture::read ( cv::Mat & frame)

Capture the next frame into a cv::Mat.

Parameters
frameOutput matrix.
Returns
true if a frame was available.

Definition at line 70 of file mxvk_cv.cpp.

70 {
71 return cap.read(frame);
72 }

◆ readRgba()

bool mxvk::VK_Capture::readRgba ( cv::Mat & rgba,
bool flipY = false )

Definition at line 74 of file mxvk_cv.cpp.

74 {
75#ifdef MXVK_CUDA
76 cv::Mat cpuFallbackFrame;
77
78 try {
79 if (initializeCuda()) {
80 if (cudaMappedInput) {
81 if (!cap.read(mappedFrame) || mappedFrame.empty()) {
82 return false;
83 }
84 cpuFallbackFrame = host_mem_mat_header(mappedFrame);
85 const cv::cuda::GpuMat mappedInput = host_mem_gpu_header(mappedFrame);
86 cv::cuda::cvtColor(mappedInput, gpuRgba, cv::COLOR_BGR2RGBA, 0, cuda_stream);
87 } else {
88 if (!cap.read(frame) || frame.empty()) {
89 return false;
90 }
91 cpuFallbackFrame = frame;
92 gpuFrame.upload(frame, cuda_stream);
93 cv::cuda::cvtColor(gpuFrame, gpuRgba, cv::COLOR_BGR2RGBA, 0, cuda_stream);
94 }
95
96 if (flipY) {
97 cv::cuda::flip(gpuRgba, gpuVulkanRgba, 0, cuda_stream);
98 } else {
99 gpuVulkanRgba = gpuRgba;
100 }
101
102 if (!cudaPipelineLogged) {
103 std::cout << std::format(
104 "mxvk_cv: CUDA RGBA path active: capture -> GpuMat -> cv::cuda::cvtColor(BGR to RGBA) -> {} -> download\n",
105 flipY ? "cv::cuda::flip(Y)" : "no Y flip");
106 cudaPipelineLogged = true;
107 }
108
109 pinnedRgba.create(gpuVulkanRgba.size(), gpuVulkanRgba.type());
110 gpuVulkanRgba.download(pinnedRgba, cuda_stream);
111 cuda_stream.waitForCompletion();
112 rgba = host_mem_mat_header(pinnedRgba);
113 return true;
114 }
115 } catch (const cv::Exception &e) {
116 std::cout << std::format("mxvk_cv: CUDA RGBA path failed; falling back to CPU path: {}\n", e.what());
117 cudaAvailable = false;
118 if (cpuFallbackFrame.empty()) {
119 return false;
120 }
121 cv::cvtColor(cpuFallbackFrame, rgba, cv::COLOR_BGR2RGBA);
122 if (flipY) {
123 cv::flip(rgba, rgba, 0);
124 }
125 return true;
126 }
127#endif
128
129 if (!cap.read(frame) || frame.empty()) {
130 return false;
131 }
132 cv::cvtColor(frame, rgba, cv::COLOR_BGR2RGBA);
133 if (flipY) {
134 cv::flip(rgba, rgba, 0);
135 }
136 return true;
137 }

◆ readToModelTexture()

bool mxvk::VK_Capture::readToModelTexture ( VKAbstractModel & model,
bool flipY = false )

Definition at line 298 of file mxvk_cv.cpp.

298 {
299#ifdef MXVK_CUDA
300 if (initializeCuda()) {
301 cv::Mat cpuFallbackFrame;
302
303 try {
304 if (cudaMappedInput) {
305 if (!cap.read(mappedFrame) || mappedFrame.empty()) {
306 return false;
307 }
308 cpuFallbackFrame = host_mem_mat_header(mappedFrame);
309 const cv::cuda::GpuMat mappedInput = host_mem_gpu_header(mappedFrame);
310 cv::cuda::cvtColor(mappedInput, gpuRgba, cv::COLOR_BGR2RGBA, 0, cuda_stream);
311 } else {
312 if (!cap.read(frame) || frame.empty()) {
313 return false;
314 }
315 cpuFallbackFrame = frame;
316 gpuFrame.upload(frame, cuda_stream);
317 cv::cuda::cvtColor(gpuFrame, gpuRgba, cv::COLOR_BGR2RGBA, 0, cuda_stream);
318 }
319
320 if (flipY) {
321 cv::cuda::flip(gpuRgba, gpuVulkanRgba, 0, cuda_stream);
322 } else {
323 gpuVulkanRgba = gpuRgba;
324 }
325
326 if (model.updatePrimaryTextureCuda(gpuVulkanRgba, cuda_stream)) {
327 if (!cudaPipelineLogged) {
328 std::cout << std::format(
329 "mxvk_cv: CUDA interop active for model texture: RGBA GpuMat -> {} -> Vulkan external-memory image array (no download)\n",
330 flipY ? "cv::cuda::flip(Y)" : "no Y flip");
331 cudaPipelineLogged = true;
332 }
333 return true;
334 }
335
336 if (!cudaPipelineLogged) {
337 std::cout << "mxvk_cv: CUDA interop unavailable for model texture; using pinned CUDA download -> Vulkan staging upload\n";
338 cudaPipelineLogged = true;
339 }
340 pinnedRgba.create(gpuVulkanRgba.size(), gpuVulkanRgba.type());
341 gpuVulkanRgba.download(pinnedRgba, cuda_stream);
342 cuda_stream.waitForCompletion();
343
344 pinnedRgbaMat = host_mem_mat_header(pinnedRgba);
345 return model.updatePrimaryTexture(pinnedRgbaMat.ptr(), pinnedRgbaMat.cols, pinnedRgbaMat.rows,
346 static_cast<int>(pinnedRgbaMat.step));
347 } catch (const cv::Exception &e) {
348 std::cout << std::format("mxvk_cv: CUDA model-texture path failed; falling back to CPU path: {}\n", e.what());
349 cudaAvailable = false;
350 if (cpuFallbackFrame.empty()) {
351 return false;
352 }
353 cv::Mat rgba;
354 cv::cvtColor(cpuFallbackFrame, rgba, cv::COLOR_BGR2RGBA);
355 if (flipY) {
356 cv::flip(rgba, rgba, 0);
357 }
358 return model.updatePrimaryTexture(rgba.ptr(), rgba.cols, rgba.rows, static_cast<int>(rgba.step));
359 }
360 }
361#endif
362 cv::Mat rgba;
363 if (!readRgba(rgba, flipY)) {
364 return false;
365 }
366 return model.updatePrimaryTexture(rgba.ptr(), rgba.cols, rgba.rows, static_cast<int>(rgba.step));
367 }
bool readRgba(cv::Mat &rgba, bool flipY=false)
Definition mxvk_cv.cpp:74

◆ readToSprite() [1/2]

bool mxvk::VK_Capture::readToSprite ( VK_Sprite & targetSprite)

Definition at line 369 of file mxvk_cv.cpp.

369 {
370#ifdef MXVK_CUDA
371 if (initializeCuda()) {
372 return readToSprite(targetSprite, cudaFlipYForVulkan);
373 }
374#endif
375 return readToSprite(targetSprite, false);
376 }
bool readToSprite(VK_Sprite &targetSprite)
Definition mxvk_cv.cpp:369

◆ readToSprite() [2/2]

bool mxvk::VK_Capture::readToSprite ( VK_Sprite & targetSprite,
bool flipY )

Definition at line 378 of file mxvk_cv.cpp.

378 {
379#ifdef MXVK_CUDA
380 if (initializeCuda()) {
381 cv::Mat cpuFallbackFrame;
382
383 try {
384 if (cudaMappedInput) {
385 if (!cap.read(mappedFrame) || mappedFrame.empty()) {
386 return false;
387 }
388 cpuFallbackFrame = host_mem_mat_header(mappedFrame);
389 const cv::cuda::GpuMat mappedInput = host_mem_gpu_header(mappedFrame);
390 cv::cuda::cvtColor(mappedInput, gpuRgba, cv::COLOR_BGR2RGBA, 0, cuda_stream);
391 } else {
392 if (!cap.read(frame) || frame.empty()) {
393 return false;
394 }
395 cpuFallbackFrame = frame;
396 gpuFrame.upload(frame, cuda_stream);
397 cv::cuda::cvtColor(gpuFrame, gpuRgba, cv::COLOR_BGR2RGBA, 0, cuda_stream);
398 }
399
400 if (flipY) {
401 cv::cuda::flip(gpuRgba, gpuVulkanRgba, 0, cuda_stream);
402 } else {
403 gpuVulkanRgba = gpuRgba;
404 }
405
406 if (targetSprite.updateTextureCuda(gpuVulkanRgba, cuda_stream)) {
407 if (!cudaPipelineLogged) {
408 std::cout << std::format(
409 "mxvk_cv: CUDA interop active for external sprite: RGBA GpuMat -> {} -> Vulkan external-memory image\n",
410 flipY ? "cv::cuda::flip(Y)" : "no Y flip");
411 cudaPipelineLogged = true;
412 }
413 return true;
414 }
415
416 if (!cudaPipelineLogged) {
417 std::cout << "mxvk_cv: CUDA interop unavailable for external sprite; using pinned CUDA download -> Vulkan staging upload\n";
418 cudaPipelineLogged = true;
419 }
420 pinnedRgba.create(gpuVulkanRgba.size(), gpuVulkanRgba.type());
421 gpuVulkanRgba.download(pinnedRgba, cuda_stream);
422 cuda_stream.waitForCompletion();
423
424 pinnedRgbaMat = host_mem_mat_header(pinnedRgba);
425 targetSprite.updateTexture(pinnedRgbaMat.ptr(), pinnedRgbaMat.cols, pinnedRgbaMat.rows,
426 static_cast<int>(pinnedRgbaMat.step));
427 return true;
428 } catch (const cv::Exception &e) {
429 std::cout << std::format("mxvk_cv: CUDA external-sprite path failed; falling back to CPU path: {}\n", e.what());
430 cudaAvailable = false;
431 if (cpuFallbackFrame.empty()) {
432 return false;
433 }
434 cv::Mat rgba;
435 cv::cvtColor(cpuFallbackFrame, rgba, cv::COLOR_BGR2RGBA);
436 if (flipY) {
437 cv::flip(rgba, rgba, 0);
438 }
439 targetSprite.updateTexture(rgba.ptr(), rgba.cols, rgba.rows, static_cast<int>(rgba.step));
440 return true;
441 }
442 }
443#endif
444 if (!cap.read(frame) || frame.empty()) {
445 return false;
446 }
447 cv::Mat rgba;
448 cv::cvtColor(frame, rgba, cv::COLOR_BGR2RGBA);
449 if (flipY) {
450 cv::flip(rgba, rgba, 0);
451 }
452 targetSprite.updateTexture(rgba.ptr(), rgba.cols, rgba.rows, static_cast<int>(rgba.step));
453 return true;
454 }

◆ reload()

bool mxvk::VK_Capture::reload ( size_t width,
size_t height,
const std::string & vert,
const std::string & frag )

Replace the vertex/fragment shaders.

Parameters
widthDisplay width.
heightDisplay height.
vertNew vertex shader path.
fragNew fragment shader path.
Returns
true on success.

Definition at line 57 of file mxvk_cv.cpp.

57 {
58 if (sprite) {
59 sprite->createEmptySprite(static_cast<int>(width), static_cast<int>(height), vert, frag);
60 sprite->enableExtendedUBO();
61 std::cout << std::format("mxvk_cv: Shader reloaded: vert={} frag={}\n",
62 std::filesystem::path(vert).filename().string(),
63 std::filesystem::path(frag).filename().string());
64 return true;
65 }
66 std::cout << "mxvk_cv: Reload failed: no sprite\n";
67 return false;
68 }

◆ resetSprite()

void mxvk::VK_Capture::resetSprite ( )

Destroy the current sprite and its Vulkan resources.

Call this before swapchain/command-pool teardown to avoid destroying sprite staging command buffers against an invalid command pool.

Definition at line 42 of file mxvk_cv.cpp.

42 {
43 sprite.reset();
44 }

◆ set()

void mxvk::VK_Capture::set ( unsigned int option,
double value )

Set a VideoCapture property.

Parameters
optioncv::VideoCaptureProperties constant.
valueDesired value.

Definition at line 481 of file mxvk_cv.cpp.

481 {
482 cap.set(option, value);
483 }

The documentation for this class was generated from the following files: