1 | /* |
2 | Copyright (c) 2012, Broadcom Europe Ltd |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the following conditions are met: |
7 | * Redistributions of source code must retain the above copyright |
8 | notice, this list of conditions and the following disclaimer. |
9 | * Redistributions in binary form must reproduce the above copyright |
10 | notice, this list of conditions and the following disclaimer in the |
11 | documentation and/or other materials provided with the distribution. |
12 | * Neither the name of the copyright holder nor the |
13 | names of its contributors may be used to endorse or promote products |
14 | derived from this software without specific prior written permission. |
15 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | |
28 | #define VCOS_VERIFY_BKPTS 1 // TODO remove |
29 | #define VCOS_LOG_CATEGORY (&log_cat) |
30 | |
31 | #include "interface/khronos/common/khrn_client.h" |
32 | #include "interface/vcos/vcos.h" |
33 | #ifdef KHRN_FRUIT_DIRECT |
34 | #include "middleware/khronos/egl/egl_server.h" |
35 | #include "middleware/khronos/ext/egl_khr_image.h" |
36 | #include "middleware/khronos/common/khrn_umem.h" |
37 | #endif |
38 | |
39 | #include "interface/khronos/wf/wfc_client_stream.h" |
40 | #include "interface/khronos/wf/wfc_server_api.h" |
41 | |
42 | //#define WFC_FULL_LOGGING |
43 | #ifdef WFC_FULL_LOGGING |
44 | #define WFC_LOG_LEVEL VCOS_LOG_TRACE |
45 | #else |
46 | #define WFC_LOG_LEVEL VCOS_LOG_WARN |
47 | #endif |
48 | |
49 | //============================================================================== |
50 | |
51 | //!@name Stream data block pool sizes |
52 | //!@{ |
53 | #define WFC_STREAM_BLOCK_SIZE (WFC_MAX_STREAMS_PER_CLIENT / 8) |
54 | #define WFC_STREAM_MAX_EXTENSIONS 7 |
55 | #define WFC_STREAM_MAX_STREAMS (WFC_STREAM_BLOCK_SIZE * (WFC_STREAM_MAX_EXTENSIONS + 1)) |
56 | //!@} |
57 | |
58 | //!@name Global lock to protect global data (stream data list, next stream ID) |
59 | //!@{ |
60 | #define GLOBAL_LOCK() do {vcos_once(&wfc_stream_initialise_once, wfc_stream_initialise); vcos_mutex_lock(&wfc_stream_global_lock);} while (0) |
61 | #define GLOBAL_UNLOCK() do {vcos_mutex_unlock(&wfc_stream_global_lock);} while (0) |
62 | //!@} |
63 | |
64 | //!@name Stream-specific mutex. Global lock must already be held when acquiring this lock. |
65 | //!@{ |
66 | #define STREAM_LOCK(stream_ptr) do {vcos_mutex_lock(&stream_ptr->mutex);} while (0) |
67 | #define STREAM_UNLOCK(stream_ptr) do {vcos_mutex_unlock(&stream_ptr->mutex);} while (0) |
68 | //!@} |
69 | |
70 | //! Period in milliseconds to wait for an existing stream handle to be released |
71 | //! when creating a new one. |
72 | #define WFC_STREAM_RETRY_DELAY_MS 1 |
73 | //! Number of attempts allowed to create a stream with a given handle. |
74 | #define WFC_STREAM_RETRIES 50 |
75 | |
76 | //============================================================================== |
77 | |
78 | //! Top-level stream type |
79 | typedef struct WFC_STREAM_tag |
80 | { |
81 | //! Handle; may be assigned by window manager. |
82 | WFCNativeStreamType handle; |
83 | |
84 | //! Number of times this stream has been registered in the process. Creation implies registration |
85 | uint32_t registrations; |
86 | |
87 | //! Flag to indicate entry is no longer in use and imminently due for destruction. |
88 | bool to_be_deleted; |
89 | |
90 | //! Mutex, for thread safety. |
91 | VCOS_MUTEX_T mutex; |
92 | |
93 | //! Configuration info. |
94 | WFC_STREAM_INFO_T info; |
95 | |
96 | //!@brief Image providers to which this stream sends data; recorded so we do |
97 | //! not destroy this stream if it is still associated with a source or mask. |
98 | uint32_t num_of_sources_or_masks; |
99 | //! Record if this stream holds the output from an off-screen context. |
100 | bool used_for_off_screen; |
101 | |
102 | //! Thread for handling server-side request to change source and/or destination rectangles |
103 | VCOS_THREAD_T rect_req_thread_data; |
104 | //! Flag for when thread must terminate |
105 | bool rect_req_terminate; |
106 | //! Callback function notifying calling module |
107 | WFC_STREAM_REQ_RECT_CALLBACK_T req_rect_callback; |
108 | //! Argument to callback function |
109 | void *req_rect_cb_args; |
110 | |
111 | //! Pointer to next stream |
112 | struct WFC_STREAM_tag *next; |
113 | //! Pointer to previous stream |
114 | struct WFC_STREAM_tag *prev; |
115 | } WFC_STREAM_T; |
116 | |
117 | //============================================================================== |
118 | |
119 | //! Blockpool containing all created streams. |
120 | static VCOS_BLOCKPOOL_T wfc_stream_blockpool; |
121 | //! Next stream handle, allocated by wfc_stream_get_next(). |
122 | static WFCNativeStreamType wfc_stream_next_handle = (1 << 31); |
123 | |
124 | static VCOS_LOG_CAT_T log_cat = VCOS_LOG_INIT("wfc_client_stream" , WFC_LOG_LEVEL); |
125 | |
126 | //! Ensure lock and blockpool are only initialised once |
127 | static VCOS_ONCE_T wfc_stream_initialise_once; |
128 | //! The global (process-wide) lock |
129 | static VCOS_MUTEX_T wfc_stream_global_lock; |
130 | //! Pointer to the first stream data block |
131 | static WFC_STREAM_T *wfc_stream_head; |
132 | |
133 | //============================================================================== |
134 | //!@name Static functions |
135 | //!@{ |
136 | static void wfc_stream_initialise(void); |
137 | static WFC_STREAM_T *wfc_stream_global_lock_and_find_stream_ptr(WFCNativeStreamType stream); |
138 | static WFC_STREAM_T *wfc_stream_create_stream_ptr(WFCNativeStreamType stream, bool allow_duplicate); |
139 | static WFC_STREAM_T *wfc_stream_find_stream_ptr(WFCNativeStreamType stream); |
140 | static void wfc_stream_destroy_if_ready(WFC_STREAM_T *stream_ptr); |
141 | static void *wfc_stream_rect_req_thread(void *arg); |
142 | static void wfc_client_stream_post_sem(void *cb_data); |
143 | //!@} |
144 | //============================================================================== |
145 | //!@name Public functions |
146 | //!@{ |
147 | |
148 | WFCNativeStreamType wfc_stream_get_next(void) |
149 | // In cases where the caller doesn't want to assign a stream number, provide |
150 | // one for it. |
151 | { |
152 | GLOBAL_LOCK(); |
153 | |
154 | WFCNativeStreamType next_stream = wfc_stream_next_handle; |
155 | wfc_stream_next_handle++; |
156 | |
157 | GLOBAL_UNLOCK(); |
158 | |
159 | return next_stream; |
160 | } |
161 | |
162 | //------------------------------------------------------------------------------ |
163 | |
164 | uint32_t wfc_stream_create(WFCNativeStreamType stream, uint32_t flags) |
165 | // Create a stream, using the given stream handle (typically assigned by the |
166 | // window manager). Return zero if OK. |
167 | { |
168 | WFC_STREAM_T *stream_ptr; |
169 | uint32_t result = 0; |
170 | |
171 | vcos_log_info("%s: stream 0x%x flags 0x%x" , VCOS_FUNCTION, stream, flags); |
172 | |
173 | // Create stream |
174 | stream_ptr = wfc_stream_create_stream_ptr(stream, false); |
175 | if(stream_ptr == NULL) |
176 | { |
177 | vcos_log_error("%s: unable to create data block for stream 0x%x" , VCOS_FUNCTION, stream); |
178 | return VCOS_ENOMEM; |
179 | } |
180 | |
181 | uint64_t pid = vcos_process_id_current(); |
182 | uint32_t pid_lo = (uint32_t) pid; |
183 | uint32_t pid_hi = (uint32_t) (pid >> 32); |
184 | int stream_in_use_retries = WFC_STREAM_RETRIES; |
185 | WFC_STREAM_INFO_T info; |
186 | |
187 | memset(&info, 0, sizeof(info)); |
188 | info.size = sizeof(info); |
189 | info.flags = flags; |
190 | |
191 | do |
192 | { |
193 | stream_ptr->handle = wfc_server_stream_create_info(stream, &info, pid_lo, pid_hi); |
194 | vcos_log_trace("%s: server create returned 0x%x" , VCOS_FUNCTION, stream_ptr->handle); |
195 | |
196 | // If a handle is re-used rapidly, it may still be in use in the server temporarily |
197 | // Retry after a short delay |
198 | if (stream_ptr->handle == WFC_INVALID_HANDLE) |
199 | vcos_sleep(WFC_STREAM_RETRY_DELAY_MS); |
200 | } |
201 | while (stream_ptr->handle == WFC_INVALID_HANDLE && stream_in_use_retries-- > 0); |
202 | |
203 | if (stream_ptr->handle == WFC_INVALID_HANDLE) |
204 | { |
205 | // Even after the retries, stream handle was still in use. Fail. |
206 | vcos_log_error("%s: stream 0x%x already exists in server" , VCOS_FUNCTION, stream); |
207 | result = VCOS_EEXIST; |
208 | wfc_stream_destroy_if_ready(stream_ptr); |
209 | } |
210 | else |
211 | { |
212 | vcos_assert(stream_ptr->handle == stream); |
213 | |
214 | stream_ptr->registrations++; |
215 | stream_ptr->info.flags = flags; |
216 | STREAM_UNLOCK(stream_ptr); |
217 | } |
218 | |
219 | return result; |
220 | } |
221 | |
222 | //------------------------------------------------------------------------------ |
223 | |
224 | WFCNativeStreamType wfc_stream_create_assign_id(uint32_t flags) |
225 | // Create a stream, and automatically assign it a new stream number, which is returned |
226 | { |
227 | WFCNativeStreamType stream = wfc_stream_get_next(); |
228 | uint32_t failure = wfc_stream_create(stream, flags); |
229 | |
230 | if (failure == VCOS_EEXIST) |
231 | { |
232 | // If a duplicate stream exists, give it one more go with a new ID |
233 | stream = wfc_stream_get_next(); |
234 | failure = wfc_stream_create(stream, flags); |
235 | } |
236 | |
237 | if(failure) {return WFC_INVALID_HANDLE;} |
238 | else {return stream;} |
239 | } |
240 | |
241 | //------------------------------------------------------------------------------ |
242 | |
243 | uint32_t wfc_stream_create_req_rect |
244 | (WFCNativeStreamType stream, uint32_t flags, |
245 | WFC_STREAM_REQ_RECT_CALLBACK_T callback, void *cb_args) |
246 | // Create a stream, using the given stream handle, which will notify the calling |
247 | // module when the server requests a change in source and/or destination rectangle, |
248 | // using the supplied callback. Return zero if OK. |
249 | { |
250 | vcos_log_info("wfc_stream_create_req_rect: stream %X" , stream); |
251 | |
252 | uint32_t failure; |
253 | |
254 | failure = wfc_stream_create(stream, flags | WFC_STREAM_FLAGS_REQ_RECT); |
255 | if (failure) |
256 | return failure; |
257 | |
258 | WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream); |
259 | // Stream just created, so ought to be found |
260 | vcos_assert(stream_ptr); |
261 | |
262 | // There's no point creating this type of stream if you don't supply a callback |
263 | // to update the src/dest rects via WF-C. |
264 | vcos_assert(callback != NULL); |
265 | |
266 | stream_ptr->req_rect_callback = callback; |
267 | stream_ptr->req_rect_cb_args = cb_args; |
268 | |
269 | // Create thread for handling server-side request to change source |
270 | // and/or destination rectangles. One per stream (if enabled). |
271 | VCOS_STATUS_T status = vcos_thread_create(&stream_ptr->rect_req_thread_data, "wfc_stream_rect_req_thread" , |
272 | NULL, wfc_stream_rect_req_thread, (void *) stream); |
273 | vcos_demand(status == VCOS_SUCCESS); |
274 | |
275 | STREAM_UNLOCK(stream_ptr); |
276 | |
277 | return 0; |
278 | } |
279 | |
280 | //------------------------------------------------------------------------------ |
281 | |
282 | bool wfc_stream_register_source_or_mask(WFCNativeStreamType stream, bool add_source_or_mask) |
283 | // Indicate that a source or mask is now associated with this stream, or should |
284 | // now be removed from such an association. |
285 | { |
286 | WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream); |
287 | |
288 | if (!stream_ptr) |
289 | return false; |
290 | |
291 | vcos_log_trace("%s: stream 0x%x %d->%d" , VCOS_FUNCTION, stream, |
292 | stream_ptr->num_of_sources_or_masks, |
293 | add_source_or_mask ? stream_ptr->num_of_sources_or_masks + 1 : stream_ptr->num_of_sources_or_masks - 1); |
294 | |
295 | if(add_source_or_mask) |
296 | { |
297 | stream_ptr->num_of_sources_or_masks++; |
298 | STREAM_UNLOCK(stream_ptr); |
299 | } |
300 | else |
301 | { |
302 | if(vcos_verify(stream_ptr->num_of_sources_or_masks > 0)) |
303 | {stream_ptr->num_of_sources_or_masks--;} |
304 | |
305 | // Stream is unlocked by destroy_if_ready |
306 | wfc_stream_destroy_if_ready(stream_ptr); |
307 | } |
308 | |
309 | return true; |
310 | } |
311 | |
312 | //------------------------------------------------------------------------------ |
313 | |
314 | void wfc_stream_await_buffer(WFCNativeStreamType stream) |
315 | // Suspend until buffer is available on the server. |
316 | { |
317 | vcos_log_trace("%s: stream 0x%x" , VCOS_FUNCTION, stream); |
318 | |
319 | WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream); |
320 | if (!stream_ptr) |
321 | return; |
322 | |
323 | if(vcos_verify(stream_ptr->info.flags & WFC_STREAM_FLAGS_BUF_AVAIL)) |
324 | { |
325 | VCOS_SEMAPHORE_T image_available_sem; |
326 | VCOS_STATUS_T status; |
327 | |
328 | // Long running operation, so keep VC alive until it completes. |
329 | wfc_server_use_keep_alive(); |
330 | |
331 | status = vcos_semaphore_create(&image_available_sem, "WFC image available" , 0); |
332 | vcos_assert(status == VCOS_SUCCESS); // For all relevant platforms |
333 | vcos_unused(status); |
334 | |
335 | wfc_server_stream_on_image_available(stream, wfc_client_stream_post_sem, &image_available_sem); |
336 | |
337 | vcos_log_trace("%s: pre async sem wait: stream: %X" , VCOS_FUNCTION, stream); |
338 | vcos_semaphore_wait(&image_available_sem); |
339 | vcos_log_trace("%s: post async sem wait: stream: %X" , VCOS_FUNCTION, stream); |
340 | |
341 | vcos_semaphore_delete(&image_available_sem); |
342 | wfc_server_release_keep_alive(); |
343 | } |
344 | |
345 | STREAM_UNLOCK(stream_ptr); |
346 | |
347 | } |
348 | |
349 | //------------------------------------------------------------------------------ |
350 | |
351 | void wfc_stream_destroy(WFCNativeStreamType stream) |
352 | // Destroy a stream - unless it is still in use, in which case, mark it for |
353 | // destruction once all users have finished with it. |
354 | { |
355 | vcos_log_info("%s: stream: %X" , VCOS_FUNCTION, stream); |
356 | |
357 | WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream); |
358 | |
359 | if (stream_ptr) |
360 | { |
361 | /* If stream is still in use (e.g. it's attached to at least one source/mask |
362 | * which is associated with at least one element) then destruction is delayed |
363 | * until it's no longer in use. */ |
364 | if (stream_ptr->registrations> 0) |
365 | { |
366 | stream_ptr->registrations--; |
367 | vcos_log_trace("%s: stream: %X ready to destroy?" , VCOS_FUNCTION, stream); |
368 | } |
369 | else |
370 | { |
371 | vcos_log_error("%s: stream: %X destroyed when unregistered" , VCOS_FUNCTION, stream); |
372 | } |
373 | |
374 | // Stream is unlocked by destroy_if_ready |
375 | wfc_stream_destroy_if_ready(stream_ptr); |
376 | } |
377 | else |
378 | { |
379 | vcos_log_warn("%s: stream %X doesn't exist" , VCOS_FUNCTION, stream); |
380 | } |
381 | |
382 | } |
383 | |
384 | //------------------------------------------------------------------------------ |
385 | //!@name |
386 | //! Off-screen composition functions |
387 | //!@{ |
388 | //------------------------------------------------------------------------------ |
389 | |
390 | uint32_t wfc_stream_create_for_context |
391 | (WFCNativeStreamType stream, uint32_t width, uint32_t height) |
392 | // Create a stream for an off-screen context to output to, with the default number of buffers. |
393 | { |
394 | return wfc_stream_create_for_context_nbufs(stream, width, height, 0); |
395 | } |
396 | |
397 | uint32_t wfc_stream_create_for_context_nbufs |
398 | (WFCNativeStreamType stream, uint32_t width, uint32_t height, uint32_t nbufs) |
399 | // Create a stream for an off-screen context to output to, with a specific number of buffers. |
400 | { |
401 | WFC_STREAM_T *stream_ptr; |
402 | bool stream_created = false; |
403 | |
404 | if(!vcos_verify(stream != WFC_INVALID_HANDLE)) |
405 | {return 1;} |
406 | |
407 | stream_ptr = wfc_stream_find_stream_ptr(stream); |
408 | if (stream_ptr) |
409 | { |
410 | uint32_t flags = stream_ptr->info.flags; |
411 | |
412 | // Stream already exists, check flags match expected |
413 | STREAM_UNLOCK(stream_ptr); |
414 | |
415 | if (flags != WFC_STREAM_FLAGS_NONE) |
416 | { |
417 | vcos_log_error("%s: stream flags mismatch (expected 0x%x, got 0x%x)" , VCOS_FUNCTION, WFC_STREAM_FLAGS_NONE, flags); |
418 | return 1; |
419 | } |
420 | } |
421 | else |
422 | { |
423 | // Create stream |
424 | if (wfc_stream_create(stream, WFC_STREAM_FLAGS_NONE) != 0) |
425 | return 1; |
426 | stream_created = true; |
427 | } |
428 | |
429 | // Allocate buffers on the server. |
430 | if (!wfc_server_stream_allocate_images(stream, width, height, nbufs)) |
431 | { |
432 | // Failed to allocate buffers |
433 | vcos_log_warn("%s: failed to allocate %u buffers for stream %X size %ux%u" , VCOS_FUNCTION, nbufs, stream, width, height); |
434 | if (stream_created) |
435 | wfc_stream_destroy(stream); |
436 | return 1; |
437 | } |
438 | |
439 | return 0; |
440 | } |
441 | |
442 | //------------------------------------------------------------------------------ |
443 | |
444 | bool wfc_stream_used_for_off_screen(WFCNativeStreamType stream) |
445 | // Returns true if this stream exists, and is in use as the output of an |
446 | // off-screen context. |
447 | { |
448 | bool used_for_off_screen; |
449 | |
450 | vcos_log_trace("%s: stream 0x%x" , VCOS_FUNCTION, stream); |
451 | |
452 | WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream); |
453 | if (!stream_ptr) |
454 | return false; |
455 | |
456 | used_for_off_screen = stream_ptr->used_for_off_screen; |
457 | |
458 | STREAM_UNLOCK(stream_ptr); |
459 | |
460 | return used_for_off_screen; |
461 | |
462 | } |
463 | |
464 | //------------------------------------------------------------------------------ |
465 | |
466 | void wfc_stream_register_off_screen(WFCNativeStreamType stream, bool used_for_off_screen) |
467 | // Called on behalf of an off-screen context, to either set or clear the stream's |
468 | // flag indicating that it's being used as output for that context. |
469 | { |
470 | if(stream == WFC_INVALID_HANDLE) |
471 | {return;} |
472 | |
473 | vcos_log_trace("%s: stream 0x%x" , VCOS_FUNCTION, stream); |
474 | |
475 | WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream); |
476 | if (!stream_ptr) |
477 | return; |
478 | |
479 | stream_ptr->used_for_off_screen = used_for_off_screen; |
480 | |
481 | if (used_for_off_screen) |
482 | STREAM_UNLOCK(stream_ptr); |
483 | else |
484 | { |
485 | // Stream is unlocked by destroy_if_ready |
486 | wfc_stream_destroy_if_ready(stream_ptr); |
487 | } |
488 | } |
489 | |
490 | //!@} // Off-screen composition functions |
491 | //!@} // Public functions |
492 | //============================================================================== |
493 | |
494 | /** Initialise logging and global mutex */ |
495 | static void wfc_stream_initialise(void) |
496 | { |
497 | VCOS_STATUS_T status; |
498 | |
499 | vcos_log_set_level(&log_cat, WFC_LOG_LEVEL); |
500 | vcos_log_register("wfc_client_stream" , &log_cat); |
501 | |
502 | vcos_log_trace("%s" , VCOS_FUNCTION); |
503 | |
504 | status = vcos_mutex_create(&wfc_stream_global_lock, "WFC stream global lock" ); |
505 | vcos_assert(status == VCOS_SUCCESS); |
506 | |
507 | status = vcos_blockpool_create_on_heap(&wfc_stream_blockpool, |
508 | WFC_STREAM_BLOCK_SIZE, sizeof(WFC_STREAM_T), |
509 | VCOS_BLOCKPOOL_ALIGN_DEFAULT, VCOS_BLOCKPOOL_FLAG_NONE, |
510 | "wfc stream pool" ); |
511 | vcos_assert(status == VCOS_SUCCESS); |
512 | |
513 | status = vcos_blockpool_extend(&wfc_stream_blockpool, |
514 | WFC_STREAM_MAX_EXTENSIONS, WFC_STREAM_BLOCK_SIZE); |
515 | vcos_assert(status == VCOS_SUCCESS); |
516 | } |
517 | |
518 | //------------------------------------------------------------------------------ |
519 | |
520 | /** Take the global lock and then search for the stream data for a given handle. |
521 | * The global lock is not released on return and the stream is not locked. |
522 | * |
523 | * @param stream The stream handle. |
524 | * @return The pointer to the stream structure, or NULL if not found. |
525 | */ |
526 | static WFC_STREAM_T *wfc_stream_global_lock_and_find_stream_ptr(WFCNativeStreamType stream) |
527 | { |
528 | WFC_STREAM_T *stream_ptr; |
529 | |
530 | GLOBAL_LOCK(); |
531 | |
532 | stream_ptr = wfc_stream_head; |
533 | while (stream_ptr && stream_ptr->handle != stream) |
534 | stream_ptr = stream_ptr->next; |
535 | |
536 | return stream_ptr; |
537 | } |
538 | |
539 | //------------------------------------------------------------------------------ |
540 | |
541 | /** Create a stream structure corresponding to the specified stream handle. If |
542 | * the stream structure already exists or there is an error allocating it, the |
543 | * function returns NULL. On success, the stream pointer is left locked. |
544 | * |
545 | * @param stream The stream handle. |
546 | * @param allow_duplicate True to allow an existing entry |
547 | * @return The pointer to the new stream structure, or NULL on error. |
548 | */ |
549 | static WFC_STREAM_T *wfc_stream_create_stream_ptr(WFCNativeStreamType stream, bool allow_duplicate) |
550 | { |
551 | WFC_STREAM_T *stream_ptr = wfc_stream_global_lock_and_find_stream_ptr(stream); |
552 | |
553 | vcos_log_trace("%s: stream handle 0x%x" , VCOS_FUNCTION, stream); |
554 | |
555 | if (stream_ptr && !stream_ptr->to_be_deleted) |
556 | { |
557 | if (!allow_duplicate) |
558 | { |
559 | vcos_log_error("%s: attempt to create duplicate of stream handle 0x%x" , VCOS_FUNCTION, stream); |
560 | // Stream already exists, return NULL |
561 | stream_ptr = NULL; |
562 | } |
563 | else |
564 | { |
565 | vcos_log_trace("%s: duplicate of stream handle 0x%x created" , VCOS_FUNCTION, stream); |
566 | |
567 | STREAM_LOCK(stream_ptr); |
568 | } |
569 | } |
570 | else |
571 | { |
572 | if (stream_ptr) |
573 | { |
574 | vcos_log_trace("%s: recycling data block for stream handle 0x%x" , VCOS_FUNCTION, stream); |
575 | |
576 | // Recycle existing entry |
577 | stream_ptr->to_be_deleted = false; |
578 | |
579 | STREAM_LOCK(stream_ptr); |
580 | } |
581 | else |
582 | { |
583 | vcos_log_trace("%s: allocating block for stream handle 0x%x" , VCOS_FUNCTION, stream); |
584 | |
585 | // Create new block and insert it into the list |
586 | stream_ptr = vcos_blockpool_calloc(&wfc_stream_blockpool); |
587 | |
588 | if (stream_ptr) |
589 | { |
590 | VCOS_STATUS_T status; |
591 | |
592 | status = vcos_mutex_create(&stream_ptr->mutex, "WFC_STREAM_T mutex" ); |
593 | if (vcos_verify(status == VCOS_SUCCESS)) |
594 | { |
595 | STREAM_LOCK(stream_ptr); |
596 | |
597 | // First stream in this process, connect |
598 | if (!wfc_stream_head) |
599 | wfc_server_connect(); |
600 | |
601 | stream_ptr->handle = stream; |
602 | stream_ptr->info.size = sizeof(stream_ptr->info); |
603 | |
604 | // Insert data into list |
605 | stream_ptr->next = wfc_stream_head; |
606 | if (wfc_stream_head) |
607 | wfc_stream_head->prev = stream_ptr; |
608 | wfc_stream_head = stream_ptr; |
609 | } |
610 | else |
611 | { |
612 | vcos_log_error("%s: unable to create mutex for stream handle 0x%x" , VCOS_FUNCTION, stream); |
613 | vcos_blockpool_free(stream_ptr); |
614 | stream_ptr = NULL; |
615 | } |
616 | } |
617 | else |
618 | { |
619 | vcos_log_error("%s: unable to allocate data for stream handle 0x%x" , VCOS_FUNCTION, stream); |
620 | } |
621 | } |
622 | } |
623 | |
624 | GLOBAL_UNLOCK(); |
625 | |
626 | return stream_ptr; |
627 | } |
628 | |
629 | //------------------------------------------------------------------------------ |
630 | |
631 | /** Destroys a stream structure identified by stream handle. If the stream is not |
632 | * found or the stream has not been marked for deletion, the operation has no |
633 | * effect. |
634 | * |
635 | * @param stream The stream handle. |
636 | */ |
637 | static void wfc_stream_destroy_stream_ptr(WFCNativeStreamType stream) |
638 | { |
639 | WFC_STREAM_T *stream_ptr = wfc_stream_global_lock_and_find_stream_ptr(stream); |
640 | |
641 | vcos_log_trace("%s: stream handle 0x%x" , VCOS_FUNCTION, stream); |
642 | |
643 | if (stream_ptr) |
644 | { |
645 | if (stream_ptr->to_be_deleted) |
646 | { |
647 | STREAM_LOCK(stream_ptr); |
648 | |
649 | vcos_log_trace("%s: unlinking from list" , VCOS_FUNCTION); |
650 | |
651 | if (stream_ptr->next) |
652 | stream_ptr->next->prev = stream_ptr->prev; |
653 | if (stream_ptr->prev) |
654 | stream_ptr->prev->next = stream_ptr->next; |
655 | else |
656 | wfc_stream_head = stream_ptr->next; |
657 | |
658 | // No streams left in this process, disconnect |
659 | if (wfc_stream_head == NULL) |
660 | wfc_server_disconnect(); |
661 | } |
662 | else |
663 | { |
664 | vcos_log_trace("%s: stream 0x%x recycled before destruction" , VCOS_FUNCTION, stream); |
665 | stream_ptr = NULL; |
666 | } |
667 | } |
668 | else |
669 | { |
670 | vcos_log_error("%s: stream 0x%x not found" , VCOS_FUNCTION, stream); |
671 | } |
672 | |
673 | GLOBAL_UNLOCK(); |
674 | |
675 | if (stream_ptr) |
676 | { |
677 | // Stream data block no longer in list, can safely destroy it |
678 | STREAM_UNLOCK(stream_ptr); |
679 | |
680 | // Wait for rectangle request thread to complete |
681 | if(stream_ptr->info.flags & WFC_STREAM_FLAGS_REQ_RECT) |
682 | vcos_thread_join(&stream_ptr->rect_req_thread_data, NULL); |
683 | |
684 | // Destroy mutex |
685 | vcos_mutex_delete(&stream_ptr->mutex); |
686 | |
687 | // Delete |
688 | vcos_blockpool_free(stream_ptr); |
689 | } |
690 | } |
691 | |
692 | //------------------------------------------------------------------------------ |
693 | |
694 | /** Return a pointer to the stream structure corresponding to the specified stream |
695 | * handle. On success, the stream pointer is locked. |
696 | * |
697 | * @param stream The stream handle. |
698 | * @return The pointer to the stream structure, or NULL on error. |
699 | */ |
700 | static WFC_STREAM_T *wfc_stream_find_stream_ptr(WFCNativeStreamType stream) |
701 | { |
702 | WFC_STREAM_T *stream_ptr = wfc_stream_global_lock_and_find_stream_ptr(stream); |
703 | |
704 | if (stream_ptr && !stream_ptr->to_be_deleted) |
705 | STREAM_LOCK(stream_ptr); |
706 | |
707 | GLOBAL_UNLOCK(); |
708 | |
709 | return stream_ptr; |
710 | } |
711 | |
712 | //------------------------------------------------------------------------------ |
713 | |
714 | /** Destroy the stream if it is no longer in use. The stream must be locked on |
715 | * entry and shall be unlocked (or destroyed along with the rest of the stream) |
716 | * on exit. |
717 | * |
718 | * @param stream_ptr The locked stream data pointer. |
719 | */ |
720 | static void wfc_stream_destroy_if_ready(WFC_STREAM_T *stream_ptr) |
721 | { |
722 | WFCNativeStreamType stream; |
723 | uint64_t pid = vcos_process_id_current(); |
724 | uint32_t pid_lo = (uint32_t)pid; |
725 | uint32_t pid_hi = (uint32_t)(pid >> 32); |
726 | |
727 | if (stream_ptr == NULL) |
728 | { |
729 | vcos_log_error("%s: stream_ptr is NULL" , VCOS_FUNCTION); |
730 | return; |
731 | } |
732 | |
733 | if(stream_ptr->num_of_sources_or_masks > 0 |
734 | || stream_ptr->used_for_off_screen |
735 | || stream_ptr->registrations > 0) |
736 | { |
737 | vcos_log_trace("%s: stream: %X not ready: reg:%u srcs:%u o/s:%d" , VCOS_FUNCTION, |
738 | stream_ptr->handle, stream_ptr->registrations, |
739 | stream_ptr->num_of_sources_or_masks, stream_ptr->used_for_off_screen); |
740 | STREAM_UNLOCK(stream_ptr); |
741 | return; |
742 | } |
743 | |
744 | stream = stream_ptr->handle; |
745 | |
746 | vcos_log_info("%s: stream: %X to be destroyed" , VCOS_FUNCTION, stream); |
747 | |
748 | // Prevent stream from being found, although it can be recycled. |
749 | stream_ptr->to_be_deleted = true; |
750 | |
751 | // Delete server-side stream |
752 | wfc_server_stream_destroy(stream, pid_lo, pid_hi); |
753 | |
754 | STREAM_UNLOCK(stream_ptr); |
755 | |
756 | wfc_stream_destroy_stream_ptr(stream); |
757 | } |
758 | |
759 | //------------------------------------------------------------------------------ |
760 | |
761 | //! Convert from dispmanx source rectangle type (int * 2^16) to WF-C type (float). |
762 | #define WFC_DISPMANX_TO_SRC_VAL(value) (((WFCfloat) (value)) / 65536.0) |
763 | |
764 | static void *wfc_stream_rect_req_thread(void *arg) |
765 | //!@brief Thread for handling server-side request to change source and/or destination |
766 | //! rectangles. One per stream (if enabled). |
767 | { |
768 | WFCNativeStreamType stream = (WFCNativeStreamType) arg; |
769 | |
770 | WFC_STREAM_REQ_RECT_CALLBACK_T callback; |
771 | void *cb_args; |
772 | VCOS_SEMAPHORE_T rect_req_sem; |
773 | VCOS_STATUS_T status; |
774 | |
775 | int32_t vc_rects[8]; |
776 | WFCint dest_rect[WFC_RECT_SIZE]; |
777 | WFCfloat src_rect[WFC_RECT_SIZE]; |
778 | |
779 | vcos_log_info("wfc_stream_rect_req_thread: START: stream: %X" , stream); |
780 | |
781 | WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream); |
782 | if (!stream_ptr) |
783 | return NULL; |
784 | |
785 | // Get local pointers to stream parameters |
786 | callback = stream_ptr->req_rect_callback; |
787 | cb_args = stream_ptr->req_rect_cb_args; |
788 | |
789 | STREAM_UNLOCK(stream_ptr); |
790 | |
791 | status = vcos_semaphore_create(&rect_req_sem, "WFC rect req" , 0); |
792 | vcos_assert(status == VCOS_SUCCESS); // On all relevant platforms |
793 | |
794 | while (status == VCOS_SUCCESS) |
795 | { |
796 | wfc_server_stream_on_rects_change(stream, wfc_client_stream_post_sem, &rect_req_sem); |
797 | |
798 | // Await new values from server |
799 | vcos_semaphore_wait(&rect_req_sem); |
800 | |
801 | status = wfc_server_stream_get_rects(stream, vc_rects); |
802 | if (status == VCOS_SUCCESS) |
803 | { |
804 | // Convert from VC/dispmanx to WF-C types. |
805 | vcos_static_assert(sizeof(dest_rect) == (4 * sizeof(int32_t))); |
806 | memcpy(dest_rect, vc_rects, sizeof(dest_rect)); // Types are equivalent |
807 | |
808 | src_rect[WFC_RECT_X] = WFC_DISPMANX_TO_SRC_VAL(vc_rects[4]); |
809 | src_rect[WFC_RECT_Y] = WFC_DISPMANX_TO_SRC_VAL(vc_rects[5]); |
810 | src_rect[WFC_RECT_WIDTH] = WFC_DISPMANX_TO_SRC_VAL(vc_rects[6]); |
811 | src_rect[WFC_RECT_HEIGHT] = WFC_DISPMANX_TO_SRC_VAL(vc_rects[7]); |
812 | |
813 | callback(cb_args, dest_rect, src_rect); |
814 | } |
815 | } |
816 | |
817 | vcos_semaphore_delete(&rect_req_sem); |
818 | |
819 | vcos_log_info("wfc_stream_rect_req_thread: END: stream: %X" , stream); |
820 | |
821 | return NULL; |
822 | } |
823 | |
824 | //------------------------------------------------------------------------------ |
825 | |
826 | static void wfc_client_stream_post_sem(void *cb_data) |
827 | { |
828 | VCOS_SEMAPHORE_T *sem = (VCOS_SEMAPHORE_T *)cb_data; |
829 | |
830 | vcos_log_trace("%s: sem %p" , VCOS_FUNCTION, sem); |
831 | vcos_assert(sem != NULL); |
832 | vcos_semaphore_post(sem); |
833 | } |
834 | |
835 | //============================================================================== |
836 | #ifdef KHRN_FRUIT_DIRECT |
837 | static KHRN_UMEM_HANDLE_T get_ustorage(EGLImageKHR im, KHRN_DEPS_T *deps) |
838 | { |
839 | KHRN_UMEM_HANDLE_T ret = KHRN_UMEM_HANDLE_INVALID; |
840 | EGL_SERVER_STATE_T *state = EGL_GET_SERVER_STATE(); |
841 | EGL_IMAGE_T *eglimage_; |
842 | KHRN_IMAGE_T *image; |
843 | |
844 | KHRN_MEM_HANDLE_T eglimage = |
845 | khrn_map_lookup(&state->eglimages, (uint32_t) im); |
846 | |
847 | if (eglimage == KHRN_MEM_HANDLE_INVALID) { |
848 | vcos_log_error("Bad image %p" , im); |
849 | goto end; |
850 | } |
851 | |
852 | eglimage_ = khrn_mem_lock(eglimage); |
853 | vcos_assert(eglimage_->external.src != KHRN_UMEM_HANDLE_INVALID); |
854 | image = khrn_mem_lock(eglimage_->external.src); |
855 | |
856 | /* FIXME: We probably don't need this. It doesn't make any sense */ |
857 | khrn_deps_quick_write(deps, &image->interlock); |
858 | |
859 | ret = image->ustorage; |
860 | |
861 | khrn_mem_unlock(eglimage_->external.src); |
862 | khrn_mem_unlock(eglimage); |
863 | |
864 | end: |
865 | return ret; |
866 | } |
867 | |
868 | void wfc_stream_signal_eglimage_data(WFCNativeStreamType stream, EGLImageKHR im) |
869 | { |
870 | wfc_stream_signal_eglimage_data_protected(stream, im, 0); |
871 | } |
872 | |
873 | #ifdef ANDROID |
874 | void wfc_stream_signal_eglimage_data_protected(WFCNativeStreamType stream, EGLImageKHR im, uint32_t is_protected) |
875 | { |
876 | EGL_SERVER_STATE_T *state = EGL_GET_SERVER_STATE(); |
877 | KHRN_DEPS_T deps; |
878 | KHRN_MEM_HANDLE_T eglimage; |
879 | EGL_IMAGE_T *eglimage_; |
880 | KHRN_IMAGE_T *image_; |
881 | |
882 | CLIENT_LOCK(); |
883 | |
884 | khrn_deps_init(&deps); |
885 | |
886 | eglimage = khrn_map_lookup(&state->eglimages, (uint32_t)im); |
887 | eglimage_ = khrn_mem_lock(eglimage); |
888 | image_ = khrn_mem_lock(eglimage_->external.src); |
889 | |
890 | vcos_assert(image_->ustorage != KHRN_UMEM_HANDLE_INVALID); |
891 | khrn_umem_acquire(&deps, image_->ustorage); |
892 | |
893 | image_->flags &= ~IMAGE_FLAG_PROTECTED; |
894 | if (is_protected) |
895 | image_->flags |= IMAGE_FLAG_PROTECTED; |
896 | |
897 | khrn_signal_image_data((uint32_t)stream, image_->ustorage, image_->width, image_->height, image_->stride, image_->offset, |
898 | image_->format, image_->flags, eglimage_->flip_y ? true : false); |
899 | |
900 | khrn_mem_unlock(eglimage_->external.src); |
901 | khrn_mem_unlock(eglimage); |
902 | |
903 | CLIENT_UNLOCK(); |
904 | } |
905 | #else |
906 | void wfc_stream_signal_eglimage_data_protected(WFCNativeStreamType stream, EGLImageKHR im, uint32_t is_protected) |
907 | { |
908 | EGL_SERVER_STATE_T *state = EGL_GET_SERVER_STATE(); |
909 | KHRN_DEPS_T deps; |
910 | KHRN_MEM_HANDLE_T eglimage; |
911 | EGL_IMAGE_T *eglimage_; |
912 | KHRN_IMAGE_T *image_; |
913 | WFC_STREAM_IMAGE_T stream_image; |
914 | |
915 | CLIENT_LOCK(); |
916 | |
917 | khrn_deps_init(&deps); |
918 | |
919 | eglimage = khrn_map_lookup(&state->eglimages, (uint32_t)im); |
920 | eglimage_ = khrn_mem_lock(eglimage); |
921 | image_ = khrn_mem_lock(eglimage_->external.src); |
922 | |
923 | vcos_assert(image_->ustorage != KHRN_UMEM_HANDLE_INVALID); |
924 | khrn_umem_acquire(&deps, image_->ustorage); |
925 | |
926 | /* The EGL protection flag is passed through the KHRN_IMAGE_T flags field */ |
927 | image_->flags &= ~IMAGE_FLAG_PROTECTED; |
928 | if (is_protected) |
929 | image_->flags |= IMAGE_FLAG_PROTECTED; |
930 | |
931 | memset(&stream_image, 0, sizeof(stream_image)); |
932 | stream_image.length = sizeof(stream_image); |
933 | stream_image.type = WFC_STREAM_IMAGE_TYPE_EGL; |
934 | |
935 | stream_image.handle = image_->ustorage; |
936 | stream_image.width = image_->width; |
937 | stream_image.height = image_->height; |
938 | stream_image.format = image_->format; |
939 | stream_image.pitch = image_->stride; |
940 | stream_image.offset = image_->offset; |
941 | stream_image.flags = image_->flags; |
942 | stream_image.flip = eglimage_->flip_y ? WFC_STREAM_IMAGE_FLIP_VERT : WFC_STREAM_IMAGE_FLIP_NONE; |
943 | |
944 | khrn_mem_unlock(eglimage_->external.src); |
945 | khrn_mem_unlock(eglimage); |
946 | |
947 | CLIENT_UNLOCK(); |
948 | |
949 | wfc_server_stream_signal_image(stream, &stream_image); |
950 | } |
951 | #endif |
952 | |
953 | void wfc_stream_release_eglimage_data(WFCNativeStreamType stream, |
954 | EGLImageKHR im) |
955 | { |
956 | KHRN_DEPS_T deps; |
957 | KHRN_UMEM_HANDLE_T ustorage; |
958 | |
959 | CLIENT_LOCK(); |
960 | ustorage = get_ustorage(im, &deps); |
961 | khrn_umem_release(&deps, ustorage); |
962 | CLIENT_UNLOCK(); |
963 | } |
964 | #endif |
965 | |
966 | void wfc_stream_signal_mm_image_data(WFCNativeStreamType stream, uint32_t im) |
967 | { |
968 | wfc_server_stream_signal_mm_image_data(stream, im); |
969 | } |
970 | |
971 | void wfc_stream_signal_raw_pixels(WFCNativeStreamType stream, uint32_t handle, |
972 | uint32_t format, uint32_t w, uint32_t h, uint32_t pitch, uint32_t vpitch) |
973 | { |
974 | wfc_server_stream_signal_raw_pixels(stream, handle, format, w, h, pitch, vpitch); |
975 | } |
976 | |
977 | void wfc_stream_signal_image(WFCNativeStreamType stream, |
978 | const WFC_STREAM_IMAGE_T *image) |
979 | { |
980 | wfc_server_stream_signal_image(stream, image); |
981 | } |
982 | |
983 | void wfc_stream_register(WFCNativeStreamType stream) { |
984 | uint64_t pid = vcos_process_id_current(); |
985 | uint32_t pid_lo = (uint32_t)pid; |
986 | uint32_t pid_hi = (uint32_t)(pid >> 32); |
987 | |
988 | if (wfc_server_connect() == VCOS_SUCCESS) |
989 | { |
990 | WFC_STREAM_INFO_T info; |
991 | uint32_t status; |
992 | |
993 | info.size = sizeof(info); |
994 | status = wfc_server_stream_get_info(stream, &info); |
995 | |
996 | if (status == VCOS_SUCCESS) |
997 | { |
998 | WFC_STREAM_T *stream_ptr = wfc_stream_create_stream_ptr(stream, true); |
999 | |
1000 | if (stream_ptr) |
1001 | { |
1002 | stream_ptr->registrations++; |
1003 | memcpy(&stream_ptr->info, &info, info.size); |
1004 | STREAM_UNLOCK(stream_ptr); |
1005 | } |
1006 | |
1007 | wfc_server_stream_register(stream, pid_lo, pid_hi); |
1008 | } |
1009 | else |
1010 | { |
1011 | vcos_log_error("%s: get stream info failed: %u" , VCOS_FUNCTION, status); |
1012 | } |
1013 | } |
1014 | } |
1015 | |
1016 | void wfc_stream_unregister(WFCNativeStreamType stream) { |
1017 | uint64_t pid = vcos_process_id_current(); |
1018 | uint32_t pid_lo = (uint32_t)pid; |
1019 | uint32_t pid_hi = (uint32_t)(pid >> 32); |
1020 | WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream); |
1021 | |
1022 | if (vcos_verify(stream_ptr != NULL)) |
1023 | { |
1024 | wfc_server_stream_unregister(stream, pid_lo, pid_hi); |
1025 | |
1026 | if (stream_ptr->registrations > 0) |
1027 | { |
1028 | stream_ptr->registrations--; |
1029 | vcos_log_trace("%s: stream %X" , VCOS_FUNCTION, stream); |
1030 | } |
1031 | else |
1032 | { |
1033 | vcos_log_error("%s: stream %X already fully unregistered" , VCOS_FUNCTION, stream); |
1034 | } |
1035 | |
1036 | wfc_stream_destroy_if_ready(stream_ptr); |
1037 | } |
1038 | |
1039 | wfc_server_disconnect(); |
1040 | } |
1041 | |
1042 | //============================================================================== |
1043 | |
1044 | |