QortalOS Brooklyn for Raspberry Pi 4
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

612 lines
17 KiB

/*
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.
*/
#define VCOS_LOG_CATEGORY (&khrn_client_log)
#include "interface/khronos/common/khrn_int_common.h"
#include "interface/khronos/common/khrn_client_check_types.h"
#include "interface/khronos/common/khrn_client.h"
#include "interface/khronos/common/khrn_client_rpc.h"
#include "interface/khronos/egl/egl_client_config.h"
#include "interface/khronos/glxx/glxx_client.h"
#if defined(V3D_LEAN)
#include "interface/khronos/common/khrn_int_misc_impl.h"
#endif
#if EGL_KHR_sync
#include "interface/khronos/ext/egl_khr_sync_client.h"
#endif
#if EGL_BRCM_perf_monitor
#include "interface/khronos/ext/egl_brcm_perf_monitor_client.h"
#endif
#if EGL_BRCM_driver_monitor
#include "interface/khronos/ext/egl_brcm_driver_monitor_client.h"
#endif
#ifdef RPC_LIBRARY
#include "middleware/dlloader/dlfcn.h"
#include "applications/vmcs/khronos/khronos_server.h"
#endif
VCOS_LOG_CAT_T khrn_client_log = VCOS_LOG_INIT("khrn_client", VCOS_LOG_WARN);
/*
void client_try_unload_server(CLIENT_PROCESS_STATE_T *process)
Preconditions:
-
Postconditions:
-
Invariants preserved:
-
Invariants used:
-
*/
void client_try_unload_server(CLIENT_PROCESS_STATE_T *process)
{
if (/* some context is current */
(process->context_current_count != 0) ||
/* egl is initialised */
process->inited) {
return;
}
/*
Prompt the server to unload Khronos VLL if it can,
and wait until it is done
*/
#ifdef RPC_LIBRARY //TODO: not thread safe
if (process->connected) {
const KHRONOS_FUNC_TABLE_T *func_table;
func_table = khronos_server_lock_func_table(client_library_get_connection());
if (func_table != NULL)
{
func_table->khrn_misc_try_unload_impl();
khronos_server_disconnect();
}
khronos_server_unlock_func_table();
process->connected = false;
}
#else
{
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
RPC_INT_RES(RPC_CALL0_RES(khrn_misc_try_unload_impl, thread, KHRNMISCTRYUNLOAD_ID)); // return unimportant - read is just to cause blocking
}
#endif
}
/*
void client_process_state_init(CLIENT_PROCESS_STATE_T *state)
Implementation notes:
Does nothing if already initialised.
Preconditions:
process is a valid pointer
Thread owns EGL lock
client_process_state_term must be called at some point after calling this function if we return true.
Postconditions:
Either:
- an error occurred and false is returned, or
- process->inited is true
Invariants preserved:
All invariants on CLIENT_PROCESS_STATE_T
(CLIENT_PROCESS_STATE_INITED_SANITY)
Invariants used:
-
*/
bool client_process_state_init(CLIENT_PROCESS_STATE_T *process)
{
if (!process->inited) {
if (!khrn_pointer_map_init(&process->contexts, 64))
return false;
if (!khrn_pointer_map_init(&process->surfaces, 64))
{
khrn_pointer_map_term(&process->contexts);
return false;
}
if (!khrn_pointer_map_init(&process->windows, 64))
{
khrn_pointer_map_term(&process->contexts);
khrn_pointer_map_term(&process->surfaces);
return false;
}
#if EGL_KHR_sync
if (!khrn_pointer_map_init(&process->syncs, 64))
{
khrn_pointer_map_term(&process->contexts);
khrn_pointer_map_term(&process->surfaces);
khrn_pointer_map_term(&process->windows);
return false;
}
#endif
#if EGL_BRCM_global_image && EGL_KHR_image
khrn_global_image_map_init(&process->global_image_egl_images, 8);
#endif
process->next_surface = 1;
process->next_context = 1;
#if EGL_KHR_sync
process->next_sync = 0x80000001;
#endif
#if EGL_BRCM_global_image && EGL_KHR_image
process->next_global_image_egl_image = 1 << 31;
#endif
#if EGL_BRCM_perf_monitor
process->perf_monitor_inited = false;
#endif
#ifdef RPC_LIBRARY
if (!process->connected) {
process->khrn_connection.make_current_func = client_library_send_make_current;
khronos_server_lock_func_table(&process->khrn_connection);
khronos_server_connect(&process->khrn_connection);
khronos_server_unlock_func_table();
RPC_CALL0(khrn_misc_startup_impl, NULL, no_id);
process->connected = true;
}
#endif
process->inited = true;
}
#ifndef ABSTRACT_PLATFORM
#if defined(ANDROID) && !defined (ANDROID_HWCOMPOSER)
egl_config_install_configs(1); // T-format configs
#else
egl_config_install_configs(0); // RSO configs
#endif
#endif
return true;
}
#include "interface/khronos/common/khrn_client_cr.c"
/*
void client_process_state_term(CLIENT_PROCESS_STATE_T *process)
Implementation notes:
Does nothing if already terminated.
Preconditions:
process is a valid pointer
Thread owns EGL lock
Postconditions:
process->inited is false.
Invariants preserved:
(EGL_CONTEXT_IS_DESTROYED)
(CLIENT_PROCESS_STATE_INITED_SANITY)
Invariants used:
-
*/
void client_process_state_term(CLIENT_PROCESS_STATE_T *process)
{
if (process->inited) {
khrn_pointer_map_iterate(&process->contexts, callback_destroy_context, NULL);
khrn_pointer_map_term(&process->contexts);
khrn_pointer_map_iterate(&process->surfaces, callback_destroy_surface, NULL);
khrn_pointer_map_term(&process->surfaces);
khrn_pointer_map_term(&process->windows);
#if EGL_KHR_sync
egl_sync_destroy_all(&process->syncs);
khrn_pointer_map_term(&process->syncs);
#endif
#if EGL_BRCM_global_image && EGL_KHR_image
khrn_global_image_map_term(&process->global_image_egl_images);
#endif
#if EGL_BRCM_perf_monitor
egl_perf_monitor_term(process);
#endif
#if EGL_BRCM_driver_monitor
egl_driver_monitor_term(process);
#endif
process->inited = false;
}
}
CLIENT_PROCESS_STATE_T client_process_state = {
#ifdef RPC_LIBRARY
false, /* not connected */
#endif
0, /* nothing current */
false}; /* not inited */
void client_thread_state_init(CLIENT_THREAD_STATE_T *state)
{
state->error = EGL_SUCCESS;
state->bound_api = EGL_OPENGL_ES_API;
state->opengl.context = 0;
state->opengl.draw = 0;
state->opengl.read = 0;
state->openvg.context = 0;
state->openvg.draw = 0;
state->openvg.read = 0;
state->high_priority = false;
state->merge_pos = 0;
state->merge_end = 0;
state->glgeterror_hack = 0;
state->async_error_notification = false;
}
void client_thread_state_term(CLIENT_THREAD_STATE_T *state)
{
// TODO: termination
platform_term_rpc( state );
}
EGL_CONTEXT_T *client_egl_get_context(CLIENT_THREAD_STATE_T *thread, CLIENT_PROCESS_STATE_T *process, EGLContext ctx)
{
EGL_CONTEXT_T *context = (EGL_CONTEXT_T *)khrn_pointer_map_lookup(&process->contexts, (uint32_t)(size_t)ctx);
vcos_assert(!context || !context->is_destroyed);
if (!context)
thread->error = EGL_BAD_CONTEXT;
return context;
}
EGL_SURFACE_T *client_egl_get_surface(CLIENT_THREAD_STATE_T *thread, CLIENT_PROCESS_STATE_T *process, EGLSurface surf)
{
EGL_SURFACE_T *surface = (EGL_SURFACE_T *)khrn_pointer_map_lookup(&process->surfaces, (uint32_t)(size_t)surf);
vcos_assert (!surface || !surface->is_destroyed);
if (!surface)
thread->error = EGL_BAD_SURFACE;
#if EGL_KHR_lock_surface
if (surface && surface->is_locked) {
thread->error = EGL_BAD_ACCESS;
surface = NULL;
}
#endif
return surface;
}
/*
We don't actually insist that the surface is locked. But unlike client_egl_get_surface, we don't throw an
error if it isn't.
*/
EGL_SURFACE_T *client_egl_get_locked_surface(CLIENT_THREAD_STATE_T *thread, CLIENT_PROCESS_STATE_T *process, EGLSurface surf)
{
EGL_SURFACE_T *surface = (EGL_SURFACE_T *)khrn_pointer_map_lookup(&process->surfaces, (uint32_t)(size_t)surf);
vcos_assert (!surface || !surface->is_destroyed);
if (!surface)
thread->error = EGL_BAD_SURFACE;
return surface;
}
/*
* just return if we've seen window before
*/
EGLNativeWindowType client_egl_get_window(CLIENT_THREAD_STATE_T *thread, CLIENT_PROCESS_STATE_T *process, EGLNativeWindowType window)
{
EGLNativeWindowType win = (EGLNativeWindowType)khrn_pointer_map_lookup(&process->windows, (uint32_t)(size_t)window);
return win;
}
static uint32_t convert_gltype(EGL_CONTEXT_TYPE_T type)
{
switch (type) {
case OPENGL_ES_11: return EGL_SERVER_GL11;
case OPENGL_ES_20: return EGL_SERVER_GL20;
default: UNREACHABLE(); return 0;
}
}
void client_send_make_current(CLIENT_THREAD_STATE_T *thread)
{
uint64_t pid = rpc_get_client_id(thread);
uint32_t gltype = thread->opengl.context ? convert_gltype(thread->opengl.context->type) : 0;
EGL_GL_CONTEXT_ID_T servergl = thread->opengl.context ? thread->opengl.context->servercontext : EGL_SERVER_NO_GL_CONTEXT;
EGL_SURFACE_ID_T servergldraw = thread->opengl.draw ? thread->opengl.draw->serverbuffer : EGL_SERVER_NO_SURFACE;
EGL_SURFACE_ID_T serverglread = thread->opengl.read ? thread->opengl.read->serverbuffer : EGL_SERVER_NO_SURFACE;
EGL_VG_CONTEXT_ID_T servervg = thread->openvg.context ? thread->openvg.context->servercontext : EGL_SERVER_NO_VG_CONTEXT;
EGL_SURFACE_ID_T servervgsurf = thread->openvg.draw ? thread->openvg.draw->serverbuffer : EGL_SERVER_NO_SURFACE;
/*
if the size of this call in the merge buffer changes,
CLIENT_MAKE_CURRENT_SIZE in khrn_client.h should be updated
*/
if (!thread->opengl.context || !thread->opengl.draw)
{
vcos_log_trace("Send null make current %x %x",
(unsigned int)(char *)thread->opengl.context, (unsigned int)(char *)thread->opengl.draw);
}
else
{
vcos_log_trace("Send make current %d[%d %s%s] %d[%d %d%s]",
(int)thread->opengl.context->name,
thread->opengl.context->servercontext,
thread->opengl.context->is_current ? " C" : "",
thread->opengl.context->is_destroyed ? " D" : "",
(int)thread->opengl.draw->name,
thread->opengl.draw->serverbuffer,
thread->opengl.draw->context_binding_count,
thread->opengl.draw->is_destroyed ? " D" : "");
}
RPC_CALL8_MAKECURRENT(eglIntMakeCurrent_impl,
thread,
EGLINTMAKECURRENT_ID,
RPC_UINT((uint32_t)pid),
RPC_UINT((uint32_t)(pid >> 32)),
RPC_UINT(gltype),
RPC_UINT(servergl),
RPC_UINT(servergldraw),
RPC_UINT(serverglread),
RPC_UINT(servervg),
RPC_UINT(servervgsurf));
}
PLATFORM_TLS_T client_tls;
PLATFORM_MUTEX_T client_mutex;
#ifdef CLIENT_THREAD_IS_PROCESS
PLATFORM_TLS_T client_tls_process;
PLATFORM_TLS_T client_tls_mutex;
#endif
bool client_process_attach()
{
KHR_STATUS_T status;
status = platform_tls_create(&client_tls);
if (status != KHR_SUCCESS) {
return false;
}
#ifdef CLIENT_THREAD_IS_PROCESS
status = platform_tls_create(&client_tls_process);
if (status != KHR_SUCCESS) {
return false;
}
status = platform_tls_create(&client_tls_mutex);
if (status != KHR_SUCCESS) {
return false;
}
#endif
status = platform_mutex_create(&client_mutex);
if (status != KHR_SUCCESS) {
platform_tls_destroy(client_tls);
return false;
}
if (!RPC_INIT()) {
platform_mutex_destroy(&client_mutex);
platform_tls_destroy(client_tls);
return false;
}
return true;
}
bool client_thread_attach()
{
CLIENT_THREAD_STATE_T *state = (CLIENT_THREAD_STATE_T *)khrn_platform_malloc(sizeof(CLIENT_THREAD_STATE_T), "CLIENT_THREAD_STATE_T");
if (!state)
return false;
client_thread_state_init(state);
platform_tls_set(client_tls, state);
#ifdef CLIENT_THREAD_IS_PROCESS
{ //add mutex into thread's tls
KHR_STATUS_T status;
PLATFORM_MUTEX_T *local_mutex = (PLATFORM_MUTEX_T*)vcos_tls_get(client_tls_mutex);
if (!local_mutex)
{
local_mutex = (PLATFORM_MUTEX_T*)khrn_platform_malloc(sizeof(PLATFORM_MUTEX_T),"thread mutex");
if (!local_mutex)
return false;
status = platform_mutex_create(local_mutex);
if (status != KHR_SUCCESS) {
khrn_platform_free(local_mutex);
return false;
}
vcos_tls_set(client_tls_mutex,local_mutex);
}
}
#endif
#ifndef RPC_LIBRARY //TODO
client_send_make_current(state);
#endif
return true;
}
void client_thread_detach(void *dummy)
{
CLIENT_THREAD_STATE_T *state = CLIENT_GET_THREAD_STATE();
UNUSED(dummy);
platform_tls_remove(client_tls);
client_thread_state_term(state);
khrn_platform_free(state);
platform_maybe_free_process();
#ifdef CLIENT_THREAD_IS_PROCESS
{
CLIENT_PROCESS_STATE_T *process = CLIENT_GET_PROCESS_STATE();
khrn_platform_free(process);
platform_tls_remove(client_tls_process);
}
{
PLATFORM_MUTEX_T *local_mutex = (PLATFORM_MUTEX_T*)vcos_tls_get(client_tls_mutex);
vcos_assert(local_mutex);
platform_mutex_destroy(local_mutex);
khrn_platform_free(local_mutex);
platform_tls_remove(client_tls_mutex);
}
#endif
}
void client_process_detach()
{
RPC_TERM();
platform_tls_destroy(client_tls);
platform_mutex_destroy(&client_mutex);
#ifdef CLIENT_THREAD_IS_PROCESS
platform_tls_destroy(client_tls_process);
#endif
}
#ifdef RPC_LIBRARY
KHRONOS_SERVER_CONNECTION_T *client_library_get_connection(void)
{
return &client_process_state.khrn_connection;
}
#endif
#if defined(RPC_LIBRARY) || defined(RPC_DIRECT_MULTI)
void client_library_send_make_current(const KHRONOS_FUNC_TABLE_T *func_table)
{
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
if (thread->opengl.context || thread->openvg.context)
{
uint64_t pid = rpc_get_client_id(thread);
uint32_t gltype = thread->opengl.context ? convert_gltype(thread->opengl.context->type) : 0;
EGL_GL_CONTEXT_ID_T servergl = thread->opengl.context ? thread->opengl.context->servercontext : EGL_SERVER_NO_GL_CONTEXT;
EGL_SURFACE_ID_T servergldraw = thread->opengl.draw ? thread->opengl.draw->serverbuffer : EGL_SERVER_NO_SURFACE;
EGL_SURFACE_ID_T serverglread = thread->opengl.read ? thread->opengl.read->serverbuffer : EGL_SERVER_NO_SURFACE;
EGL_VG_CONTEXT_ID_T servervg = thread->openvg.context ? thread->openvg.context->servercontext : EGL_SERVER_NO_VG_CONTEXT;
EGL_SURFACE_ID_T servervgsurf = thread->openvg.draw ? thread->openvg.draw->serverbuffer : EGL_SERVER_NO_SURFACE;
/*
if the size of this call in the merge buffer changes,
CLIENT_MAKE_CURRENT_SIZE in khrn_client.h should be updated
*/
func_table->eglIntMakeCurrent_impl(
(uint32_t)pid,
(uint32_t)(pid >> 32),
gltype,
servergl,
servergldraw,
serverglread,
servervg,
servervgsurf);
}
}
#endif
#ifdef GL_GET_ERROR_ASYNC
static void callback_set_error(KHRN_POINTER_MAP_T *map, uint32_t key, void *value, void *data)
{
EGL_CONTEXT_T *context = (EGL_CONTEXT_T *)value;
UNUSED(map);
UNUSED_NDEBUG(key);
vcos_assert( context != NULL );
vcos_assert((uintptr_t)key == (uintptr_t)context->name);
if (context->servercontext == *((uint32_t *)data)){
CLIENT_THREAD_STATE_T *thread = context->thread;
/* todo: VG */
if (thread && IS_OPENGLES_11_OR_20(thread)) {
vcos_log_error("GL OOM context %d", context->servercontext);
glxx_set_error(GLXX_GET_CLIENT_STATE(thread), GL_OUT_OF_MEMORY);
}
}
}
void client_set_error(uint32_t server_context_name)
{
CLIENT_PROCESS_STATE_T *process;
CLIENT_LOCK();
process = CLIENT_GET_PROCESS_STATE();
khrn_pointer_map_iterate(&process->contexts, callback_set_error, &server_context_name);
CLIENT_UNLOCK();
}
#endif