MXVK Vulkan Framework 0.33.1
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 grab ()
 Decode and discard the next capture frame without conversion.
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 480 of file mxvk_cv.cpp.

480 {
481 if (sprite)
482 sprite->drawSprite(x, y);
483 }

◆ 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 475 of file mxvk_cv.cpp.

475 {
476 if (sprite)
477 sprite->drawSpriteRect(x, y, width, height);
478 }

◆ get()

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

Query a VideoCapture property.

Parameters
optioncv::VideoCaptureProperties constant.
Returns
Current value.

Definition at line 489 of file mxvk_cv.cpp.

489 {
490 return cap.get(option);
491 }

◆ 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(); }

◆ grab()

bool mxvk::VK_Capture::grab ( )

Decode and discard the next capture frame without conversion.

Returns
true when the backend advanced to another frame.

Definition at line 74 of file mxvk_cv.cpp.

74 {
75 return cap.grab();
76 }

◆ 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 460 of file mxvk_cv.cpp.

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

◆ 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 78 of file mxvk_cv.cpp.

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

◆ readToModelTexture()

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

Definition at line 302 of file mxvk_cv.cpp.

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

◆ readToSprite() [1/2]

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

Definition at line 373 of file mxvk_cv.cpp.

373 {
374#ifdef MXVK_CUDA
375 if (initializeCuda()) {
376 return readToSprite(targetSprite, cudaFlipYForVulkan);
377 }
378#endif
379 return readToSprite(targetSprite, false);
380 }
bool readToSprite(VK_Sprite &targetSprite)
Definition mxvk_cv.cpp:373

◆ readToSprite() [2/2]

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

Definition at line 382 of file mxvk_cv.cpp.

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

◆ 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 485 of file mxvk_cv.cpp.

485 {
486 cap.set(option, value);
487 }

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