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

FFmpeg-backed RGBA video writer. More...

#include <MXWrite/mxwrite.hpp>

Public Member Functions

void close ()
 Close the writer and flush pending packets.
bool get_block_when_full () const
 Check whether the encoder queue blocks instead of dropping frames.
double get_duration () const
 Return the encoded duration in seconds.
int64_t get_frame_count () const
 Return the number of frames submitted to the writer.
bool is_hardware_encode () const
 True when the active encoder backend is hardware (NVENC).
bool is_open () const
 Check whether the writer is currently open.
bool open (const std::string &filename, int width, int height, float fps, const char *crf)
 Open an output file using the legacy CRF string interface.
bool open (const std::string &filename, int width, int height, float fps, const EncodeOptions &opts)
 Open an output file using explicit encoder options.
bool open_ts (const std::string &filename, int width, int height, float fps, const char *crf)
 Open a timestamp-based output stream using the legacy CRF string interface.
bool open_ts (const std::string &filename, int width, int height, float fps, const EncodeOptions &opts)
 Open a timestamp-based output stream using explicit encoder options.
void set_block_when_full (bool value)
 If true, producer threads block when the encoder queue is full instead of dropping frames. Intended for headless/batch transcoding where every input frame must reach the output. Default: false (drop).
void write (void *rgba_buffer)
 Queue a host RGBA frame for immediate-mode encoding.
bool write_cuda_rgba (void *cuda_rgba_buffer, int src_stride, bool bottom_up=false)
 Queue a CUDA RGBA frame for encoding.
void write_hdr_rgba16 (void *rgba16_buffer)
 Write a 16-bit RGBA frame that is already PQ- or HLG-encoded in BT.2020 primaries (8 bytes/pixel: R16,G16,B16,A16, little-endian unsigned normalised).
void write_ts (void *rgba_buffer)
 Queue a host RGBA frame using capture timestamps.
 Writer ()=default
 Construct a closed writer.
 ~Writer ()
 Close the writer on destruction if it is still open.

Detailed Description

FFmpeg-backed RGBA video writer.

The writer accepts host RGBA buffers, optional CUDA device buffers, and 16-bit HDR RGBA buffers. It can encode either frame-by-frame or from timestamped frames, depending on the open mode.

Definition at line 102 of file mxwrite.hpp.

Constructor & Destructor Documentation

◆ Writer()

Writer::Writer ( )
default

Construct a closed writer.

◆ ~Writer()

Writer::~Writer ( )
inline

Close the writer on destruction if it is still open.

Definition at line 197 of file mxwrite.hpp.

197 {
198 if (is_open()) {
199 close();
200 opened = false;
201 }
202 }
bool is_open() const
Check whether the writer is currently open.
Definition mxwrite.hpp:183
void close()
Close the writer and flush pending packets.
Definition mxwrite.cpp:1465

Member Function Documentation

◆ close()

void Writer::close ( )

Close the writer and flush pending packets.

Definition at line 1465 of file mxwrite.cpp.

1465 {
1466 std::lock_guard<std::mutex> lock(writer_mutex);
1467 if (!opened) {
1468 return;
1469 }
1470
1471 stopEncoderThread();
1472
1473 if (stream && stream->duration > 0) {
1474 last_duration = static_cast<double>(stream->duration) * av_q2d(stream->time_base);
1475 } else if (fps_num > 0 && fps_den > 0) {
1476 last_duration = static_cast<double>(frame_count) * static_cast<double>(fps_den) / static_cast<double>(fps_num);
1477 }
1478
1479 av_write_trailer(format_ctx);
1480
1481 if (!(format_ctx->oformat->flags & AVFMT_NOFILE)) {
1482 avio_closep(&format_ctx->pb);
1483 }
1484
1485 av_frame_free(&frameRGBA);
1486 av_frame_free(&frameYUV);
1487 av_frame_free(&frame10);
1488 sws_freeContext(sws_ctx);
1489 av_frame_free(&upload_sw_frame);
1490#ifdef MXWRITE_HAS_CUDA_COPY
1491 // Destroy stream before tearing down FFmpeg CUDA device/frames contexts.
1492 if (cuda_upload_stream) {
1493 cudaStreamSynchronize(cuda_upload_stream);
1494 cudaStreamDestroy(cuda_upload_stream);
1495 cuda_upload_stream = nullptr;
1496 }
1497#endif
1498 avcodec_free_context(&codec_ctx);
1499 av_buffer_unref(&hw_frames_ctx);
1500 av_buffer_unref(&hw_device_ctx);
1501 avformat_free_context(format_ctx);
1502
1503
1504 while (!encode_queue.empty()) {
1505 releaseFrame(encode_queue.front());
1506 encode_queue.pop();
1507 }
1508 opened = false;
1509 format_ctx = nullptr;
1510 codec_ctx = nullptr;
1511 sws_ctx = nullptr;
1512 frameRGBA = nullptr;
1513 frameYUV = nullptr;
1514 frame10 = nullptr;
1515 upload_sw_frame = nullptr;
1516 use_hw_encode = false;
1517 hdr_output = false;
1518 stop_requested = false;
1519}

