/* Copyright (c) 2012, Broadcom Europe Ltd All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef KHRN_CLIENT_H #define KHRN_CLIENT_H typedef struct CLIENT_PROCESS_STATE CLIENT_PROCESS_STATE_T; typedef struct CLIENT_THREAD_STATE CLIENT_THREAD_STATE_T; #include "interface/khronos/common/khrn_client_platform.h" #include "interface/khronos/egl/egl_client_context.h" #include "interface/khronos/egl/egl_client_surface.h" #include "interface/khronos/include/EGL/eglext.h" #include "interface/khronos/common/khrn_client_pointermap.h" #ifdef RPC_LIBRARY #include "middleware/khronos/common/khrn_misc.h" #include "applications/vmcs/khronos/khronos_server.h" #elif defined(RPC_DIRECT_MULTI) #include "middleware/khronos/common/khrn_misc.h" #endif /* must be after EGL/eglext.h */ #if EGL_BRCM_global_image && EGL_KHR_image #include "interface/khronos/common/khrn_client_global_image_map.h" #endif extern void client_try_unload_server(CLIENT_PROCESS_STATE_T *process); /* per-thread state - EGL error - EGL bound API - EGL context and surfaces for each API - RPC merge buffer */ #define MERGE_BUFFER_SIZE 4080 typedef struct { EGL_CONTEXT_T *context; EGL_SURFACE_T *draw; EGL_SURFACE_T *read; } EGL_CURRENT_T; struct CLIENT_THREAD_STATE { /* error Invariant: (CLIENT_THREAD_STATE_ERROR) error is one of EGL_SUCCESS EGL_NOT_INITIALIZED EGL_BAD_ACCESS EGL_BAD_ALLOC EGL_BAD_ATTRIBUTE EGL_BAD_CONTEXT EGL_BAD_CONFIG EGL_BAD_CURRENT SURFACE EGL_BAD_DISPLAY EGL_BAD_SURFACE EGL_BAD_MATCH EGL_BAD_PARAMETER EGL_BAD_NATIVE PIXMAP EGL_BAD_NATIVE WINDOW EGL_CONTEXT_LOST */ EGLint error; EGLenum bound_api; /* handles to current display, context and surfaces for each API Availability: Thread owns EGL lock */ EGL_CURRENT_T opengl; EGL_CURRENT_T openvg; /* rpc stuff */ bool high_priority; uint8_t merge_buffer[MERGE_BUFFER_SIZE]; uint32_t merge_pos; uint32_t merge_end; /* Try to reduce impact of repeated consecutive glGetError() calls */ int32_t glgeterror_hack; bool async_error_notification; }; extern void client_thread_state_init(CLIENT_THREAD_STATE_T *state); extern void client_thread_state_term(CLIENT_THREAD_STATE_T *state); extern PLATFORM_TLS_T client_tls; /* CLIENT_GET_THREAD_STATE Implementation notes: TODO: make sure this gets code-reviewed Preconditions: - Postconditions: Result is a valid pointer to a thread-local CLIENT_THREAD_STATE_T structure. Invariants preserved: - Invariants used: - */ static INLINE CLIENT_THREAD_STATE_T *CLIENT_GET_THREAD_STATE(void) { CLIENT_THREAD_STATE_T *tls; tls = (CLIENT_THREAD_STATE_T *)platform_tls_get(client_tls); if( tls && tls->glgeterror_hack ) { tls->glgeterror_hack--; } return tls; } static INLINE CLIENT_THREAD_STATE_T *CLIENT_GET_CHECK_THREAD_STATE(void) { return (CLIENT_THREAD_STATE_T *)platform_tls_get_check(client_tls); } /* per-process state - EGL initialization stage - EGL contexts and surfaces - EGL counters */ struct CLIENT_PROCESS_STATE { #ifdef RPC_LIBRARY /* called khronos_server_connect? this is valid even if !inited */ bool connected; #endif /* number of current contexts across all threads in this process. this is valid even if !inited */ uint32_t context_current_count; /* inited Specifies whether the structure has been initialised and all of the other members are valid. inited is true between eglInitialise/eglTerminate. threads can still have things current when !inited Invariants: (CLIENT_PROCESS_STATE_INITED_SANITY) Only client_process_state_init/client_process_state_term modify this value */ bool inited; /* contexts A map from context id (EGLContext) to EGL_CONTEXT_T TODO: use pointers as key rather than integers Validity: inited is true Invariants: (CLIENT_PROCESS_STATE_CONTEXTS) If id is a key in contexts: contexts[id].name == id contexts[id].is_destroyed == false */ KHRN_POINTER_MAP_T contexts; /* surfaces A map from context id (EGLContext) to EGL_SURFACE_T Validity: inited is true Invariants: (CLIENT_PROCESS_STATE_SURFACES) If id is a key in surfaces: surfaces[id].name == id surfaces[id].is_destroyed == false */ KHRN_POINTER_MAP_T surfaces; /* * some platforms (e.g. Android) need to maintain a list of * known windows */ KHRN_POINTER_MAP_T windows; #if EGL_KHR_sync /* syncs Validity: inited is true */ KHRN_POINTER_MAP_T syncs; #endif #if EGL_BRCM_global_image && EGL_KHR_image KHRN_GLOBAL_IMAGE_MAP_T global_image_egl_images; #endif /* next_surface Implementation notes: TODO: these could theoretically overflow Validity: inited is true Invariant: (CLIENT_PROCESS_STATE_NEXT_SURFACE) next_surface is greater than every key in surfaces next_surface >= 1 */ uint32_t next_surface; /* next_context Validity: inited is true */ uint32_t next_context; #if EGL_KHR_sync /* next_sync Validity: inited is true */ uint32_t next_sync; #endif #if EGL_BRCM_global_image && EGL_KHR_image uint32_t next_global_image_egl_image; #endif #if EGL_BRCM_perf_monitor /* perf_monitor_inited Validity: inited is true */ bool perf_monitor_inited; #endif #if EGL_BRCM_driver_monitor /* driver_monitor_inited Validity: inited is true */ bool driver_monitor_inited; #endif #ifdef RPC_LIBRARY KHRONOS_SERVER_CONNECTION_T khrn_connection; #endif }; extern bool client_process_state_init(CLIENT_PROCESS_STATE_T *process); extern void client_process_state_term(CLIENT_PROCESS_STATE_T *process); extern CLIENT_PROCESS_STATE_T client_process_state; /* CLIENT_GET_PROCESS_STATE() Returns the process-global CLIENT_PROCESS_STATE_T object. */ #ifdef CLIENT_THREAD_IS_PROCESS extern PLATFORM_TLS_T client_tls_process; extern PLATFORM_TLS_T client_tls_mutex; extern void *platform_tls_get_process(PLATFORM_TLS_T tls); #endif static INLINE CLIENT_PROCESS_STATE_T *CLIENT_GET_PROCESS_STATE(void) { #ifdef CLIENT_THREAD_IS_PROCESS //each thread has its own client_process_state return (CLIENT_PROCESS_STATE_T *)platform_tls_get_process(client_tls_process); #else return &client_process_state; #endif } /* exposed bits of EGL */ CLIENT_PROCESS_STATE_T *client_egl_get_process_state(CLIENT_THREAD_STATE_T *thread, EGLDisplay dpy, EGLBoolean check_inited); EGL_CONTEXT_T *client_egl_get_context(CLIENT_THREAD_STATE_T *thread, CLIENT_PROCESS_STATE_T *process, EGLContext ctx); EGL_SURFACE_T *client_egl_get_surface(CLIENT_THREAD_STATE_T *thread, CLIENT_PROCESS_STATE_T *process, EGLSurface surf); EGL_SURFACE_T *client_egl_get_locked_surface(CLIENT_THREAD_STATE_T *thread, CLIENT_PROCESS_STATE_T *process, EGLSurface surf); EGLNativeWindowType client_egl_get_window(CLIENT_THREAD_STATE_T *thread, CLIENT_PROCESS_STATE_T *process, EGLNativeWindowType window); /* client state */ #define CLIENT_MAKE_CURRENT_SIZE 36 /* RPC_CALL8 */ extern void client_send_make_current(CLIENT_THREAD_STATE_T *thread); extern void client_set_error(uint32_t server_context_name); /* big giant lock */ extern PLATFORM_MUTEX_T client_mutex; /* CLIENT_LOCK() Acquires EGL lock. Implementation notes: TODO make sure this gets reviewed Preconditions: TODO: check mutex hierarchy methodology Mutex: >(MUTEX_EGL_LOCK) Is being called from a function which _always_ subsequently calls CLIENT_UNLOCK() Postconditions: Mutex: (MUTEX_EGL_LOCK) Thread owns EGL lock */ static INLINE void CLIENT_LOCK(void) { platform_client_lock(); } /* CLIENT_UNLOCK() Releases EGL lock. Implementation notes: TODO make sure this gets reviewed Preconditions: Mutex: (MUTEX_EGL_LOCK) Thread owns EGL lock Is being called from a function which has _always_ previously called CLIENT_LOCK() Postconditions: Mutex: >(MUTEX_EGL_LOCK) */ static INLINE void CLIENT_UNLOCK(void) { platform_client_release(); } /* bool CLIENT_LOCK_AND_GET_STATES(EGLDisplay dpy, CLIENT_THREAD_STATE_T **thread, CLIENT_PROCESS_STATE_T **process) Try to acquire EGL lock and get thread and process state. Implementation notes: TODO make sure this gets reviewed Preconditions: thread is a valid pointer to a thread* process is a valid pointer to a process* Mutex: >(MUTEX_EGL_LOCK) Is being called from a function which calls CLIENT_UNLOCK() if we return true Postconditions: The following conditions cause error to assume the specified value EGL_BAD_DISPLAY An EGLDisplay argument does not name a valid EGLDisplay EGL_NOT_INITIALIZED EGL is not initialized for the specified display. if more than one condition holds, the first error is generated. Either: Mutex: (MUTEX_EGL_LOCK) Thread owns EGL lock result is true Or: Nothing changes result is false */ static INLINE bool CLIENT_LOCK_AND_GET_STATES(EGLDisplay dpy, CLIENT_THREAD_STATE_T **thread, CLIENT_PROCESS_STATE_T **process) { *thread = CLIENT_GET_THREAD_STATE(); CLIENT_LOCK(); *process = client_egl_get_process_state(*thread, dpy, EGL_TRUE); if (*process != NULL) return true; else { CLIENT_UNLOCK(); return false; } } /* process and thread attach/detach hooks */ #ifdef __cplusplus extern "C" { #endif extern bool client_process_attach(void); extern bool client_thread_attach(void); extern void client_thread_detach(void *dummy); extern void client_process_detach(void); #ifdef RPC_LIBRARY extern KHRONOS_SERVER_CONNECTION_T *client_library_get_connection(void); extern void client_library_send_make_current(const KHRONOS_FUNC_TABLE_T *func_table); #elif defined(RPC_DIRECT_MULTI) extern void client_library_send_make_current(const KHRONOS_FUNC_TABLE_T *func_table); #endif #ifdef __cplusplus } #endif #endif