UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
gpujpeg_to_dxt.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2012-2023, CESNET z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifdef HAVE_CONFIG_H
39#include "config.h"
40#include "config_unix.h"
41#include "config_win32.h"
42#endif // HAVE_CONFIG_H
43
44#include "cuda_dxt/cuda_dxt.h"
45
46#include <pthread.h>
47#include <stdlib.h>
48
49#include "libgpujpeg/gpujpeg_decoder.h"
50#include "libgpujpeg/gpujpeg_version.h"
51
52#include "debug.h"
53#include "lib_common.h"
54#include "host.h"
56#include "video.h"
57#include "video_decompress.h"
58
59#define MOD_NAME "[GPUJPEG to DXT] "
60
61namespace {
62
63struct thread_data {
64 thread_data() :
65 gpujpeg_decoder(0), desc(), out_codec(), ppb(), dxt_out_buff(0),
66 cuda_dev_index(-1)
67 {}
69 // currently only for output frames
71
72 struct gpujpeg_decoder *gpujpeg_decoder;
73 struct video_desc desc;
74 codec_t out_codec;
75 int ppb;
76 char *dxt_out_buff;
77
78 int cuda_dev_index;
79};
80
81struct msg_reconfigure : public msg {
82 struct video_desc desc;
83 int ppb; // pixels per byte
84 codec_t out_codec;
85};
86
87struct msg_reconfigure_status : public msg {
88 msg_reconfigure_status(bool val) : success(val) {}
89
90 bool success;
91};
92
93struct msg_frame : public msg {
94 msg_frame(int len) {
95 data = new char[len];
96 data_len = len;
97 }
98 ~msg_frame() {
99 delete data;
100 }
101 char *data;
102 int data_len;
103};
104
105struct state_decompresss_gpujpeg_to_dxt {
106 struct thread_data thread_data[MAX_CUDA_DEVICES];
107 pthread_t thread_id[MAX_CUDA_DEVICES];
108
109 struct video_desc desc;
110 int ppb;
111
112 // which card is free to process next image
113 unsigned int free;
114 unsigned int occupied_count; // number of GPUs occupied
115
116};
117
118} // namespace
119
120static int reconfigure_thread(struct thread_data *s, struct video_desc desc, int ppb);
121
122static void *worker_thread(void *arg)
123{
124 struct thread_data *s =
125 (struct thread_data *) arg;
126
127 while(1) {
128 msg *message = s->m_in.pop();
129
130 if (dynamic_cast<msg_quit *>(message)) {
131 delete message;
132 break;
133 } else if (dynamic_cast<msg_reconfigure *>(message)) {
134 msg_reconfigure *reconf = dynamic_cast<msg_reconfigure *>(message);
135 bool ret = reconfigure_thread(s, reconf->desc, reconf->ppb);
136 s->desc = reconf->desc;
137 s->out_codec = reconf->out_codec;
138 s->ppb = reconf->ppb;
139
140 msg_reconfigure_status *status = new msg_reconfigure_status(ret);
141 s->m_out.push(status);
142
143 delete message;
144 } else if (dynamic_cast<msg_frame *>(message)) {
145 msg_frame *frame_msg = dynamic_cast<msg_frame *>(message);
146 struct gpujpeg_decoder_output decoder_output;
147
148 gpujpeg_decoder_output_set_cuda_buffer(&decoder_output);
149 gpujpeg_decoder_decode(s->gpujpeg_decoder, (uint8_t *) frame_msg->data, frame_msg->data_len,
150 &decoder_output);
151
152 if (s->out_codec == DXT1) {
153 cuda_rgb_to_dxt1(decoder_output.data, s->dxt_out_buff, s->desc.width,
154 -s->desc.height, 0);
155 } else {
156 cuda_rgb_to_dxt6(decoder_output.data, s->dxt_out_buff, s->desc.width,
157 -s->desc.height, 0);
158 }
159
160 msg_frame *output_frame = new msg_frame(s->desc.width * s->desc.height / s->ppb);
161
162 if (cuda_wrapper_memcpy((char*) output_frame->data, s->dxt_out_buff,
163 output_frame->data_len,
166 log_msg(LOG_LEVEL_ERROR, MOD_NAME "unable to copy from device.");
167 }
168 s->m_out.push(output_frame);
169
170 delete frame_msg;
171 }
172 }
173
174 if(s->gpujpeg_decoder) {
175 gpujpeg_decoder_destroy(s->gpujpeg_decoder);
176 }
177
178 cuda_wrapper_free(s->dxt_out_buff);
179
180 return NULL;
181}
182
184{
185 struct state_decompresss_gpujpeg_to_dxt *s;
186
187 s = new state_decompresss_gpujpeg_to_dxt;
188
189 s->free = 0;
190 s->occupied_count = 0;
191
192 for(unsigned int i = 0; i < cuda_devices_count; ++i) {
193 int ret = gpujpeg_init_device(cuda_devices[i], TRUE);
194 if(ret != 0) {
195 log_msg(LOG_LEVEL_ERROR, MOD_NAME "initializing CUDA device %d failed.\n", cuda_devices[0]);
196 delete s;
197 return NULL;
198 }
199 }
200
201 for(unsigned int i = 0; i < cuda_devices_count; ++i) {
202 s->thread_data[i].cuda_dev_index = i;
203 int ret = pthread_create(&s->thread_id[i], NULL, worker_thread, &s->thread_data[i]);
204 assert(ret == 0);
205 }
206
207 return s;
208}
209
210static void flush(struct state_decompresss_gpujpeg_to_dxt *s)
211{
212 if (s->occupied_count > 0) {
213 for (unsigned int i = 0; i < cuda_devices_count; ++i) {
214 if (i == s->free)
215 continue;
216 struct msg_frame *completed =
217 dynamic_cast<msg_frame *>(s->thread_data[(s->free+1)%cuda_devices_count].m_out.pop());
218 delete completed;
219 }
220 s->occupied_count = 0;
221 }
222 s->free = 0;
223}
224
232 int rshift, int gshift, int bshift, int pitch, codec_t out_codec)
233{
234 struct state_decompresss_gpujpeg_to_dxt *s = (struct state_decompresss_gpujpeg_to_dxt *) state;
235
236 UNUSED(rshift);
237 UNUSED(gshift);
238 UNUSED(bshift);
239 assert(out_codec == DXT1 || out_codec == DXT5);
240 if(out_codec == DXT1) {
241 s->ppb = 2;
242 } else { // DXT5
243 s->ppb = 1;
244 }
245 s->desc = desc;
246 assert(pitch == (int) desc.width / s->ppb); // default for DXT1
247
248 flush(s);
249
250 for(unsigned int i = 0; i < cuda_devices_count; ++i) {
251 msg_reconfigure *reconf = new msg_reconfigure;
252 reconf->desc = desc;
253 reconf->out_codec = out_codec;
254 reconf->ppb = s->ppb;
255
256 s->thread_data[i].m_in.push(reconf);
257
258 msg_reconfigure_status *status =
259 dynamic_cast<msg_reconfigure_status *>(s->thread_data[i].m_out.pop());
260 assert(status != NULL);
261 if (!status->success) {
262 delete status;
263 return FALSE;
264 }
265 delete status;
266 }
267
268 return TRUE;
269}
270
274static int reconfigure_thread(struct thread_data *s, struct video_desc desc, int ppb)
275{
276 if(s->gpujpeg_decoder != NULL) {
277 gpujpeg_decoder_destroy(s->gpujpeg_decoder);
278 s->gpujpeg_decoder = NULL;
279 } else {
280 gpujpeg_init_device(cuda_devices[s->cuda_dev_index], 0);
281 }
282
283 if(s->dxt_out_buff != NULL) {
284 cuda_wrapper_free(s->dxt_out_buff);
285 s->dxt_out_buff = NULL;
286 }
287
288 if(cuda_wrapper_malloc_host((void **) &s->dxt_out_buff, desc.width * desc.height / ppb)
290 fprintf(stderr, "Could not allocate CUDA output buffer.\n");
291 return false;
292 }
293 //gpujpeg_init_device(cuda_device, GPUJPEG_OPENGL_INTEROPERABILITY);
294
295#if LIBGPUJPEG_API_VERSION <= 2
296 s->gpujpeg_decoder = gpujpeg_decoder_create();
297#else
298 s->gpujpeg_decoder = gpujpeg_decoder_create(NULL);
299#endif
300 if(!s->gpujpeg_decoder) {
301 log_msg(LOG_LEVEL_ERROR, "Creating GPUJPEG decoder failed.\n");
302 return false;
303 }
304
305 return true;
306}
307
308static decompress_status gpujpeg_to_dxt_decompress(void *state, unsigned char *dst, unsigned char *buffer,
309 unsigned int src_len, int frame_seq, video_frame_callbacks * /* callbacks */, codec_t * /* internal_codec */)
310{
311 struct state_decompresss_gpujpeg_to_dxt *s = (struct state_decompresss_gpujpeg_to_dxt *) state;
312 UNUSED(frame_seq);
313
314 msg_frame *message = new msg_frame(src_len);
315 memcpy(message->data, buffer, src_len);
316
317 if(s->occupied_count < cuda_devices_count - 1) {
318 s->thread_data[s->free].m_in.push(message);
319 s->free = s->free + 1; // should not exceed cuda_device_count
320 s->occupied_count += 1;
321 } else {
322 s->thread_data[s->free].m_in.push(message);
323
324 s->free = (s->free + 1) % cuda_devices_count;
325
326 struct msg_frame *completed =
327 dynamic_cast<msg_frame *>(s->thread_data[s->free].m_out.pop());
328 assert(completed != NULL);
329 memcpy(dst, completed->data, s->desc.width * s->desc.height / s->ppb);
330
331 delete completed;
332 }
333
334 return DECODER_GOT_FRAME;
335}
336
337static int gpujpeg_to_dxt_decompress_get_property(void *state, int property, void *val, size_t *len)
338{
339 UNUSED(state);
341 UNUSED(val);
342 UNUSED(len);
343
344 return FALSE;
345}
346
348{
349 struct state_decompresss_gpujpeg_to_dxt *s = (struct state_decompresss_gpujpeg_to_dxt *) state;
350
351 if(!state) {
352 return;
353 }
354
355 flush(s);
356
357 for(unsigned int i = 0; i < cuda_devices_count; ++i) {
358 msg_quit *quit_msg = new msg_quit;
359 s->thread_data[i].m_in.push(quit_msg);
360 }
361
362 for(unsigned int i = 0; i < cuda_devices_count; ++i) {
363 pthread_join(s->thread_id[i], NULL);
364 }
365
366 delete s;
367}
368
369static int gpujpeg_to_dxt_decompress_get_priority(codec_t compression, struct pixfmt_desc internal, codec_t ugc) {
370 return compression == JPEG && (ugc == DXT1 || ugc == DXT5) ? 900 : -1;
371}
372
381
383
384
#define UNUSED(x)
void ** state
int void * val
struct video_desc desc
int void size_t * len
int property
simple blocking synchronized queue
#define TRUE
#define FALSE
int cuda_rgb_to_dxt6(const void *src, void *out, int size_x, int size_y, cuda_wrapper_stream_t stream)
Definition cuda_dxt.cu:816
int cuda_rgb_to_dxt1(const void *src, void *out, int size_x, int size_y, cuda_wrapper_stream_t stream)
Definition cuda_dxt.cu:782
Interface of DXT compression CUDA implementation.
int cuda_wrapper_malloc_host(void **buffer, size_t data_len)
int cuda_wrapper_free(void *buffer)
int cuda_wrapper_memcpy(void *dst, const void *src, size_t count, int kind)
#define CUDA_WRAPPER_SUCCESS
#define CUDA_WRAPPER_MEMCPY_DEVICE_TO_HOST
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
static int gpujpeg_to_dxt_decompress_get_property(void *state, int property, void *val, size_t *len)
static void * worker_thread(void *arg)
static const struct video_decompress_info gpujpeg_to_dxt_info
static int gpujpeg_to_dxt_decompress_get_priority(codec_t compression, struct pixfmt_desc internal, codec_t ugc)
static decompress_status gpujpeg_to_dxt_decompress(void *state, unsigned char *dst, unsigned char *buffer, unsigned int src_len, int frame_seq, video_frame_callbacks *, codec_t *)
static int gpujpeg_to_dxt_decompress_reconfigure(void *state, struct video_desc desc, int rshift, int gshift, int bshift, int pitch, codec_t out_codec)
static void * gpujpeg_to_dxt_decompress_init(void)
static int reconfigure_thread(struct thread_data *s, struct video_desc desc, int ppb)
#define MOD_NAME
static void gpujpeg_to_dxt_decompress_done(void *state)
static void flush(struct state_decompresss_gpujpeg_to_dxt *s)
decompress_status
@ DECODER_GOT_FRAME
unsigned int cuda_devices_count
Definition host.cpp:168
unsigned int cuda_devices[MAX_CUDA_DEVICES]
Definition host.cpp:167
#define MAX_CUDA_DEVICES
Definition host.h:97
@ LIBRARY_CLASS_VIDEO_DECOMPRESS
Definition lib_common.h:81
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
unsigned int width
width of every tile (!)
Definition types.h:179
unsigned int height
height of every tile (!)
Definition types.h:180
Struct containing callbacks of a video_frame.
Definition types.h:230
codec_t
Definition types.h:65
@ DXT1
S3 Texture Compression DXT1.
Definition types.h:77
@ JPEG
JPEG image, restart intervals may be used. Compatible with GPUJPEG.
Definition types.h:81
@ DXT5
S3 Texture Compression DXT5.
Definition types.h:79
This is an umbrella header for video functions.
API for video decompress drivers.
#define VIDEO_DECOMPRESS_ABI_VERSION