◆ get_block_when_full()

bool Writer::get_block_when_full ( ) const
inline

Check whether the encoder queue blocks instead of dropping frames.

Definition at line 191 of file mxwrite.hpp.

191{ return block_when_full; }

◆ get_duration()

double Writer::get_duration ( ) const

Return the encoded duration in seconds.

Definition at line 1521 of file mxwrite.cpp.

1521 {
1522 if (!opened && last_duration > 0.0) {
1523 return last_duration;
1524 }
1525 if (stream && stream->duration > 0) {
1526 return static_cast<double>(stream->duration) * av_q2d(stream->time_base);
1527 }
1528 if (fps_num > 0 && fps_den > 0) {
1529 return static_cast<double>(frame_count) * static_cast<double>(fps_den) / static_cast<double>(fps_num);
1530 }
1531 return 0.0;
1532}

◆ get_frame_count()

int64_t Writer::get_frame_count ( ) const
inline

Return the number of frames submitted to the writer.

Definition at line 193 of file mxwrite.hpp.

193{ return frame_count; }

◆ is_hardware_encode()

bool Writer::is_hardware_encode ( ) const
inline

True when the active encoder backend is hardware (NVENC).

Definition at line 185 of file mxwrite.hpp.

185{ return use_hw_encode; }

◆ is_open()

bool Writer::is_open ( ) const
inline

Check whether the writer is currently open.

Definition at line 183 of file mxwrite.hpp.

183{ return opened; }

◆ open() [1/2]

bool Writer::open ( const std::string & filename,
int width,
int height,
float fps,
const char * crf )

Open an output file using the legacy CRF string interface.

Parameters
filenameOutput file path.
widthOutput width in pixels.
heightOutput height in pixels.
fpsOutput frame rate.
crfConstant Rate Factor as a string.
Returns
true on success.

Definition at line 473 of file mxwrite.cpp.

473 {
474 std::lock_guard<std::mutex> lock(writer_mutex);
475 EncodeOptions opts;
476 if (crf && *crf) {
477 try { opts.crf = std::stoi(crf); } catch (...) {}
478 }
479 // Preserve legacy low-latency behaviour for old callers.
480 opts.preset = "ultrafast";
481 opts.tune = "zerolatency";
482 opts.realtime = true;
483 return openInternal(filename, w, h, fps, opts, false);
484}
std::string tune
Optional tuning mode.
Definition mxwrite.hpp:58
bool realtime
Enable low-latency settings.
Definition mxwrite.hpp:61
int crf
Constant Rate Factor.
Definition mxwrite.hpp:59
std::string preset
Encoder preset name.
Definition mxwrite.hpp:57

◆ open() [2/2]

bool Writer::open ( const std::string & filename,
int width,
int height,
float fps,
const EncodeOptions & opts )

Open an output file using explicit encoder options.

Parameters
filenameOutput file path.
widthOutput width in pixels.
heightOutput height in pixels.
fpsOutput frame rate.
optsEncoder configuration.
Returns
true on success.

Definition at line 486 of file mxwrite.cpp.

486 {
487 std::lock_guard<std::mutex> lock(writer_mutex);
488 return openInternal(filename, w, h, fps, opts, false);
489}

◆ open_ts() [1/2]

bool Writer::open_ts ( const std::string & filename,
int width,
int height,
float fps,
const char * crf )

Open a timestamp-based output stream using the legacy CRF string interface.

