22
22
23
23
// ----
24
24
25
+ bool g_multithreaded = false;
26
+ bool g_print_decode_time = false;
27
+
28
+ // ----
29
+
30
+ typedef struct worker_data_struct {
31
+ // Request.
32
+ const uint8_t * src_ptr ;
33
+ size_t src_len ;
34
+ qoir_decode_options * options ;
35
+ int32_t y0 ;
36
+ int32_t y1 ;
37
+
38
+ // Response.
39
+ const char * status_message ;
40
+ } worker_data ;
41
+
42
+ int //
43
+ work (void * data ) {
44
+ worker_data * wd = (worker_data * )data ;
45
+ qoir_decode_options opts = {0 };
46
+ memcpy (& opts , wd -> options , sizeof (* wd -> options ));
47
+ if (!opts .pixbuf .data ) {
48
+ wd -> status_message =
49
+ "main: inconsistent qoir_decode_options.pixbuf.data field\n" ;
50
+ return 0 ;
51
+ }
52
+ if (opts .src_clip_rectangle .y0 < wd -> y0 ) {
53
+ opts .src_clip_rectangle .y0 = wd -> y0 ;
54
+ }
55
+ if (opts .src_clip_rectangle .y1 > wd -> y1 ) {
56
+ opts .src_clip_rectangle .y1 = wd -> y1 ;
57
+ }
58
+
59
+ qoir_decode_result res = qoir_decode (wd -> src_ptr , wd -> src_len , & opts );
60
+ if (res .owned_memory ) {
61
+ free (res .owned_memory );
62
+ wd -> status_message =
63
+ "main: inconsistent qoir_decode_result.owned_memory field\n" ;
64
+ } else {
65
+ wd -> status_message = res .status_message ;
66
+ }
67
+ return 0 ;
68
+ }
69
+
70
+ qoir_decode_result //
71
+ multithreaded_decode ( //
72
+ const uint8_t * src_ptr , //
73
+ const size_t src_len , //
74
+ const qoir_decode_options * options ) {
75
+ qoir_decode_pixel_configuration_result config =
76
+ qoir_decode_pixel_configuration (src_ptr , src_len );
77
+ if (config .status_message ) {
78
+ qoir_decode_result res = {0 };
79
+ res .status_message = config .status_message ;
80
+ return res ;
81
+ }
82
+
83
+ uint32_t height_in_tiles =
84
+ qoir_calculate_number_of_tiles_1d (config .dst_pixcfg .height_in_pixels );
85
+ if (height_in_tiles <= 1 ) {
86
+ return qoir_decode (src_ptr , src_len , options );
87
+ }
88
+
89
+ uint64_t pixbuf_len = 4 * (uint64_t )config .dst_pixcfg .width_in_pixels *
90
+ (uint64_t )config .dst_pixcfg .height_in_pixels ;
91
+ if (pixbuf_len > SIZE_MAX ) {
92
+ qoir_decode_result res = {0 };
93
+ res .status_message =
94
+ qoir_status_message__error_unsupported_pixbuf_dimensions ;
95
+ return res ;
96
+ }
97
+
98
+ qoir_decode_options opts = {0 };
99
+ if (options ) {
100
+ memcpy (& opts , options , sizeof (* options ));
101
+ }
102
+
103
+ opts .pixbuf .pixcfg .pixfmt = QOIR_PIXEL_FORMAT__BGRA_PREMUL ;
104
+ opts .pixbuf .pixcfg .width_in_pixels = config .dst_pixcfg .width_in_pixels ;
105
+ opts .pixbuf .pixcfg .height_in_pixels = config .dst_pixcfg .height_in_pixels ;
106
+ opts .pixbuf .data = malloc (pixbuf_len );
107
+ opts .pixbuf .stride_in_bytes = 4 * opts .pixbuf .pixcfg .width_in_pixels ;
108
+ if (!opts .pixbuf .data ) {
109
+ qoir_decode_result res = {0 };
110
+ res .status_message = qoir_status_message__error_out_of_memory ;
111
+ return res ;
112
+ }
113
+
114
+ if (!opts .use_src_clip_rectangle ) {
115
+ opts .use_src_clip_rectangle = true;
116
+ opts .src_clip_rectangle =
117
+ qoir_make_rectangle (0 , 0 , (int32_t )config .dst_pixcfg .width_in_pixels ,
118
+ (int32_t )config .dst_pixcfg .height_in_pixels );
119
+ }
120
+
121
+ const char * status_message = NULL ;
122
+ uint32_t num_threads = height_in_tiles ;
123
+ if (num_threads > 16 ) {
124
+ num_threads = 16 ;
125
+ }
126
+ worker_data data [16 ] = {0 };
127
+ SDL_Thread * threads [16 ] = {0 };
128
+
129
+ for (uint32_t i = 0 ; i < num_threads ; i ++ ) {
130
+ data [i ].src_ptr = src_ptr ;
131
+ data [i ].src_len = src_len ;
132
+ data [i ].options = & opts ;
133
+ data [i ].y0 = (((i + 0 ) * height_in_tiles ) / num_threads ) * QOIR_TILE_SIZE ;
134
+ data [i ].y1 = (((i + 1 ) * height_in_tiles ) / num_threads ) * QOIR_TILE_SIZE ;
135
+ threads [i ] = SDL_CreateThread (& work , "worker" , & data [i ]);
136
+ if (!threads [i ]) {
137
+ status_message = "main: could not create thread" ;
138
+ }
139
+ }
140
+
141
+ for (uint32_t i = 0 ; i < num_threads ; i ++ ) {
142
+ if (threads [i ]) {
143
+ SDL_WaitThread (threads [i ], NULL );
144
+ if (!status_message ) {
145
+ status_message = data [i ].status_message ;
146
+ }
147
+ }
148
+ }
149
+ if (status_message ) {
150
+ free (opts .pixbuf .data );
151
+ qoir_decode_result res = {0 };
152
+ res .status_message = status_message ;
153
+ return res ;
154
+ }
155
+ qoir_decode_result res = {0 };
156
+ res .owned_memory = opts .pixbuf .data ;
157
+ memcpy (& res .dst_pixbuf , & opts .pixbuf , sizeof (opts .pixbuf ));
158
+ return res ;
159
+ }
160
+
161
+ // ----
162
+
25
163
SDL_Surface * //
26
164
load (const char * filename , void * * owned_memory ) {
27
165
SDL_RWops * rw = SDL_RWFromFile (filename , "rb" );
@@ -60,9 +198,12 @@ load(const char* filename, void** owned_memory) {
60
198
return NULL ;
61
199
}
62
200
201
+ uint64_t now = SDL_GetPerformanceCounter ();
63
202
qoir_decode_options opts = {0 };
64
203
opts .pixfmt = QOIR_PIXEL_FORMAT__BGRA_PREMUL ;
65
- qoir_decode_result decode = qoir_decode (ptr , len , & opts );
204
+ qoir_decode_result decode = g_multithreaded
205
+ ? multithreaded_decode (ptr , len , & opts )
206
+ : qoir_decode (ptr , len , & opts );
66
207
free (ptr );
67
208
ptr = NULL ;
68
209
len = 0 ;
@@ -72,6 +213,11 @@ load(const char* filename, void** owned_memory) {
72
213
decode .status_message );
73
214
return NULL ;
74
215
}
216
+ if (g_print_decode_time ) {
217
+ uint64_t elapsed = SDL_GetPerformanceCounter () - now ;
218
+ printf ("%" PRIu64 " microseconds to decode.\n" ,
219
+ (elapsed * 1000000 ) / SDL_GetPerformanceFrequency ());
220
+ }
75
221
76
222
* owned_memory = decode .owned_memory ;
77
223
return SDL_CreateRGBSurfaceFrom (
@@ -116,8 +262,29 @@ draw(SDL_Window* window, SDL_Surface* surface) {
116
262
117
263
int //
118
264
main (int argc , char * * argv ) {
119
- if (argc != 2 ) {
120
- fprintf (stderr , "usage: %s filename\n" , argv [0 ]);
265
+ const char * filename = NULL ;
266
+ bool too_many_args = false;
267
+ for (int i = 1 ; i < argc ; i ++ ) {
268
+ const char * arg = argv [i ];
269
+ if ((arg [0 ] == '-' ) && (arg [1 ] == '-' )) {
270
+ arg ++ ;
271
+ }
272
+ if (strcmp (arg , "-multithreaded" ) == 0 ) {
273
+ g_multithreaded = true;
274
+ continue ;
275
+ } else if (strcmp (arg , "-print-decode-time" ) == 0 ) {
276
+ g_print_decode_time = true;
277
+ continue ;
278
+ } else if (filename == NULL ) {
279
+ filename = argv [i ];
280
+ continue ;
281
+ }
282
+ too_many_args = true;
283
+ }
284
+
285
+ if (too_many_args || (filename == NULL )) {
286
+ fprintf (stderr , "usage: %s -print-decode-time -multithreaded filename\n" ,
287
+ argv [0 ]);
121
288
return 1 ;
122
289
}
123
290
if (SDL_Init (SDL_INIT_VIDEO ) < 0 ) {
@@ -132,7 +299,7 @@ main(int argc, char** argv) {
132
299
return 1 ;
133
300
}
134
301
void * surface_owned_memory = NULL ;
135
- SDL_Surface * surface = load (argv [ 1 ] , & surface_owned_memory );
302
+ SDL_Surface * surface = load (filename , & surface_owned_memory );
136
303
if (!surface ) {
137
304
return 1 ;
138
305
}
0 commit comments