1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON 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
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27#define VCOS_LOG_CATEGORY (&khrn_client_log)
28#include "interface/khronos/common/khrn_int_common.h"
29
30#include "interface/khronos/common/khrn_client_check_types.h"
31#include "interface/khronos/common/khrn_client.h"
32#include "interface/khronos/common/khrn_client_rpc.h"
33#include "interface/khronos/egl/egl_client_config.h"
34#include "interface/khronos/glxx/glxx_client.h"
35
36#if defined(V3D_LEAN)
37#include "interface/khronos/common/khrn_int_misc_impl.h"
38#endif
39
40#if EGL_KHR_sync
41#include "interface/khronos/ext/egl_khr_sync_client.h"
42#endif
43
44#if EGL_BRCM_perf_monitor
45#include "interface/khronos/ext/egl_brcm_perf_monitor_client.h"
46#endif
47
48#if EGL_BRCM_driver_monitor
49#include "interface/khronos/ext/egl_brcm_driver_monitor_client.h"
50#endif
51
52#ifdef RPC_LIBRARY
53#include "middleware/dlloader/dlfcn.h"
54#include "applications/vmcs/khronos/khronos_server.h"
55#endif
56
57VCOS_LOG_CAT_T khrn_client_log = VCOS_LOG_INIT("khrn_client", VCOS_LOG_WARN);
58
59/*
60 void client_try_unload_server(CLIENT_PROCESS_STATE_T *process)
61
62 Preconditions:
63
64 -
65
66 Postconditions:
67
68 -
69
70 Invariants preserved:
71
72 -
73
74 Invariants used:
75
76 -
77*/
78
79void client_try_unload_server(CLIENT_PROCESS_STATE_T *process)
80{
81 if (/* some context is current */
82 (process->context_current_count != 0) ||
83 /* egl is initialised */
84 process->inited) {
85 return;
86 }
87
88 /*
89 Prompt the server to unload Khronos VLL if it can,
90 and wait until it is done
91 */
92#ifdef RPC_LIBRARY //TODO: not thread safe
93 if (process->connected) {
94 const KHRONOS_FUNC_TABLE_T *func_table;
95 func_table = khronos_server_lock_func_table(client_library_get_connection());
96 if (func_table != NULL)
97 {
98 func_table->khrn_misc_try_unload_impl();
99 khronos_server_disconnect();
100 }
101 khronos_server_unlock_func_table();
102
103 process->connected = false;
104 }
105#else
106 {
107 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
108 RPC_INT_RES(RPC_CALL0_RES(khrn_misc_try_unload_impl, thread, KHRNMISCTRYUNLOAD_ID)); // return unimportant - read is just to cause blocking
109 }
110#endif
111}
112
113/*
114 void client_process_state_init(CLIENT_PROCESS_STATE_T *state)
115
116 Implementation notes:
117
118 Does nothing if already initialised.
119
120 Preconditions:
121
122 process is a valid pointer
123 Thread owns EGL lock
124 client_process_state_term must be called at some point after calling this function if we return true.
125
126 Postconditions:
127
128 Either:
129 - an error occurred and false is returned, or
130 - process->inited is true
131
132 Invariants preserved:
133
134 All invariants on CLIENT_PROCESS_STATE_T
135 (CLIENT_PROCESS_STATE_INITED_SANITY)
136
137 Invariants used:
138
139 -
140*/
141
142bool client_process_state_init(CLIENT_PROCESS_STATE_T *process)
143{
144 if (!process->inited) {
145 if (!khrn_pointer_map_init(&process->contexts, 64))
146 return false;
147
148 if (!khrn_pointer_map_init(&process->surfaces, 64))
149 {
150 khrn_pointer_map_term(&process->contexts);
151 return false;
152 }
153 if (!khrn_pointer_map_init(&process->windows, 64))
154 {
155 khrn_pointer_map_term(&process->contexts);
156 khrn_pointer_map_term(&process->surfaces);
157 return false;
158 }
159
160#if EGL_KHR_sync
161 if (!khrn_pointer_map_init(&process->syncs, 64))
162 {
163 khrn_pointer_map_term(&process->contexts);
164 khrn_pointer_map_term(&process->surfaces);
165 khrn_pointer_map_term(&process->windows);
166 return false;
167 }
168#endif
169#if EGL_BRCM_global_image && EGL_KHR_image
170 khrn_global_image_map_init(&process->global_image_egl_images, 8);
171#endif
172 process->next_surface = 1;
173 process->next_context = 1;
174#if EGL_KHR_sync
175 process->next_sync = 0x80000001;
176#endif
177#if EGL_BRCM_global_image && EGL_KHR_image
178 process->next_global_image_egl_image = 1 << 31;
179#endif
180
181#if EGL_BRCM_perf_monitor
182 process->perf_monitor_inited = false;
183#endif
184
185#ifdef RPC_LIBRARY
186 if (!process->connected) {
187 process->khrn_connection.make_current_func = client_library_send_make_current;
188 khronos_server_lock_func_table(&process->khrn_connection);
189 khronos_server_connect(&process->khrn_connection);
190 khronos_server_unlock_func_table();
191 RPC_CALL0(khrn_misc_startup_impl, NULL, no_id);
192
193 process->connected = true;
194 }
195#endif
196
197 process->inited = true;
198 }
199
200#ifndef ABSTRACT_PLATFORM
201#if defined(ANDROID) && !defined (ANDROID_HWCOMPOSER)
202 egl_config_install_configs(1); // T-format configs
203#else
204 egl_config_install_configs(0); // RSO configs
205#endif
206#endif
207
208 return true;
209}
210
211#include "interface/khronos/common/khrn_client_cr.c"
212
213/*
214 void client_process_state_term(CLIENT_PROCESS_STATE_T *process)
215
216 Implementation notes:
217
218 Does nothing if already terminated.
219
220 Preconditions:
221
222 process is a valid pointer
223 Thread owns EGL lock
224
225 Postconditions:
226
227 process->inited is false.
228
229 Invariants preserved:
230
231 (EGL_CONTEXT_IS_DESTROYED)
232 (CLIENT_PROCESS_STATE_INITED_SANITY)
233
234 Invariants used:
235
236 -
237*/
238
239void client_process_state_term(CLIENT_PROCESS_STATE_T *process)
240{
241 if (process->inited) {
242 khrn_pointer_map_iterate(&process->contexts, callback_destroy_context, NULL);
243 khrn_pointer_map_term(&process->contexts);
244
245 khrn_pointer_map_iterate(&process->surfaces, callback_destroy_surface, NULL);
246 khrn_pointer_map_term(&process->surfaces);
247
248 khrn_pointer_map_term(&process->windows);
249
250#if EGL_KHR_sync
251 egl_sync_destroy_all(&process->syncs);
252 khrn_pointer_map_term(&process->syncs);
253#endif
254
255#if EGL_BRCM_global_image && EGL_KHR_image
256 khrn_global_image_map_term(&process->global_image_egl_images);
257#endif
258
259#if EGL_BRCM_perf_monitor
260 egl_perf_monitor_term(process);
261#endif
262
263#if EGL_BRCM_driver_monitor
264 egl_driver_monitor_term(process);
265#endif
266
267 process->inited = false;
268 }
269}
270
271CLIENT_PROCESS_STATE_T client_process_state = {
272#ifdef RPC_LIBRARY
273 false, /* not connected */
274#endif
275 0, /* nothing current */
276 false}; /* not inited */
277
278void client_thread_state_init(CLIENT_THREAD_STATE_T *state)
279{
280 state->error = EGL_SUCCESS;
281
282 state->bound_api = EGL_OPENGL_ES_API;
283
284 state->opengl.context = 0;
285 state->opengl.draw = 0;
286 state->opengl.read = 0;
287
288 state->openvg.context = 0;
289 state->openvg.draw = 0;
290 state->openvg.read = 0;
291
292 state->high_priority = false;
293
294 state->merge_pos = 0;
295 state->merge_end = 0;
296
297 state->glgeterror_hack = 0;
298 state->async_error_notification = false;
299}
300
301void client_thread_state_term(CLIENT_THREAD_STATE_T *state)
302{
303 // TODO: termination
304 platform_term_rpc( state );
305}
306
307EGL_CONTEXT_T *client_egl_get_context(CLIENT_THREAD_STATE_T *thread, CLIENT_PROCESS_STATE_T *process, EGLContext ctx)
308{
309 EGL_CONTEXT_T *context = (EGL_CONTEXT_T *)khrn_pointer_map_lookup(&process->contexts, (uint32_t)(size_t)ctx);
310
311 vcos_assert(!context || !context->is_destroyed);
312
313 if (!context)
314 thread->error = EGL_BAD_CONTEXT;
315
316 return context;
317}
318
319EGL_SURFACE_T *client_egl_get_surface(CLIENT_THREAD_STATE_T *thread, CLIENT_PROCESS_STATE_T *process, EGLSurface surf)
320{
321 EGL_SURFACE_T *surface = (EGL_SURFACE_T *)khrn_pointer_map_lookup(&process->surfaces, (uint32_t)(size_t)surf);
322
323 vcos_assert (!surface || !surface->is_destroyed);
324
325 if (!surface)
326 thread->error = EGL_BAD_SURFACE;
327
328#if EGL_KHR_lock_surface
329 if (surface && surface->is_locked) {
330 thread->error = EGL_BAD_ACCESS;
331 surface = NULL;
332 }
333#endif
334
335 return surface;
336}
337
338/*
339 We don't actually insist that the surface is locked. But unlike client_egl_get_surface, we don't throw an
340 error if it isn't.
341*/
342EGL_SURFACE_T *client_egl_get_locked_surface(CLIENT_THREAD_STATE_T *thread, CLIENT_PROCESS_STATE_T *process, EGLSurface surf)
343{
344 EGL_SURFACE_T *surface = (EGL_SURFACE_T *)khrn_pointer_map_lookup(&process->surfaces, (uint32_t)(size_t)surf);
345
346 vcos_assert (!surface || !surface->is_destroyed);
347
348 if (!surface)
349 thread->error = EGL_BAD_SURFACE;
350
351 return surface;
352}
353
354/*
355 * just return if we've seen window before
356 */
357EGLNativeWindowType client_egl_get_window(CLIENT_THREAD_STATE_T *thread, CLIENT_PROCESS_STATE_T *process, EGLNativeWindowType window)
358{
359 EGLNativeWindowType win = (EGLNativeWindowType)khrn_pointer_map_lookup(&process->windows, (uint32_t)(size_t)window);
360
361 return win;
362}
363
364static uint32_t convert_gltype(EGL_CONTEXT_TYPE_T type)
365{
366 switch (type) {
367 case OPENGL_ES_11: return EGL_SERVER_GL11;
368 case OPENGL_ES_20: return EGL_SERVER_GL20;
369 default: UNREACHABLE(); return 0;
370 }
371}
372
373void client_send_make_current(CLIENT_THREAD_STATE_T *thread)
374{
375 uint64_t pid = rpc_get_client_id(thread);
376 uint32_t gltype = thread->opengl.context ? convert_gltype(thread->opengl.context->type) : 0;
377 EGL_GL_CONTEXT_ID_T servergl = thread->opengl.context ? thread->opengl.context->servercontext : EGL_SERVER_NO_GL_CONTEXT;
378 EGL_SURFACE_ID_T servergldraw = thread->opengl.draw ? thread->opengl.draw->serverbuffer : EGL_SERVER_NO_SURFACE;
379 EGL_SURFACE_ID_T serverglread = thread->opengl.read ? thread->opengl.read->serverbuffer : EGL_SERVER_NO_SURFACE;
380 EGL_VG_CONTEXT_ID_T servervg = thread->openvg.context ? thread->openvg.context->servercontext : EGL_SERVER_NO_VG_CONTEXT;
381 EGL_SURFACE_ID_T servervgsurf = thread->openvg.draw ? thread->openvg.draw->serverbuffer : EGL_SERVER_NO_SURFACE;
382
383 /*
384 if the size of this call in the merge buffer changes,
385 CLIENT_MAKE_CURRENT_SIZE in khrn_client.h should be updated
386 */
387
388
389 if (!thread->opengl.context || !thread->opengl.draw)
390 {
391 vcos_log_trace("Send null make current %x %x",
392 (unsigned int)(char *)thread->opengl.context, (unsigned int)(char *)thread->opengl.draw);
393 }
394 else
395 {
396 vcos_log_trace("Send make current %d[%d %s%s] %d[%d %d%s]",
397 (int)thread->opengl.context->name,
398 thread->opengl.context->servercontext,
399 thread->opengl.context->is_current ? " C" : "",
400 thread->opengl.context->is_destroyed ? " D" : "",
401 (int)thread->opengl.draw->name,
402 thread->opengl.draw->serverbuffer,
403 thread->opengl.draw->context_binding_count,
404 thread->opengl.draw->is_destroyed ? " D" : "");
405 }
406
407 RPC_CALL8_MAKECURRENT(eglIntMakeCurrent_impl,
408 thread,
409 EGLINTMAKECURRENT_ID,
410 RPC_UINT((uint32_t)pid),
411 RPC_UINT((uint32_t)(pid >> 32)),
412 RPC_UINT(gltype),
413 RPC_UINT(servergl),
414 RPC_UINT(servergldraw),
415 RPC_UINT(serverglread),
416 RPC_UINT(servervg),
417 RPC_UINT(servervgsurf));
418}
419
420PLATFORM_TLS_T client_tls;
421PLATFORM_MUTEX_T client_mutex;
422#ifdef CLIENT_THREAD_IS_PROCESS
423PLATFORM_TLS_T client_tls_process;
424PLATFORM_TLS_T client_tls_mutex;
425#endif
426
427bool client_process_attach()
428{
429 KHR_STATUS_T status;
430 status = platform_tls_create(&client_tls);
431 if (status != KHR_SUCCESS) {
432 return false;
433 }
434
435#ifdef CLIENT_THREAD_IS_PROCESS
436 status = platform_tls_create(&client_tls_process);
437 if (status != KHR_SUCCESS) {
438 return false;
439 }
440
441 status = platform_tls_create(&client_tls_mutex);
442 if (status != KHR_SUCCESS) {
443 return false;
444 }
445#endif
446
447 status = platform_mutex_create(&client_mutex);
448
449 if (status != KHR_SUCCESS) {
450 platform_tls_destroy(client_tls);
451 return false;
452 }
453 if (!RPC_INIT()) {
454 platform_mutex_destroy(&client_mutex);
455 platform_tls_destroy(client_tls);
456 return false;
457 }
458 return true;
459}
460
461bool client_thread_attach()
462{
463 CLIENT_THREAD_STATE_T *state = (CLIENT_THREAD_STATE_T *)khrn_platform_malloc(sizeof(CLIENT_THREAD_STATE_T), "CLIENT_THREAD_STATE_T");
464
465 if (!state)
466 return false;
467
468 client_thread_state_init(state);
469
470 platform_tls_set(client_tls, state);
471
472#ifdef CLIENT_THREAD_IS_PROCESS
473 { //add mutex into thread's tls
474 KHR_STATUS_T status;
475 PLATFORM_MUTEX_T *local_mutex = (PLATFORM_MUTEX_T*)vcos_tls_get(client_tls_mutex);
476
477 if (!local_mutex)
478 {
479 local_mutex = (PLATFORM_MUTEX_T*)khrn_platform_malloc(sizeof(PLATFORM_MUTEX_T),"thread mutex");
480 if (!local_mutex)
481 return false;
482
483 status = platform_mutex_create(local_mutex);
484 if (status != KHR_SUCCESS) {
485 khrn_platform_free(local_mutex);
486 return false;
487 }
488
489 vcos_tls_set(client_tls_mutex,local_mutex);
490 }
491 }
492#endif
493
494#ifndef RPC_LIBRARY //TODO
495 client_send_make_current(state);
496#endif
497 return true;
498}
499
500void client_thread_detach(void *dummy)
501{
502 CLIENT_THREAD_STATE_T *state = CLIENT_GET_THREAD_STATE();
503 UNUSED(dummy);
504
505 platform_tls_remove(client_tls);
506 client_thread_state_term(state);
507
508 khrn_platform_free(state);
509 platform_maybe_free_process();
510
511#ifdef CLIENT_THREAD_IS_PROCESS
512 {
513 CLIENT_PROCESS_STATE_T *process = CLIENT_GET_PROCESS_STATE();
514 khrn_platform_free(process);
515 platform_tls_remove(client_tls_process);
516 }
517
518 {
519 PLATFORM_MUTEX_T *local_mutex = (PLATFORM_MUTEX_T*)vcos_tls_get(client_tls_mutex);
520 vcos_assert(local_mutex);
521
522 platform_mutex_destroy(local_mutex);
523
524 khrn_platform_free(local_mutex);
525 platform_tls_remove(client_tls_mutex);
526 }
527#endif
528}
529
530void client_process_detach()
531{
532 RPC_TERM();
533 platform_tls_destroy(client_tls);
534 platform_mutex_destroy(&client_mutex);
535
536#ifdef CLIENT_THREAD_IS_PROCESS
537 platform_tls_destroy(client_tls_process);
538#endif
539}
540
541#ifdef RPC_LIBRARY
542KHRONOS_SERVER_CONNECTION_T *client_library_get_connection(void)
543{
544 return &client_process_state.khrn_connection;
545}
546
547#endif
548
549#if defined(RPC_LIBRARY) || defined(RPC_DIRECT_MULTI)
550
551void client_library_send_make_current(const KHRONOS_FUNC_TABLE_T *func_table)
552{
553 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
554 if (thread->opengl.context || thread->openvg.context)
555 {
556 uint64_t pid = rpc_get_client_id(thread);
557 uint32_t gltype = thread->opengl.context ? convert_gltype(thread->opengl.context->type) : 0;
558 EGL_GL_CONTEXT_ID_T servergl = thread->opengl.context ? thread->opengl.context->servercontext : EGL_SERVER_NO_GL_CONTEXT;
559 EGL_SURFACE_ID_T servergldraw = thread->opengl.draw ? thread->opengl.draw->serverbuffer : EGL_SERVER_NO_SURFACE;
560 EGL_SURFACE_ID_T serverglread = thread->opengl.read ? thread->opengl.read->serverbuffer : EGL_SERVER_NO_SURFACE;
561 EGL_VG_CONTEXT_ID_T servervg = thread->openvg.context ? thread->openvg.context->servercontext : EGL_SERVER_NO_VG_CONTEXT;
562 EGL_SURFACE_ID_T servervgsurf = thread->openvg.draw ? thread->openvg.draw->serverbuffer : EGL_SERVER_NO_SURFACE;
563
564 /*
565 if the size of this call in the merge buffer changes,
566 CLIENT_MAKE_CURRENT_SIZE in khrn_client.h should be updated
567 */
568
569 func_table->eglIntMakeCurrent_impl(
570 (uint32_t)pid,
571 (uint32_t)(pid >> 32),
572 gltype,
573 servergl,
574 servergldraw,
575 serverglread,
576 servervg,
577 servervgsurf);
578 }
579}
580
581#endif
582
583#ifdef GL_GET_ERROR_ASYNC
584static void callback_set_error(KHRN_POINTER_MAP_T *map, uint32_t key, void *value, void *data)
585{
586 EGL_CONTEXT_T *context = (EGL_CONTEXT_T *)value;
587
588 UNUSED(map);
589 UNUSED_NDEBUG(key);
590
591 vcos_assert( context != NULL );
592 vcos_assert((uintptr_t)key == (uintptr_t)context->name);
593
594 if (context->servercontext == *((uint32_t *)data)){
595 CLIENT_THREAD_STATE_T *thread = context->thread;
596 /* todo: VG */
597 if (thread && IS_OPENGLES_11_OR_20(thread)) {
598 vcos_log_error("GL OOM context %d", context->servercontext);
599 glxx_set_error(GLXX_GET_CLIENT_STATE(thread), GL_OUT_OF_MEMORY);
600 }
601 }
602}
603
604void client_set_error(uint32_t server_context_name)
605{
606 CLIENT_PROCESS_STATE_T *process;
607 CLIENT_LOCK();
608 process = CLIENT_GET_PROCESS_STATE();
609 khrn_pointer_map_iterate(&process->contexts, callback_set_error, &server_context_name);
610 CLIENT_UNLOCK();
611}
612#endif
613