Parameters
filenameOutput file path.
widthOutput width in pixels.
heightOutput height in pixels.
fpsNominal input frame rate.
crfConstant Rate Factor as a string.
Returns
true on success.

Definition at line 491 of file mxwrite.cpp.

491 {
492 std::lock_guard<std::mutex> lock(writer_mutex);
493 EncodeOptions opts;
494 if (crf && *crf) {
495 try { opts.crf = std::stoi(crf); } catch (...) {}
496 }
497 opts.preset = "ultrafast";
498 opts.tune = "zerolatency";
499 opts.realtime = true;
500 return openInternal(filename, w, h, fps, opts, true);
501}

◆ open_ts() [2/2]

bool Writer::open_ts ( const std::string & filename,
int width,
int height,
float fps,
const EncodeOptions & opts )

Open a timestamp-based output stream using explicit encoder options.

Parameters
filenameOutput file path.
widthOutput width in pixels.
heightOutput height in pixels.
fpsNominal input frame rate.
optsEncoder configuration.
Returns
true on success.

Definition at line 503 of file mxwrite.cpp.

503 {
504 std::lock_guard<std::mutex> lock(writer_mutex);
505 return openInternal(filename, w, h, fps, opts, true);
506}

◆ set_block_when_full()

void Writer::set_block_when_full ( bool value)
inline

If true, producer threads block when the encoder queue is full instead of dropping frames. Intended for headless/batch transcoding where every input frame must reach the output. Default: false (drop).

Definition at line 189 of file mxwrite.hpp.

189{ block_when_full = value; }

◆ write()

void Writer::write ( void * rgba_buffer)

Queue a host RGBA frame for immediate-mode encoding.

Parameters
rgba_bufferPointer to tightly packed RGBA8 pixels.

Definition at line 1002 of file mxwrite.cpp.

1002 {
1003 if (!rgba_buffer) {
1004 return;
1005 }
1006
1007 {
1008 std::lock_guard<std::mutex> lock(writer_mutex);
1009 if (!opened) {
1010 return;
1011 }
1012 }
1013
1014 AVFrame *queued_frame = av_frame_alloc();
1015 if (!queued_frame) {
1016 std::cerr << "Writer: failed to allocate queued frame\n";
1017 return;
1018 }
1019
1020 if (use_hw_encode) {
1021 queued_frame->format = AV_PIX_FMT_CUDA;
1022 queued_frame->width = width;
1023 queued_frame->height = height;
1024 if (av_hwframe_get_buffer(hw_frames_ctx, queued_frame, 0) < 0) {
1025 std::cerr << "Writer: failed to allocate CUDA frame from hardware pool\n";
1026 releaseFrame(queued_frame);
1027 return;
1028 }
1029
1030 if (av_frame_make_writable(upload_sw_frame) < 0) {
1031 std::cerr << "Writer: software upload frame not writable\n";
1032 releaseFrame(queued_frame);
1033 return;
1034 }
1035
1036 const auto *src = static_cast<const uint8_t *>(rgba_buffer);
1037 for (int y = 0; y < height; ++y) {
1038 std::memcpy(upload_sw_frame->data[0] + static_cast<size_t>(y) * upload_sw_frame->linesize[0],
1039 src + static_cast<size_t>(y) * static_cast<size_t>(width) * 4,
1040 static_cast<size_t>(width) * 4);
1041 }
1042
1043 if (av_hwframe_transfer_data(queued_frame, upload_sw_frame, 0) < 0) {
1044 std::cerr << "Writer: failed to transfer RGBA system frame to CUDA frame\n";
1045 releaseFrame(queued_frame);
1046 return;
1047 }
1048 } else {
1049 queued_frame->format = AV_PIX_FMT_RGBA;
1050 queued_frame->width = width;
1051 queued_frame->height = height;
1052 if (av_frame_get_buffer(queued_frame, 32) < 0) {
1053 std::cerr << "Writer: failed to allocate queued RGBA frame buffer\n";
1054 releaseFrame(queued_frame);
1055 return;
1056 }
1057 if (av_frame_make_writable(queued_frame) < 0) {
1058 std::cerr << "Writer: queued RGBA frame not writable\n";
1059 releaseFrame(queued_frame);
1060 return;
1061 }
1062
1063 const auto *src = static_cast<const uint8_t *>(rgba_buffer);
1064 for (int y = 0; y < height; ++y) {
1065 std::memcpy(queued_frame->data[0] + static_cast<size_t>(y) * queued_frame->linesize[0],
1066 src + static_cast<size_t>(y) * static_cast<size_t>(width) * 4,
1067 static_cast<size_t>(width) * 4);
1068 }
1069 }
1070
1071 {
1072 std::unique_lock<std::mutex> lock(queue_mutex);
1073 if (block_when_full.load(std::memory_order_relaxed)) {
1074 if (encode_queue.size() >= MAX_QUEUE_SIZE) {
1075 // In no-drop mode, once the queue reaches capacity we wait
1076 // until the encoder thread drains it, then continue.
1077 queue_cv.wait(lock, [this] {
1078 return stop_requested || encode_queue.empty();
1079 });
1080 }
1081 if (stop_requested) {
1082 releaseFrame(queued_frame);
1083 return;
1084 }
1085 } else if (stop_requested || encode_queue.size() >= MAX_QUEUE_SIZE) {
1086 static int drop_counter = 0;
1087 if (++drop_counter % 30 == 0) {
1088 std::cerr << "Writer: dropped " << drop_counter << " SDR frames (encoder queue full)\n";
1089 }
1090 releaseFrame(queued_frame);
1091 return;
1092 }
1093 queued_frame->pts = frame_count++;
1094 encode_queue.push(queued_frame);
1095 }
1096
1097 queue_cv.notify_one();
1098}

◆ write_cuda_rgba()

bool Writer::write_cuda_rgba ( void * cuda_rgba_buffer,
int src_stride,
bool bottom_up = false )

Queue a CUDA RGBA frame for encoding.

Parameters
cuda_rgba_bufferCUDA device pointer.
src_strideSource row pitch in bytes.
bottom_upWhether the source is stored bottom-up.
Returns
true if the frame was accepted.

Definition at line 1185 of file mxwrite.cpp.

1185 {
1186 if (!cuda_rgba_buffer || src_stride <= 0) {
1187 return false;
1188 }
1189
1190 {
1191 std::lock_guard<std::mutex> lock(writer_mutex);
1192 if (!opened || !use_hw_encode) {
1193 return false;
1194 }
1195 }
1196
1197 AVFrame *queued_frame = av_frame_alloc();
1198 if (!queued_frame) {
1199 std::cerr << "Writer: failed to allocate queued CUDA frame\n";
1200 return false;
1201 }
1202
1203 queued_frame->format = AV_PIX_FMT_CUDA;
1204 queued_frame->width = width;
1205 queued_frame->height = height;
1206
1207 if (av_hwframe_get_buffer(hw_frames_ctx, queued_frame, 0) < 0) {
1208 std::cerr << "Writer: failed to allocate CUDA frame from hardware pool\n";
1209 releaseFrame(queued_frame);
1210 return false;
1211 }
1212
1213#ifdef MXWRITE_HAS_CUDA_COPY
1214 cudaStream_t stream = cuda_upload_stream;
1215 const bool use_async_stream = (stream != nullptr);
1216 if (!bottom_up) {
1217 const auto copy_err = use_async_stream
1218 ? cudaMemcpy2DAsync(
1219 queued_frame->data[0],
1220 static_cast<size_t>(queued_frame->linesize[0]),
1221 cuda_rgba_buffer,
1222 static_cast<size_t>(src_stride),
1223 static_cast<size_t>(width) * 4,
1224 static_cast<size_t>(height),
1225 cudaMemcpyDeviceToDevice,
1226 stream)
1227 : cudaMemcpy2D(
1228 queued_frame->data[0],
1229 static_cast<size_t>(queued_frame->linesize[0]),
1230 cuda_rgba_buffer,
1231 static_cast<size_t>(src_stride),
1232 static_cast<size_t>(width) * 4,
1233 static_cast<size_t>(height),
1234 cudaMemcpyDeviceToDevice);
1235
1236 if (copy_err != cudaSuccess) {
1237 std::cerr << "Writer: cudaMemcpy2D device upload failed: " << cudaGetErrorString(copy_err) << "\n";
1238 releaseFrame(queued_frame);
1239 return false;
1240 }
1241 } else {
1242 // Flip vertically by issuing one async row copy per destination row
1243 // onto a single stream — kept asynchronous so launch overhead overlaps
1244 // and the producer thread only blocks once at cudaStreamSynchronize.
1245 auto *src_base = static_cast<unsigned char *>(cuda_rgba_buffer);
1246 auto *dst_base = queued_frame->data[0];
1247 const size_t row_bytes = static_cast<size_t>(width) * 4;
1248
1249 for (int y = 0; y < height; ++y) {
1250 auto *src_row = src_base + static_cast<size_t>(height - 1 - y) * static_cast<size_t>(src_stride);
1251 auto *dst_row = dst_base + static_cast<size_t>(y) * static_cast<size_t>(queued_frame->linesize[0]);
1252 const auto row_copy_err = use_async_stream
1253 ? cudaMemcpyAsync(dst_row, src_row, row_bytes, cudaMemcpyDeviceToDevice, stream)
1254 : cudaMemcpy(dst_row, src_row, row_bytes, cudaMemcpyDeviceToDevice);
1255 if (row_copy_err != cudaSuccess) {
1256 std::cerr << "Writer: cudaMemcpy row upload failed: " << cudaGetErrorString(row_copy_err) << "\n";
1257 releaseFrame(queued_frame);
1258 return false;
1259 }
1260 }
1261 }
1262 // Single synchronisation point — NVENC requires the data to be ready when
1263 // avcodec_send_frame() reads it, and the encoder thread is decoupled by
1264 // the queue so this sync only serialises this one producer call.
1265 if (use_async_stream) {
1266 const auto sync_err = cudaStreamSynchronize(stream);
1267 if (sync_err != cudaSuccess) {
1268 std::cerr << "Writer: cudaStreamSynchronize failed: " << cudaGetErrorString(sync_err) << "\n";
1269 releaseFrame(queued_frame);
1270 return false;
1271 }
1272 }
1273#else
1274 std::cerr << "Writer: CUDA copy support disabled at build time\n";
1275 releaseFrame(queued_frame);
1276 return false;
1277#endif
1278
1279 {
1280 std::unique_lock<std::mutex> lock(queue_mutex);
1281 if (block_when_full.load(std::memory_order_relaxed)) {
1282 if (encode_queue.size() >= MAX_QUEUE_SIZE) {
1283 queue_cv.wait(lock, [this] {
1284 return stop_requested || encode_queue.empty();
1285 });
1286 }
1287 if (stop_requested) {
1288 releaseFrame(queued_frame);
1289 return false;
1290 }
1291 } else if (stop_requested || encode_queue.size() >= MAX_QUEUE_SIZE) {
1292 static int drop_counter = 0;
1293 if (++drop_counter % 30 == 0) {
1294 std::cerr << "Writer: dropped " << drop_counter << " frames (encoder queue full)\n";
1295 }
1296 releaseFrame(queued_frame);
1297 // Return TRUE so the producer does NOT fall back to the slow CPU
1298 // write() path — we already "handled" the frame (by dropping it).
1299 // Falling back would double-process every frame and double the drops.
1300 queue_cv.notify_one();
1301 return true;
1302 }
1303 queued_frame->pts = frame_count++;
1304 encode_queue.push(queued_frame);
1305 }
1306
1307 queue_cv.notify_one();
1308 return true;
1309}

◆ write_hdr_rgba16()

void Writer::write_hdr_rgba16 ( void * rgba16_buffer)

Write a 16-bit RGBA frame that is already PQ- or HLG-encoded in BT.2020 primaries (8 bytes/pixel: R16,G16,B16,A16, little-endian unsigned normalised).

The data is expected to originate from the HDR GPU encode pass: it is BT.2020 primaries with a non-linear PQ (or HLG) transfer already applied, so this path skips colour-space conversion and only performs (a) the BT.2020 non-constant-luminance RGB'->YCbCr' matrix, and (b) 16-bit -> 10-bit limited-range scaling, producing AV_PIX_FMT_YUV420P10LE for libx265 Main10. Requires the writer to have been opened with EncodeOptions::HdrInfo::enabled.

Parameters
rgba16_bufferPointer to tightly packed RGBA16 pixels.

Definition at line 1100 of file mxwrite.cpp.

1100 {
1101 if (!rgba16_buffer) {
1102 return;
1103 }
1104
1105 {
1106 std::lock_guard<std::mutex> lock(writer_mutex);
1107 if (!opened) {
1108 return;
1109 }
1110 if (!hdr_output) {
1111 std::cerr << "Writer: write_hdr_rgba16 called but writer not in HDR mode\n";
1112 return;
1113 }
1114 }
1115
1116 AVFrame *queued_frame = av_frame_alloc();
1117 if (!queued_frame) {
1118 std::cerr << "Writer: failed to allocate queued HDR frame\n";
1119 return;
1120 }
1121 queued_frame->format = AV_PIX_FMT_YUV420P10LE;
1122 queued_frame->width = width;
1123 queued_frame->height = height;
1124 if (av_frame_get_buffer(queued_frame, 32) < 0) {
1125 std::cerr << "Writer: failed to allocate YUV420P10 buffer\n";
1126 releaseFrame(queued_frame);
1127 return;
1128 }
1129 if (av_frame_make_writable(queued_frame) < 0) {
1130 std::cerr << "Writer: queued HDR frame not writable\n";
1131 releaseFrame(queued_frame);
1132 return;
1133 }
1134
1135 // Convert the already-PQ/HLG-encoded 16-bit BT.2020 RGBA into the
1136 // 10-bit limited-range YUV420 plane that libx265 Main10 expects.
1138 reinterpret_cast<const uint16_t *>(rgba16_buffer),
1139 width * 4,
1140 reinterpret_cast<uint16_t *>(queued_frame->data[0]),
1141 queued_frame->linesize[0] / 2,
1142 reinterpret_cast<uint16_t *>(queued_frame->data[1]),
1143 queued_frame->linesize[1] / 2,
1144 reinterpret_cast<uint16_t *>(queued_frame->data[2]),
1145 queued_frame->linesize[2] / 2,
1146 width,
1147 height);
1148
1149 queued_frame->color_primaries = static_cast<AVColorPrimaries>(
1150 hdr_info.color_primaries ? hdr_info.color_primaries : AVCOL_PRI_BT2020);
1151 queued_frame->color_trc = static_cast<AVColorTransferCharacteristic>(
1152 hdr_info.color_trc ? hdr_info.color_trc : AVCOL_TRC_SMPTE2084);
1153 queued_frame->colorspace = static_cast<AVColorSpace>(
1154 hdr_info.color_space ? hdr_info.color_space : AVCOL_SPC_BT2020_NCL);
1155 queued_frame->color_range = static_cast<AVColorRange>(
1156 hdr_info.color_range ? hdr_info.color_range : AVCOL_RANGE_MPEG);
1157
1158 {
1159 std::unique_lock<std::mutex> lock(queue_mutex);
1160 if (block_when_full.load(std::memory_order_relaxed)) {
1161 if (encode_queue.size() >= MAX_QUEUE_SIZE) {
1162 queue_cv.wait(lock, [this] {
1163 return stop_requested || encode_queue.empty();
1164 });
1165 }
1166 if (stop_requested) {
1167 releaseFrame(queued_frame);
1168 return;
1169 }
1170 } else if (stop_requested || encode_queue.size() >= MAX_QUEUE_SIZE) {
1171 static int drop_counter = 0;
1172 if (++drop_counter % 30 == 0) {
1173 std::cerr << "Writer: dropped " << drop_counter << " HDR frames (encoder queue full)\n";
1174 }
1175 releaseFrame(queued_frame);
1176 return;
1177 }
1178 queued_frame->pts = frame_count++;
1179 encode_queue.push(queued_frame);
1180 }
1181
1182 queue_cv.notify_one();
1183}
void convertBt2020Rgba16EncodedToYuv420p10(const uint16_t *rgba, int src_stride_shorts, uint16_t *y_plane, int y_stride_shorts, uint16_t *u_plane, int u_stride_shorts, uint16_t *v_plane, int v_stride_shorts, int width, int height)
Definition mxwrite.cpp:139

◆ write_ts()

void Writer::write_ts ( void * rgba_buffer)

Queue a host RGBA frame using capture timestamps.

Parameters
rgba_bufferPointer to tightly packed RGBA8 pixels.

Definition at line 1311 of file mxwrite.cpp.

1311 {
1312 write(rgba_buffer);
1313}
void write(void *rgba_buffer)
Queue a host RGBA frame for immediate-mode encoding.
Definition mxwrite.cpp:1002

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