|
|
/* |
|
|
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. |
|
|
*/ |
|
|
|
|
|
/* |
|
|
Global preconditions? |
|
|
|
|
|
Server is up (needed by RPC_CALL[n]_RES) |
|
|
|
|
|
|
|
|
Spec ambiguity: |
|
|
|
|
|
What should we do if eglGetError is called twice? Currently we reset the error to EGL_SUCCESS. |
|
|
|
|
|
eglGetConfigs: |
|
|
"The number of configurations is returned in num config" |
|
|
We assume this refers to the number of configurations returned, rather than the total number of |
|
|
configurations available. (These are different values if config_size is too small). |
|
|
|
|
|
eglChooseConfig: |
|
|
Fail if the same attribute is specified more than once? |
|
|
(I can't find anything in the spec saying what to do in this case) |
|
|
|
|
|
In general, which attribute values are accepted and which raise |
|
|
EGL_BAD_ATTRIBUTE is vague. |
|
|
|
|
|
In particular, what do we do about the "ignored" and conditionally |
|
|
ignored ones? |
|
|
|
|
|
Currently ignoring ("ignored"): |
|
|
case EGL_MAX_PBUFFER_HEIGHT: |
|
|
case EGL_MAX_PBUFFER_PIXELS: |
|
|
case EGL_MAX_PBUFFER_WIDTH: |
|
|
case EGL_NATIVE_VISUAL_ID: |
|
|
|
|
|
Currently not ignoring ("conditionally ignored") |
|
|
case EGL_TRANSPARENT_BLUE_VALUE: |
|
|
case EGL_TRANSPARENT_GREEN_VALUE: |
|
|
case EGL_TRANSPARENT_RED_VALUE: |
|
|
|
|
|
Currently ignoring ("conditionally ignored") |
|
|
case EGL_NATIVE_VISUAL_TYPE: |
|
|
|
|
|
The following sentences in the spec seem to contradict each other: |
|
|
"If EGL_MATCH_NATIVE_PIXMAP is specified in attrib list, it must be followed |
|
|
by an attribute value" |
|
|
|
|
|
"If EGL_DONT_CARE is specified as an attribute value, then the |
|
|
attribute will not be checked. EGL_DONT_CARE may be specified for all attributes |
|
|
except EGL_LEVEL." |
|
|
|
|
|
In addition, EGL_NONE is listed as the default match value for EGL_MATCH_NATIVE_PIXMAP. |
|
|
What happens if EGL_DONT_CARE or EGL_NONE is a valid native pixmap value? |
|
|
|
|
|
What we actually do is we always treat the value supplied with EGL_MATCH_NATIVE_PIXMAP |
|
|
as a valid handle (and fail if it's invalid), and ignore it only if it's not in the list |
|
|
at all. |
|
|
|
|
|
|
|
|
EGL_MATCH_NATIVE_PIXMAP: todo: we'll set thread->error to something like EGL_BAD_ATTRIBUTE; should we be setting it to EGL_BAD_NATIVE_PIXMAP? |
|
|
What is EGL_PRESERVED_RESOURCES? |
|
|
|
|
|
|
|
|
|
|
|
Do we need to do anything for EGL_LEVEL? |
|
|
|
|
|
What is EGL_PRESERVED_RESOURCES? |
|
|
What exactly are EGL_NATIVE_VISUAL_ID, EGL_NATIVE_VISUAL_TYPE? |
|
|
|
|
|
|
|
|
Implementation notes: |
|
|
|
|
|
We only support one display. This is assumed to have a native display_id |
|
|
of 0 (==EGL_DEFAULT_DISPLAY) and an EGLDisplay id of 1 |
|
|
|
|
|
All EGL client functions preserve the invariant (CLIENT_THREAD_STATE_ERROR) |
|
|
|
|
|
It would be nice for the EGL version to only be defined in one place (rather than both eglInitialize and eglQueryString). |
|
|
|
|
|
We allow implicit casts from bool to EGLint |
|
|
|
|
|
|
|
|
We make the following assumptions: |
|
|
|
|
|
EGL_CONFIG_CAVEAT (all EGL_NONE) |
|
|
EGL_COLOR_BUFFER_TYPE (all EGL_RGB_BUFFER) |
|
|
EGL_SAMPLES (if EGL_SAMPLES is 1 then all 4 else all 0) |
|
|
EGL_NATIVE_RENDERABLE is true |
|
|
EGL_MAX_PBUFFER_WIDTH, EGL_MAX_PBUFFER_HEIGHT, EGL_MIN_SWAP_INTERVAL, EGL_MAX_SWAP_INTERVAL are the same for all configs |
|
|
|
|
|
All configs support all of: |
|
|
(EGL_PBUFFER_BIT | EGL_PIXMAP_BIT | EGL_WINDOW_BIT | EGL_VG_COLORSPACE_LINEAR_BIT | EGL_VG_ALPHA_FORMAT_PRE_BIT | EGL_MULTISAMPLE_RESOLVE_BOX_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT); |
|
|
|
|
|
|
|
|
EGL_OPTIMAL_FORMAT_BIT_KHR: Considered optimal if no format conversion needs doing |
|
|
|
|
|
EGL_TRANSPARENT_TYPE is always EGL_NONE because we don't support EGL_TRANSPARENT_RGB. Should there be an EGL_TRANSPARENT_ALPHA? |
|
|
*/ |
|
|
|
|
|
#define VCOS_LOG_CATEGORY (&egl_client_log_cat) |
|
|
|
|
|
#include "interface/khronos/common/khrn_client_mangle.h" |
|
|
|
|
|
#include "interface/khronos/common/khrn_int_common.h" |
|
|
#include "interface/khronos/common/khrn_options.h" |
|
|
|
|
|
#include "interface/khronos/egl/egl_client_surface.h" |
|
|
#include "interface/khronos/egl/egl_client_context.h" |
|
|
#include "interface/khronos/egl/egl_client_config.h" |
|
|
|
|
|
#include "interface/khronos/common/khrn_client.h" |
|
|
#include "interface/khronos/common/khrn_client_rpc.h" |
|
|
|
|
|
#ifdef RPC_DIRECT |
|
|
#include "interface/khronos/egl/egl_int_impl.h" |
|
|
#endif |
|
|
|
|
|
#if defined(WIN32) || defined(__mips__) |
|
|
#include "interface/khronos/common/khrn_int_misc_impl.h" |
|
|
#endif |
|
|
|
|
|
#ifdef KHRONOS_EGL_PLATFORM_OPENWFC |
|
|
#include "interface/khronos/wf/wfc_client_stream.h" |
|
|
#endif |
|
|
|
|
|
#if defined(RPC_DIRECT_MULTI) |
|
|
#include "middleware/khronos/egl/egl_server.h" |
|
|
#endif |
|
|
|
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
|
|
|
|
|
|
#include "interface/khronos/egl/egl_client_cr.c" |
|
|
|
|
|
VCOS_LOG_CAT_T egl_client_log_cat; |
|
|
|
|
|
static void egl_current_release(CLIENT_PROCESS_STATE_T *process, EGL_CURRENT_T *current); |
|
|
void egl_gl_flush_callback(bool wait); |
|
|
void egl_vg_flush_callback(bool wait); |
|
|
|
|
|
/* |
|
|
TODO: do an RPC call to make sure the Khronos vll is loaded (and that it stays loaded until eglTerminate) |
|
|
Also affects global image (and possibly others?) |
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) |
|
|
|
|
|
Khronos documentation: |
|
|
|
|
|
EGL may be initialized on a display by calling |
|
|
EGLBoolean eglInitialize(EGLDisplay dpy, EGLint |
|
|
*major, EGLint *minor); |
|
|
EGL TRUE is returned on success, and major and minor are updated with the major |
|
|
and minor version numbers of the EGL implementation (for example, in an EGL |
|
|
1.2 implementation, the values of *major and *minor would be 1 and 2, respectively). |
|
|
major and minor are not updated if they are specified as NULL. |
|
|
EGL FALSE is returned on failure and major and minor are not updated. An |
|
|
EGL BAD DISPLAY error is generated if the dpy argument does not refer to a valid |
|
|
EGLDisplay. An EGL NOT INITIALIZED error is generated if EGL cannot be |
|
|
initialized for an otherwise valid dpy. |
|
|
Initializing an already-initialized display is allowed, but the only effect of such |
|
|
a call is to return EGL TRUE and update the EGL version numbers. An initialized |
|
|
display may be used from other threads in the same address space without being |
|
|
initalized again in those threads. |
|
|
|
|
|
Implementation notes: |
|
|
|
|
|
client_egl_get_process_state sets some errors for us. |
|
|
|
|
|
Preconditions: |
|
|
|
|
|
major is NULL or a valid pointer |
|
|
minor is NULL or a valid pointer |
|
|
eglTerminate(dpy) must be called at some point after calling this function if we return EGL_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, or could not be initialized, for the specified display. |
|
|
EGL_SUCCESS Function succeeded. |
|
|
|
|
|
if more than one condition holds, the first error is generated. |
|
|
|
|
|
Invariants preserved: |
|
|
|
|
|
- |
|
|
|
|
|
Invariants used: |
|
|
|
|
|
- |
|
|
*/ |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
EGLBoolean result; |
|
|
|
|
|
CLIENT_LOCK(); |
|
|
|
|
|
{ |
|
|
CLIENT_PROCESS_STATE_T *process = client_egl_get_process_state(thread, dpy, EGL_FALSE); |
|
|
|
|
|
if (process) { |
|
|
if (!client_process_state_init(process)) |
|
|
{ |
|
|
thread->error = EGL_NOT_INITIALIZED; |
|
|
result = EGL_FALSE; |
|
|
} |
|
|
else |
|
|
{ |
|
|
thread->error = EGL_SUCCESS; |
|
|
result = EGL_TRUE; |
|
|
} |
|
|
} else |
|
|
result = EGL_FALSE; |
|
|
|
|
|
if (result) { |
|
|
if (major) |
|
|
*major = 1; |
|
|
if (minor) |
|
|
*minor = 4; |
|
|
} |
|
|
} |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
|
|
|
vcos_log_set_level(&egl_client_log_cat, VCOS_LOG_WARN); |
|
|
vcos_log_register("egl_client", &egl_client_log_cat); |
|
|
vcos_log_info("eglInitialize end. dpy=%d.", (int)dpy); |
|
|
khrn_init_options(); |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
/* |
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglTerminate(EGLDisplay dpy) |
|
|
|
|
|
Khronos documentation: |
|
|
|
|
|
To release resources associated with use of EGL and client APIs on a display, |
|
|
call |
|
|
EGLBoolean eglTerminate(EGLDisplay dpy); |
|
|
Termination marks all EGL-specific resources associated with the specified display |
|
|
for deletion. If contexts or surfaces created with respect to dpy are current (see |
|
|
section 3.7.3) to any thread, then they are not actually released while they remain |
|
|
current. Such contexts and surfaces will be destroyed, and all future references to |
|
|
them will become invalid, as soon as any otherwise valid eglMakeCurrent call is |
|
|
made from the thread they are bound to. |
|
|
eglTerminate returns EGL TRUE on success. |
|
|
If the dpy argument does not refer to a valid EGLDisplay, EGL FALSE is |
|
|
returned, and an EGL BAD DISPLAY error is generated. |
|
|
Termination of a display that has already been terminated, or has not yet been |
|
|
initialized, is allowed, but the only effect of such a call is to return EGL TRUE, since |
|
|
there are no EGL resources associated with the display to release. A terminated |
|
|
display may be re-initialized by calling eglInitialize again. When re-initializing |
|
|
a terminated display, resources which were marked for deletion as a result of the |
|
|
earlier termination remain so marked, and references to them are not valid. |
|
|
|
|
|
Implementation notes: |
|
|
|
|
|
- |
|
|
|
|
|
Preconditions: |
|
|
|
|
|
- |
|
|
|
|
|
Postconditions: |
|
|
|
|
|
The following conditions cause error to assume the specified value |
|
|
|
|
|
EGL_BAD_DISPLAY An EGLDisplay argument does not name a valid EGLDisplay |
|
|
EGL_SUCCESS Function succeeded. |
|
|
|
|
|
if more than one condition holds, the first error is generated. |
|
|
|
|
|
Invariants preserved: |
|
|
|
|
|
- |
|
|
|
|
|
Invariants used: |
|
|
|
|
|
- |
|
|
*/ |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglTerminate(EGLDisplay dpy) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
|
|
|
vcos_log_trace("eglTerminate start. dpy=%d", (int)dpy); |
|
|
|
|
|
#ifdef RPC_DIRECT_MULTI |
|
|
return true; //it is moved to khronos_exit |
|
|
#else |
|
|
{ |
|
|
EGLBoolean result; |
|
|
CLIENT_LOCK(); |
|
|
|
|
|
{ |
|
|
CLIENT_PROCESS_STATE_T *process = client_egl_get_process_state(thread, dpy, EGL_FALSE); |
|
|
|
|
|
if (process) { |
|
|
client_process_state_term(process); |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
result = EGL_TRUE; |
|
|
|
|
|
client_try_unload_server(process); |
|
|
} else |
|
|
result = EGL_FALSE; |
|
|
} |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
|
|
|
vcos_log_trace("eglTerminate end. dpy=%d", (int)dpy); |
|
|
vcos_log_unregister(&egl_client_log_cat); |
|
|
return result; |
|
|
} |
|
|
#endif |
|
|
} |
|
|
|
|
|
/* |
|
|
EGLAPI const char EGLAPIENTRY * eglQueryString(EGLDisplay dpy, EGLint name) |
|
|
|
|
|
Khronos documentation: |
|
|
|
|
|
3.3 EGL Versioning |
|
|
const char *eglQueryString(EGLDisplay dpy, EGLint |
|
|
name); |
|
|
eglQueryString returns a pointer to a static, zero-terminated string describing |
|
|
some aspect of the EGL implementation running on the specified display. |
|
|
name may be one of EGL CLIENT APIS, EGL EXTENSIONS, EGL VENDOR, or |
|
|
EGL VERSION. |
|
|
The EGL CLIENT APIS string describes which client rendering APIs are supported. |
|
|
It is zero-terminated and contains a space-separated list of API names, |
|
|
which must include at least one of <EFBFBD><EFBFBD>OpenGL ES<EFBFBD><EFBFBD> or <EFBFBD><EFBFBD>OpenVG<EFBFBD><EFBFBD>. |
|
|
Version 1.3 - December 4, 2006 |
|
|
3.4. CONFIGURATION MANAGEMENT 13 |
|
|
The EGL EXTENSIONS string describes which EGL extensions are supported |
|
|
by the EGL implementation running on the specified display. The string is zeroterminated |
|
|
and contains a space-separated list of extension names; extension names |
|
|
themselves do not contain spaces. If there are no extensions to EGL, then the empty |
|
|
string is returned. |
|
|
The format and contents of the EGL VENDOR string is implementation dependent. |
|
|
The format of the EGL VERSION string is: |
|
|
<major version.minor version><space><vendor specific info> |
|
|
Both the major and minor portions of the version number are numeric. Their values |
|
|
must match the major and minor values returned by eglInitialize (see section 3.2). |
|
|
The vendor-specific information is optional; if present, its format and contents are |
|
|
implementation specific. |
|
|
On failure, NULL is returned. An EGL NOT INITIALIZED error is generated if |
|
|
EGL is not initialized for dpy. An EGL BAD PARAMETER error is generated if name |
|
|
is not one of the values described above. |
|
|
|
|
|
Implementation notes: |
|
|
|
|
|
We support the following extensions but they can be removed from the driver if defined to zero. |
|
|
EGL_KHR_image extensions |
|
|
EGL_KHR_lock_surface |
|
|
|
|
|
Preconditions: |
|
|
|
|
|
- |
|
|
|
|
|
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. |
|
|
EGL_BAD_PARAMETER name is not one of {EGL_CLIENT_APIS, EGL_EXTENSIONS, EGL_VENDOR, EGL_VERSION} |
|
|
EGL_SUCCESS Function succeeded. |
|
|
|
|
|
if more than one condition holds, the first error is generated. |
|
|
|
|
|
Return value is NULL or a pointer to a null-terminated string which is valid forever. |
|
|
|
|
|
Invariants preserved: |
|
|
|
|
|
- |
|
|
|
|
|
Invariants used: |
|
|
|
|
|
- |
|
|
*/ |
|
|
|
|
|
EGLAPI const char EGLAPIENTRY * eglQueryString(EGLDisplay dpy, EGLint name) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
const char *result = NULL; |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
thread->error = EGL_SUCCESS; |
|
|
switch (name) { |
|
|
case EGL_CLIENT_APIS: |
|
|
result = "OpenGL_ES OpenVG"; |
|
|
break; |
|
|
case EGL_EXTENSIONS: |
|
|
//TODO: this list isn't quite correct |
|
|
result = "" |
|
|
#if EGL_KHR_image |
|
|
"EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap EGL_KHR_vg_parent_image EGL_KHR_gl_texture_2D_image EGL_KHR_gl_texture_cubemap_image " |
|
|
#endif |
|
|
#if EGL_KHR_lock_surface |
|
|
"EGL_KHR_lock_surface " |
|
|
#endif |
|
|
#if EGL_ANDROID_swap_rectangle |
|
|
"EGL_ANDROID_swap_rectangle " |
|
|
#endif |
|
|
#ifdef ANDROID |
|
|
"EGL_ANDROID_image_native_buffer " |
|
|
#endif |
|
|
#ifdef ANDROID |
|
|
#ifdef EGL_KHR_fence_sync |
|
|
"EGL_KHR_fence_sync " |
|
|
#endif |
|
|
#endif |
|
|
; |
|
|
break; |
|
|
case EGL_VENDOR: |
|
|
result = "Broadcom"; |
|
|
break; |
|
|
case EGL_VERSION: |
|
|
result = "1.4"; |
|
|
break; |
|
|
default: |
|
|
thread->error = EGL_BAD_PARAMETER; |
|
|
result = NULL; |
|
|
} |
|
|
CLIENT_UNLOCK(); |
|
|
} else |
|
|
result = NULL; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
/* |
|
|
EGLAPI EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list) |
|
|
|
|
|
Khronos documentation: |
|
|
|
|
|
3.5.1 Creating On-Screen Rendering Surfaces |
|
|
To create an on-screen rendering surface, first create a native platform window |
|
|
with attributes corresponding to the desired EGLConfig (e.g. with the same color |
|
|
depth, with other constraints specific to the platform). Using the platform-specific |
|
|
type EGLNativeWindowType, which is the type of a handle to that native window, |
|
|
then call: |
|
|
|
|
|
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, |
|
|
const EGLint *attrib_list); |
|
|
|
|
|
eglCreateWindowSurface creates an onscreen EGLSurface and returns a handle |
|
|
to it. Any EGL context created with a compatible EGLConfig can be used to |
|
|
render into this surface. |
|
|
|
|
|
attrib list specifies a list of attributes for the window. The list has the same |
|
|
structure as described for eglChooseConfig. Attributes that can be specified in |
|
|
attrib list include EGL_RENDER_BUFFER, EGL_VG_COLORSPACE, and EGL_VG_ALPHA_FORMAT. |
|
|
It is possible that some platforms will define additional attributes specific to |
|
|
those environments, as an EGL extension. |
|
|
|
|
|
attrib list may be NULL or empty (first attribute is EGL_NONE), in which case |
|
|
all attributes assumes their default value as described below. |
|
|
EGL_RENDER_BUFFER specifies which buffer should be used for client API |
|
|
rendering to the window, as described in section 2.2.2. If its value is EGL_SINGLE_BUFFER, |
|
|
then client APIs should render directly into the visible window. |
|
|
|
|
|
If its value is EGL_BACK_BUFFER, then all client APIs should render into the back |
|
|
buffer. The default value of EGL_RENDER_BUFFER is EGL_BACK_BUFFER. |
|
|
|
|
|
Client APIs may not be able to respect the requested rendering buffer. To |
|
|
determine the actual buffer being rendered to by a context, call eglQueryContext |
|
|
(see section 3.7.4). |
|
|
|
|
|
EGL_VG_COLORSPACE specifies the color space used by OpenVG when |
|
|
rendering to the surface. If its value is EGL_VG_COLORSPACE_sRGB, then |
|
|
a non-linear, perceptually uniform color space is assumed, with a corresponding |
|
|
VGImageFormat of form VG_s*. If its value is EGL_VG_- |
|
|
COLORSPACE_LINEAR, then a linear color space is assumed, with a corresponding |
|
|
VGImageFormat of form VG_l*. The default value of EGL_VG_COLORSPACE |
|
|
is EGL_VG_COLORSPACE_sRGB. |
|
|
|
|
|
EGL_VG_ALPHA_FORMAT specifies how alpha values are interpreted by |
|
|
OpenVG when rendering to the surface. If its value is EGL_VG_ALPHA_FORMAT_- |
|
|
NONPRE, then alpha values are not premultipled. If its value is EGL_VG_ALPHA_- |
|
|
FORMAT_PRE, then alpha values are premultiplied. The default value of EGL_VG_- |
|
|
ALPHA_FORMAT is EGL_VG_ALPHA_FORMAT_NONPRE. |
|
|
|
|
|
Note that the EGL_VG_COLORSPACE and EGL_VG_ALPHA_FORMAT attributes |
|
|
are used only by OpenVG . EGL itself, and other client APIs such as OpenGL and |
|
|
OpenGL ES , do not distinguish multiple colorspace models. Refer to section 11.2 |
|
|
of the OpenVG 1.0 specification for more information. |
|
|
|
|
|
Similarly, the EGL_VG_ALPHA_FORMAT attribute does not necessarily control |
|
|
or affect the window system<EFBFBD>s interpretation of alpha values, even when the window |
|
|
system makes use of alpha to composite surfaces at display time. The window system's |
|
|
use and interpretation of alpha values is outside the scope of EGL. However, |
|
|
the preferred behavior is for window systems to ignore the value of EGL_VG_- |
|
|
ALPHA_FORMAT when compositing window surfaces. |
|
|
|
|
|
On failure eglCreateWindowSurface returns EGL_NO_SURFACE. If the attributes |
|
|
of win do not correspond to config, then an EGL_BAD_MATCH error is generated. |
|
|
If config does not support rendering to windows (the EGL_SURFACE_TYPE |
|
|
attribute does not contain EGL_WINDOW_BIT), an EGL_BAD_MATCH error is generated. |
|
|
If config does not support the colorspace or alpha format attributes specified |
|
|
in attrib list (as defined for eglCreateWindowSurface), an EGL_BAD_MATCH error |
|
|
is generated. If config is not a valid EGLConfig, an EGL_BAD_CONFIG error |
|
|
is generated. If win is not a valid native window handle, then an EGL_BAD_NATIVE_WINDOW |
|
|
error should be generated. If there is already an EGLConfig |
|
|
associated with win (as a result of a previous eglCreateWindowSurface call), then |
|
|
an EGL_BAD_ALLOC error is generated. Finally, if the implementation cannot allocate |
|
|
resources for the new EGL window, an EGL_BAD_ALLOC error is generated. |
|
|
|
|
|
Implementation notes: |
|
|
|
|
|
- |
|
|
|
|
|
Preconditions: |
|
|
|
|
|
attrib_list is NULL or a pointer to an EGL_NONE-terminated list of attribute/value pairs |
|
|
|
|
|
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. |
|
|
EGL_BAD_CONFIG config is not a valid EGLConfig |
|
|
EGL_BAD_NATIVE_WINDOW win is not a valid native window handle |
|
|
EGL_BAD_ATTRIBUTE attrib_list contains an undefined EGL attribute or an attribute value that is unrecognized or out of range. |
|
|
(TODO: EGL_BAD_ATTRIBUTE not mentioned in spec) |
|
|
EGL_BAD_NATIVE_WINDOW window is larger than EGL_CONFIG_MAX_WIDTH x EGL_CONFIG_MAX_HEIGHT |
|
|
(TODO: Maybe EGL_BAD_ALLOC might be more appropriate?) |
|
|
EGL_BAD_ALLOC implementation cannot allocate resources for the new EGL window |
|
|
(TODO: If there is already an EGLConfig associated with win) |
|
|
EGL_SUCCESS Function succeeded. |
|
|
|
|
|
if more than one condition holds, the first error is generated. |
|
|
|
|
|
Return value is EGL_NO_SURFACE or an EGLSurface handle which is valid until the EGL session ends or |
|
|
eglDestroySurface is called. |
|
|
|
|
|
Invariants preserved: |
|
|
|
|
|
(CLIENT_PROCESS_STATE_SURFACES) |
|
|
(CLIENT_PROCESS_STATE_NEXT_SURFACE) |
|
|
|
|
|
Invariants used: |
|
|
|
|
|
- |
|
|
*/ |
|
|
|
|
|
EGLAPI EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLSurface result; |
|
|
|
|
|
vcos_log_trace("eglCreateWindowSurface for window %p", win); |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
uint32_t handle = platform_get_handle(dpy, win); |
|
|
|
|
|
if ((int)(size_t)config < 1 || (int)(size_t)config > EGL_MAX_CONFIGS) { |
|
|
thread->error = EGL_BAD_CONFIG; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else if (handle == PLATFORM_WIN_NONE) { |
|
|
// The platform reports that this is an invalid window handle |
|
|
thread->error = EGL_BAD_NATIVE_WINDOW; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else { |
|
|
bool linear = false; |
|
|
bool premult = false; |
|
|
bool single = false; |
|
|
|
|
|
if (!egl_surface_check_attribs(WINDOW, attrib_list, &linear, &premult, &single, 0, 0, 0, 0, 0, 0)) { |
|
|
thread->error = EGL_BAD_ATTRIBUTE; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else { |
|
|
EGL_SURFACE_T *surface; |
|
|
|
|
|
uint32_t width, height; |
|
|
uint32_t num_buffers = 3; |
|
|
uint32_t swapchain_count; |
|
|
|
|
|
platform_get_dimensions(dpy, |
|
|
win, &width, &height, &swapchain_count); |
|
|
|
|
|
if (swapchain_count > 0) |
|
|
num_buffers = swapchain_count; |
|
|
else |
|
|
{ |
|
|
if (khrn_options.double_buffer) |
|
|
num_buffers = 2; |
|
|
} |
|
|
|
|
|
if (width <= 0 || width > EGL_CONFIG_MAX_WIDTH || height <= 0 || height > EGL_CONFIG_MAX_HEIGHT) { |
|
|
/* TODO: Maybe EGL_BAD_ALLOC might be more appropriate? */ |
|
|
thread->error = EGL_BAD_NATIVE_WINDOW; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else { |
|
|
surface = egl_surface_create( |
|
|
(EGLSurface)(size_t)process->next_surface, |
|
|
WINDOW, |
|
|
linear ? LINEAR : SRGB, |
|
|
premult ? PRE : NONPRE, |
|
|
#ifdef DIRECT_RENDERING |
|
|
1, |
|
|
#else |
|
|
(uint32_t)(single ? 1 : num_buffers), |
|
|
#endif |
|
|
width, height, |
|
|
config, |
|
|
win, |
|
|
handle, |
|
|
false, |
|
|
false, |
|
|
false, |
|
|
EGL_NO_TEXTURE, |
|
|
EGL_NO_TEXTURE, |
|
|
0, 0); |
|
|
|
|
|
if (surface) { |
|
|
if (khrn_pointer_map_insert(&process->surfaces, process->next_surface, surface)) { |
|
|
thread->error = EGL_SUCCESS; |
|
|
result = (EGLSurface)(size_t)process->next_surface++; |
|
|
} else { |
|
|
thread->error = EGL_BAD_ALLOC; |
|
|
result = EGL_NO_SURFACE; |
|
|
egl_surface_free(surface); |
|
|
} |
|
|
} else { |
|
|
thread->error = EGL_BAD_ALLOC; |
|
|
result = EGL_NO_SURFACE; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
else |
|
|
result = EGL_NO_SURFACE; |
|
|
|
|
|
vcos_log_trace("eglCreateWindowSurface end %i", (int) result); |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
/* |
|
|
EGLAPI EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list) |
|
|
|
|
|
Khronos documentation: |
|
|
|
|
|
3.5.2 Creating Off-Screen Rendering Surfaces |
|
|
EGL supports off-screen rendering surfaces in pbuffers. Pbuffers differ from windows |
|
|
in the following ways: |
|
|
|
|
|
1. Pbuffers are typically allocated in offscreen (non-visible) graphics memory |
|
|
and are intended only for accelerated offscreen rendering. Allocation can fail |
|
|
if there are insufficient graphics resources (implementations are not required |
|
|
to virtualize framebuffer memory). Clients should deallocate pbuffers when |
|
|
they are no longer in use, since graphics memory is often a scarce resource. |
|
|
|
|
|
2. Pbuffers are EGL resources and have no associated native window or native |
|
|
window type. It may not be possible to render to pbuffers using native |
|
|
rendering APIs. |
|
|
|
|
|
To create a pbuffer, call |
|
|
|
|
|
EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, |
|
|
EGLConfig config, const EGLint |
|
|
*attrib_list); |
|
|
|
|
|
This creates a single pbuffer surface and returns a handle to it. |
|
|
attrib list specifies a list of attributes for the pbuffer. The list has the same |
|
|
structure as described for eglChooseConfig. Attributes that can be specified in |
|
|
attrib list include EGL_WIDTH, EGL_HEIGHT, EGL_LARGEST_PBUFFER, EGL_TEXTURE_FORMAT, |
|
|
EGL_TEXTURE_TARGET, EGL_MIPMAP_TEXTURE, EGL_VG_COLORSPACE, and EGL_VG_ALPHA_FORMAT. |
|
|
|
|
|
It is possible that some platforms will define additional attributes specific to |
|
|
those environments, as an EGL extension. |
|
|
|
|
|
attrib list may be NULL or empty (first attribute is EGL_NONE), in which case |
|
|
all the attributes assume their default values as described below. |
|
|
|
|
|
EGL_WIDTH and EGL_HEIGHT specify the pixel width and height of the rectangular |
|
|
pbuffer. If the value of EGLConfig attribute EGL_TEXTURE_FORMAT is |
|
|
not EGL_NO_TEXTURE, then the pbuffer width and height specify the size of the |
|
|
level zero texture image. The default values for EGL_WIDTH and EGL_HEIGHT are |
|
|
zero. |
|
|
|
|
|
EGL_TEXTURE_FORMAT specifies the format of the OpenGL ES texture that |
|
|
will be created when a pbuffer is bound to a texture map. It can be set to EGL_- |
|
|
TEXTURE_RGB, EGL_TEXTURE_RGBA, or EGL_NO_TEXTURE. The default value of |
|
|
EGL_TEXTURE_FORMAT is EGL_NO_TEXTURE. |
|
|
|
|
|
EGL_TEXTURE_TARGET specifies the target for the OpenGL ES texture that |
|
|
will be created when the pbuffer is created with a texture format of EGL_- |
|
|
TEXTURE_RGB or EGL_TEXTURE_RGBA. The target can be set to EGL_NO_- |
|
|
TEXTURE or EGL_TEXTURE_2D. The default value of EGL_TEXTURE_TARGET is |
|
|
EGL_NO_TEXTURE. |
|
|
|
|
|
EGL_MIPMAP_TEXTURE indicates whether storage for OpenGL ES mipmaps |
|
|
should be allocated. Space for mipmaps will be set aside if the attribute value |
|
|
is EGL_TRUE and EGL_TEXTURE_FORMAT is not EGL_NO_TEXTURE. The default |
|
|
value for EGL_MIPMAP_TEXTURE is EGL_FALSE. |
|
|
|
|
|
Use EGL_LARGEST_PBUFFER to get the largest available pbuffer when the allocation |
|
|
of the pbuffer would otherwise fail. The width and height of the allocated |
|
|
pbuffer will never exceed the values of EGL_WIDTH and EGL_HEIGHT, respectively. |
|
|
If the pbuffer will be used as a OpenGL ES texture (i.e., the value of |
|
|
EGL_TEXTURE_TARGET is EGL_TEXTURE_2D, and the value of EGL_TEXTURE_- |
|
|
FORMAT is EGL_TEXTURE_RGB or EGL_TEXTURE_RGBA), then the aspect ratio |
|
|
will be preserved and the new width and height will be valid sizes for the texture |
|
|
target (e.g. if the underlying OpenGL ES implementation does not support |
|
|
non-power-of-two textures, both the width and height will be a power of 2). Use |
|
|
eglQuerySurface to retrieve the dimensions of the allocated pbuffer. The default |
|
|
value of EGL_LARGEST_PBUFFER is EGL_FALSE. |
|
|
|
|
|
EGL_VG_COLORSPACE and EGL_VG_ALPHA_FORMAT have the same meaning |
|
|
and default values as when used with eglCreateWindowSurface. |
|
|
The resulting pbuffer will contain color buffers and ancillary buffers as specified |
|
|
by config. |
|
|
|
|
|
The contents of the depth and stencil buffers may not be preserved when rendering |
|
|
an OpenGL ES texture to the pbuffer and switching which image of the |
|
|
texture is rendered to (e.g., switching from rendering one mipmap level to rendering |
|
|
another). |
|
|
|
|
|
On failure eglCreatePbufferSurface returns EGL_NO_SURFACE. If the pbuffer |
|
|
could not be created due to insufficient resources, then an EGL_BAD_ALLOC error |
|
|
is generated. If config is not a valid EGLConfig, an EGL_BAD_CONFIG error is |
|
|
generated. If the value specified for either EGL_WIDTH or EGL_HEIGHT is less |
|
|
than zero, an EGL_BAD_PARAMETER error is generated. If config does not support |
|
|
pbuffers, an EGL_BAD_MATCH error is generated. In addition, an EGL_BAD_MATCH |
|
|
error is generated if any of the following conditions are true: |
|
|
|
|
|
The EGL_TEXTURE_FORMAT attribute is not EGL_NO_TEXTURE, and EGL_WIDTH |
|
|
and/or EGL_HEIGHT specify an invalid size (e.g., the texture size is |
|
|
not a power of two, and the underlying OpenGL ES implementation does not |
|
|
support non-power-of-two textures). |
|
|
|
|
|
The EGL_TEXTURE_FORMAT attribute is EGL_NO_TEXTURE, and EGL_TEXTURE_TARGET |
|
|
is something other than EGL_NO_TEXTURE; or, EGL_TEXTURE_FORMAT is something |
|
|
other than EGL_NO_TEXTURE, and EGL_TEXTURE_TARGET is EGL_NO_TEXTURE. |
|
|
|
|
|
Finally, an EGL_BAD_ATTRIBUTE error is generated if any of the EGL_TEXTURE_FORMAT, |
|
|
EGL_TEXTURE_TARGET, or EGL_MIPMAP_TEXTURE attributes |
|
|
are specified, but config does not support OpenGL ES rendering (e.g. |
|
|
the EGL_RENDERABLE_TYPE attribute does not include at least one of EGL_OPENGL_ES_BIT |
|
|
or EGL_OPENGL_ES2_BIT. |
|
|
|
|
|
Implementation notes: |
|
|
|
|
|
- |
|
|
|
|
|
attrib_list is NULL or a pointer to an EGL_NONE-terminated list of attribute/value pairs |
|
|
|
|
|
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. |
|
|
EGL_BAD_CONFIG config is not a valid EGLConfig |
|
|
EGL_BAD_ATTRIBUTE attrib_list contains an undefined EGL attribute or an attribute value that is unrecognized or out of range. |
|
|
(TODO: EGL_BAD_ATTRIBUTE not mentioned in spec) |
|
|
EGL_BAD_MATCH config doesn't support EGL_BIND_TO_TEXTURE_RGB(A) and you specify EGL_TEXTURE_FORMAT=EGL_TEXTURE_RGB(A) |
|
|
(TODO: no mention of this in the spec) |
|
|
EGL_BAD_ALLOC requested dimensions are larger than EGL_CONFIG_MAX_WIDTH x EGL_CONFIG_MAX_HEIGHT |
|
|
(TODO: no mention of this in the spec) |
|
|
EGL_BAD_ALLOC implementation cannot allocate resources for the new EGL window |
|
|
(TODO: If there is already an EGLConfig associated with win) |
|
|
EGL_SUCCESS Function succeeded. |
|
|
|
|
|
if more than one condition holds, the first error is generated. |
|
|
|
|
|
Return value is EGL_NO_SURFACE or an EGLSurface handle which is valid until the EGL session ends or |
|
|
eglDestroySurface is called. |
|
|
|
|
|
Invariants preserved: |
|
|
|
|
|
(CLIENT_PROCESS_STATE_SURFACES) |
|
|
(CLIENT_PROCESS_STATE_NEXT_SURFACE) |
|
|
|
|
|
Invariants used: |
|
|
|
|
|
- |
|
|
*/ |
|
|
|
|
|
EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, |
|
|
const EGLint *attrib_list) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLSurface result; |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
if ((int)(size_t)config < 1 || (int)(size_t)config > EGL_MAX_CONFIGS) { |
|
|
thread->error = EGL_BAD_CONFIG; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else { |
|
|
int width = 0; |
|
|
int height = 0; |
|
|
bool largest_pbuffer = 0; |
|
|
EGLenum texture_format = EGL_NO_TEXTURE; |
|
|
EGLenum texture_target = EGL_NO_TEXTURE; |
|
|
bool mipmap_texture = EGL_FALSE; |
|
|
bool linear = EGL_FALSE; |
|
|
bool premult = EGL_FALSE; |
|
|
|
|
|
if (!egl_surface_check_attribs(PBUFFER, attrib_list, &linear, &premult, 0, &width, &height, &largest_pbuffer, &texture_format, &texture_target, &mipmap_texture)) { |
|
|
thread->error = EGL_BAD_ATTRIBUTE; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else if ( |
|
|
(texture_format != EGL_NO_TEXTURE && (width == 0 || height == 0)) || |
|
|
((texture_format == EGL_NO_TEXTURE) != (texture_target == EGL_NO_TEXTURE)) || |
|
|
!egl_config_bindable((int)(size_t)config - 1, texture_format) |
|
|
) { |
|
|
|
|
|
/* |
|
|
"In addition, an EGL_BAD_MATCH |
|
|
error is generated if any of the following conditions are true: |
|
|
- The EGL_TEXTURE_FORMAT attribute is not EGL_NO_TEXTURE, and |
|
|
EGL_WIDTH and/or EGL_HEIGHT specify an invalid size (e.g., the texture size |
|
|
is not a power of two, and the underlying OpenGL ES implementation does |
|
|
not support non-power-of-two textures). |
|
|
- The EGL_TEXTURE_FORMAT attribute is EGL_NO_TEXTURE, and |
|
|
EGL_TEXTURE_TARGET is something other than EGL_NO_TEXTURE; or, |
|
|
EGL_TEXTURE_FORMAT is something other than EGL_NO_TEXTURE, and |
|
|
EGL_TEXTURE_TARGET is EGL_NO_TEXTURE." |
|
|
*/ |
|
|
|
|
|
/* |
|
|
TODO It doesn't seem to explicitly say it in the spec, but I'm also |
|
|
generating EGL_BAD_MATCH if the config doesn't support EGL_BIND_TO_TEXTURE_RGB(A) |
|
|
and you specify EGL_TEXTURE_FORMAT=EGL_TEXTURE_RGB(A) |
|
|
*/ |
|
|
thread->error = EGL_BAD_MATCH; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else if ((width > EGL_CONFIG_MAX_WIDTH || height > EGL_CONFIG_MAX_HEIGHT) && !largest_pbuffer) { |
|
|
/* |
|
|
TODO no mention of this in the spec, but clearly we fail if we try to allocate |
|
|
an oversize pbuffer without the largest_pbuffer stuff enabled |
|
|
*/ |
|
|
|
|
|
thread->error = EGL_BAD_ALLOC; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else { |
|
|
EGL_SURFACE_T *surface = egl_surface_create( |
|
|
(EGLSurface)(size_t)process->next_surface, |
|
|
PBUFFER, |
|
|
linear ? LINEAR : SRGB, |
|
|
premult ? PRE : NONPRE, |
|
|
1, |
|
|
width, height, |
|
|
config, |
|
|
0, |
|
|
PLATFORM_WIN_NONE, |
|
|
largest_pbuffer, |
|
|
true, |
|
|
mipmap_texture, |
|
|
texture_format, |
|
|
texture_target, |
|
|
0, 0); |
|
|
|
|
|
if (surface) { |
|
|
if (khrn_pointer_map_insert(&process->surfaces, process->next_surface, surface)) { |
|
|
thread->error = EGL_SUCCESS; |
|
|
result = (EGLSurface)(size_t)process->next_surface++; |
|
|
} else { |
|
|
thread->error = EGL_BAD_ALLOC; |
|
|
result = EGL_NO_SURFACE; |
|
|
egl_surface_free(surface); |
|
|
} |
|
|
} else { |
|
|
thread->error = EGL_BAD_ALLOC; |
|
|
result = EGL_NO_SURFACE; |
|
|
} |
|
|
} |
|
|
} |
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
else |
|
|
result = EGL_NO_SURFACE; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
typedef struct { |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLNativePixmapType pixmap; |
|
|
uint32_t pixmap_server_handle[2]; |
|
|
int is_dup; |
|
|
} PIXMAP_CHECK_DATA_T; |
|
|
|
|
|
static void callback_check_duplicate_pixmap(KHRN_POINTER_MAP_T *map, uint32_t key, void *value, void *data) |
|
|
{ |
|
|
PIXMAP_CHECK_DATA_T *pixmap_check_data = (PIXMAP_CHECK_DATA_T *)data; |
|
|
EGL_SURFACE_T *surface = (EGL_SURFACE_T *)value; |
|
|
|
|
|
UNUSED_NDEBUG(map); |
|
|
UNUSED_NDEBUG(key); |
|
|
|
|
|
vcos_assert(map == &pixmap_check_data->process->surfaces); |
|
|
vcos_assert(surface != NULL); |
|
|
vcos_assert((uintptr_t)key == (uintptr_t)surface->name); |
|
|
|
|
|
if ((surface->type == PIXMAP) && ((pixmap_check_data->pixmap_server_handle[0] || (pixmap_check_data->pixmap_server_handle[1] != (uint32_t)-1)) ? |
|
|
/* compare server handles for server-side pixmaps */ |
|
|
((surface->pixmap_server_handle[0] == pixmap_check_data->pixmap_server_handle[0]) && |
|
|
(surface->pixmap_server_handle[1] == pixmap_check_data->pixmap_server_handle[1])) : |
|
|
/* compare EGLNativePixmapType for client-side pixmaps */ |
|
|
(!surface->pixmap_server_handle[0] && (surface->pixmap_server_handle[1] == (uint32_t)-1) && |
|
|
(surface->pixmap == pixmap_check_data->pixmap)))) { |
|
|
pixmap_check_data->is_dup = 1; |
|
|
} |
|
|
} |
|
|
|
|
|
EGLAPI EGLSurface EGLAPIENTRY eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, |
|
|
EGLNativePixmapType pixmap, |
|
|
const EGLint *attrib_list) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLSurface result; |
|
|
|
|
|
vcos_log_trace("eglCreatePixmapSurface"); |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
if ((int)(size_t)config < 1 || (int)(size_t)config > EGL_MAX_CONFIGS) { |
|
|
thread->error = EGL_BAD_CONFIG; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else { |
|
|
bool linear = false; |
|
|
bool premult = false; |
|
|
|
|
|
if (!egl_surface_check_attribs(PIXMAP, attrib_list, &linear, &premult, 0, 0, 0, 0, 0, 0, 0)) { |
|
|
thread->error = EGL_BAD_ATTRIBUTE; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else { |
|
|
EGL_SURFACE_T *surface; |
|
|
|
|
|
KHRN_IMAGE_WRAP_T image; |
|
|
if (!platform_get_pixmap_info(pixmap, &image)) { |
|
|
thread->error = EGL_BAD_NATIVE_PIXMAP; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else { |
|
|
uint32_t server_handle[2] = {0, (uint32_t)-1}; |
|
|
platform_get_pixmap_server_handle(pixmap, server_handle); |
|
|
|
|
|
#if !EGL_BRCM_global_image |
|
|
if (server_handle[1] != -1) { |
|
|
thread->error = EGL_BAD_PARAMETER; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else |
|
|
#endif |
|
|
if (image.width > EGL_CONFIG_MAX_WIDTH || image.height > EGL_CONFIG_MAX_HEIGHT) { |
|
|
/* Maybe EGL_BAD_ALLOC might be more appropriate? */ |
|
|
thread->error = EGL_BAD_NATIVE_WINDOW; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else if (!egl_config_match_pixmap_info((int)(size_t)config - 1, &image) || |
|
|
!platform_match_pixmap_api_support(pixmap, egl_config_get_api_support((int)(size_t)config - 1)) |
|
|
#if EGL_BRCM_global_image |
|
|
|| ((server_handle[1] != (uint32_t)(-1)) && ( |
|
|
(!(image.format & IMAGE_FORMAT_LIN) != !linear) || |
|
|
(!(image.format & IMAGE_FORMAT_PRE) != !premult))) |
|
|
#endif |
|
|
) { |
|
|
thread->error = EGL_BAD_MATCH; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else { |
|
|
/* |
|
|
* Check that we didn't already use this pixmap in an |
|
|
* earlier call to eglCreatePixmapSurface() |
|
|
*/ |
|
|
PIXMAP_CHECK_DATA_T pixmap_check_data; |
|
|
pixmap_check_data.process = process; |
|
|
pixmap_check_data.pixmap = pixmap; |
|
|
pixmap_check_data.pixmap_server_handle[0] = 0; |
|
|
pixmap_check_data.pixmap_server_handle[1] = (uint32_t)-1; |
|
|
platform_get_pixmap_server_handle(pixmap, pixmap_check_data.pixmap_server_handle); |
|
|
pixmap_check_data.is_dup = 0; |
|
|
|
|
|
khrn_pointer_map_iterate(&process->surfaces, callback_check_duplicate_pixmap, &pixmap_check_data); |
|
|
|
|
|
if (pixmap_check_data.is_dup) { |
|
|
thread->error = EGL_BAD_ALLOC; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else { |
|
|
surface = egl_surface_create( |
|
|
(EGLSurface)(size_t)process->next_surface, |
|
|
PIXMAP, |
|
|
linear ? LINEAR : SRGB, |
|
|
premult ? PRE : NONPRE, |
|
|
1, |
|
|
image.width, image.height, |
|
|
config, |
|
|
0, |
|
|
PLATFORM_WIN_NONE, |
|
|
false, |
|
|
false, |
|
|
false, |
|
|
EGL_NO_TEXTURE, |
|
|
EGL_NO_TEXTURE, |
|
|
pixmap, ((server_handle[0] == 0) && (server_handle[1] == (uint32_t)(-1))) ? NULL : server_handle); |
|
|
|
|
|
if (surface) { |
|
|
if (khrn_pointer_map_insert(&process->surfaces, process->next_surface, surface)) { |
|
|
thread->error = EGL_SUCCESS; |
|
|
result = (EGLSurface)(size_t)process->next_surface++; |
|
|
} else { |
|
|
thread->error = EGL_BAD_ALLOC; |
|
|
result = EGL_NO_SURFACE; |
|
|
egl_surface_free(surface); |
|
|
} |
|
|
} else { |
|
|
thread->error = EGL_BAD_ALLOC; |
|
|
result = EGL_NO_SURFACE; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
else |
|
|
result = EGL_NO_SURFACE; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
//TODO: if this is a pixmap surface, should we make sure the pixmap gets |
|
|
//updated? |
|
|
//TODO: should this be a blocking call to the server, so that we know the EGL surface is really |
|
|
//destroyed before subsequently destroying the associated window? |
|
|
//TODO: is it safe for asynchronous swap notifications to come back after the surface has been |
|
|
//destroyed, or do we need to wait for them? (and how?) |
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglDestroySurface(EGLDisplay dpy, EGLSurface surf) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLBoolean result; |
|
|
|
|
|
vcos_log_trace("eglDestroySurface: surf=%d.\n calling CLIENT_LOCK_AND_GET_STATES...", (int)surf); |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
EGL_SURFACE_T *surface; |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
|
|
|
vcos_log_trace("eglDestroySurface: calling client_egl_get_surface..."); |
|
|
surface = client_egl_get_surface(thread, process, surf); |
|
|
|
|
|
if (surface) { |
|
|
surface->is_destroyed = true; |
|
|
khrn_pointer_map_delete(&process->surfaces, (uint32_t)(uintptr_t)surf); |
|
|
vcos_log_trace("eglDestroySurface: calling egl_surface_maybe_free..."); |
|
|
egl_surface_maybe_free(surface); |
|
|
} |
|
|
|
|
|
result = (thread->error == EGL_SUCCESS ? EGL_TRUE : EGL_FALSE ); |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
} else |
|
|
result = EGL_FALSE; |
|
|
|
|
|
vcos_log_trace("eglDestroySurface: end"); |
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurface(EGLDisplay dpy, EGLSurface surf, |
|
|
EGLint attribute, EGLint *value) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLBoolean result; |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
EGL_SURFACE_T *surface; |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
|
|
|
surface = client_egl_get_locked_surface(thread, process, surf); |
|
|
|
|
|
if (surface) { |
|
|
#if EGL_KHR_lock_surface |
|
|
switch (attribute) |
|
|
{ |
|
|
case EGL_BITMAP_POINTER_KHR: |
|
|
case EGL_BITMAP_PITCH_KHR: |
|
|
case EGL_BITMAP_ORIGIN_KHR: |
|
|
case EGL_BITMAP_PIXEL_RED_OFFSET_KHR: |
|
|
case EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR: |
|
|
case EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR: |
|
|
case EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR: |
|
|
case EGL_BITMAP_PIXEL_LUMINANCE_OFFSET_KHR: |
|
|
thread->error = egl_surface_get_mapped_buffer_attrib(surface, attribute, value); |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
return (thread->error == EGL_SUCCESS ? EGL_TRUE : EGL_FALSE ); |
|
|
default: |
|
|
/* Other attributes can only be queried if the surface is unlocked */ |
|
|
if (surface->is_locked) { |
|
|
thread->error = EGL_BAD_ACCESS; |
|
|
CLIENT_UNLOCK(); |
|
|
return EGL_FALSE; |
|
|
} |
|
|
} |
|
|
#endif |
|
|
if (!egl_surface_get_attrib(surface, attribute, value)) |
|
|
thread->error = EGL_BAD_ATTRIBUTE; |
|
|
} |
|
|
|
|
|
result = (thread->error == EGL_SUCCESS ? EGL_TRUE : EGL_FALSE ); |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
} else |
|
|
result = EGL_FALSE; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI(EGLenum api) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
|
|
|
switch (api) { |
|
|
case EGL_OPENVG_API: |
|
|
case EGL_OPENGL_ES_API: |
|
|
thread->bound_api = api; |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
return EGL_TRUE; |
|
|
default: |
|
|
thread->error = EGL_BAD_PARAMETER; |
|
|
return EGL_FALSE; |
|
|
} |
|
|
} |
|
|
|
|
|
EGLAPI EGLenum EGLAPIENTRY eglQueryAPI(void) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
|
|
|
return thread->bound_api; |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglWaitClient(void) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
|
|
|
//TODO: "If the surface associated with the calling thread's current context is no |
|
|
//longer valid, EGL_FALSE is returned and an EGL_BAD_CURRENT_SURFACE error is |
|
|
//generated". |
|
|
|
|
|
(void) RPC_INT_RES(RPC_CALL2_RES(eglIntFlushAndWait_impl, |
|
|
thread, |
|
|
EGLINTFLUSHANDWAIT_ID, |
|
|
RPC_UINT(thread->bound_api == EGL_OPENGL_ES_API), |
|
|
RPC_UINT(thread->bound_api == EGL_OPENVG_API))); // return unimportant - read is just to cause blocking |
|
|
|
|
|
if (thread->bound_api == EGL_OPENGL_ES_API) |
|
|
egl_gl_flush_callback(true); |
|
|
else |
|
|
egl_vg_flush_callback(true); |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
return EGL_TRUE; |
|
|
} |
|
|
|
|
|
//TODO: update pixmap surfaces? |
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglReleaseThread(void) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
bool destroy = false; |
|
|
|
|
|
|
|
|
vcos_log_trace("eglReleaseThread start."); |
|
|
|
|
|
CLIENT_LOCK(); |
|
|
|
|
|
{ |
|
|
CLIENT_PROCESS_STATE_T *process = CLIENT_GET_PROCESS_STATE(); |
|
|
|
|
|
if (process) { |
|
|
egl_current_release(process, &thread->opengl); |
|
|
egl_current_release(process, &thread->openvg); |
|
|
|
|
|
#ifdef RPC_LIBRARY |
|
|
/* TODO: not thread safe */ |
|
|
const KHRONOS_FUNC_TABLE_T *func_table = khronos_server_lock_func_table(client_library_get_connection()); |
|
|
if (func_table) { |
|
|
func_table->khrn_misc_rpc_flush_impl(); |
|
|
} |
|
|
khronos_server_unlock_func_table(); |
|
|
#else |
|
|
RPC_FLUSH(thread); |
|
|
#endif |
|
|
|
|
|
#ifndef RPC_DIRECT_MULTI |
|
|
//move it to khronos_exit() |
|
|
client_try_unload_server(process); |
|
|
#endif |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
destroy = true; |
|
|
} |
|
|
} |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
|
|
|
if (destroy) |
|
|
platform_hint_thread_finished(); |
|
|
|
|
|
vcos_log_trace("eglReleaseThread end."); |
|
|
|
|
|
return EGL_TRUE; |
|
|
|
|
|
//TODO free thread state? |
|
|
} |
|
|
|
|
|
EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferFromClientBuffer( |
|
|
EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, |
|
|
EGLConfig config, const EGLint *attrib_list) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLSurface result; |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
#ifndef NO_OPENVG |
|
|
if (buftype != EGL_OPENVG_IMAGE) { |
|
|
thread->error = EGL_BAD_PARAMETER; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else if ((int)(size_t)config < 1 || (int)(size_t)config > EGL_MAX_CONFIGS) { |
|
|
thread->error = EGL_BAD_CONFIG; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else { |
|
|
bool largest_pbuffer = 0; |
|
|
EGLenum texture_format = EGL_NO_TEXTURE; |
|
|
EGLenum texture_target = EGL_NO_TEXTURE; |
|
|
bool mipmap_texture = EGL_FALSE; |
|
|
|
|
|
/* Ignore the width and height as specified by attrib_list */ |
|
|
/* Also ignore linear and premult */ |
|
|
if (!egl_surface_check_attribs(PBUFFER, attrib_list, 0, 0, 0, 0, 0, &largest_pbuffer, &texture_format, &texture_target, &mipmap_texture)) { |
|
|
thread->error = EGL_BAD_ATTRIBUTE; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else if ( |
|
|
(texture_format == EGL_NO_TEXTURE) != (texture_target == EGL_NO_TEXTURE) || |
|
|
!egl_config_bindable((int)(size_t)config - 1, texture_format) |
|
|
) { |
|
|
|
|
|
/* |
|
|
"In addition, an EGL_BAD_MATCH |
|
|
error is generated if any of the following conditions are true: |
|
|
- The EGL_TEXTURE_FORMAT attribute is not EGL_NO_TEXTURE, and |
|
|
EGL_WIDTH and/or EGL_HEIGHT specify an invalid size (e.g., the texture size |
|
|
is not a power of two, and the underlying OpenGL ES implementation does |
|
|
not support non-power-of-two textures). |
|
|
- The EGL_TEXTURE_FORMAT attribute is EGL_NO_TEXTURE, and |
|
|
EGL_TEXTURE_TARGET is something other than EGL_NO_TEXTURE; or, |
|
|
EGL_TEXTURE_FORMAT is something other than EGL_NO_TEXTURE, and |
|
|
EGL_TEXTURE_TARGET is EGL_NO_TEXTURE." |
|
|
*/ |
|
|
|
|
|
/* |
|
|
It doesn't seem to explicitly say it in the spec, but I'm also |
|
|
generating EGL_BAD_MATCH if the config doesn't support EGL_BIND_TO_TEXTURE_RGB(A) |
|
|
and you specify EGL_TEXTURE_FORMAT=EGL_TEXTURE_RGB(A) |
|
|
*/ |
|
|
thread->error = EGL_BAD_MATCH; |
|
|
result = EGL_NO_SURFACE; |
|
|
} else { |
|
|
EGLint error; |
|
|
EGL_SURFACE_T *surface = egl_surface_from_vg_image( |
|
|
(VGImage)(size_t)buffer, |
|
|
(EGLSurface)(size_t)process->next_surface, |
|
|
config, |
|
|
largest_pbuffer, |
|
|
mipmap_texture, |
|
|
texture_format, |
|
|
texture_target, |
|
|
&error); |
|
|
|
|
|
if (surface) { |
|
|
if (khrn_pointer_map_insert(&process->surfaces, process->next_surface, surface)) { |
|
|
thread->error = EGL_SUCCESS; |
|
|
result = (EGLSurface)(size_t)process->next_surface++; |
|
|
} else { |
|
|
thread->error = EGL_BAD_ALLOC; |
|
|
result = EGL_NO_SURFACE; |
|
|
egl_surface_free(surface); |
|
|
} |
|
|
} else { |
|
|
thread->error = error; |
|
|
result = EGL_NO_SURFACE; |
|
|
} |
|
|
} |
|
|
} |
|
|
#else |
|
|
UNUSED(buftype); |
|
|
UNUSED(buffer); |
|
|
UNUSED(config); |
|
|
UNUSED(attrib_list); |
|
|
|
|
|
thread->error = EGL_BAD_PARAMETER; |
|
|
result = EGL_NO_SURFACE; |
|
|
#endif /* NO_OPENVG */ |
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
else |
|
|
result = EGL_NO_SURFACE; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surf, |
|
|
EGLint attribute, EGLint value) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLBoolean result; |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
EGL_SURFACE_T *surface; |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
|
|
|
surface = client_egl_get_surface(thread, process, surf); |
|
|
|
|
|
if (surface) |
|
|
thread->error = egl_surface_set_attrib(surface, attribute, value); |
|
|
|
|
|
result = (thread->error == EGL_SUCCESS ? EGL_TRUE : EGL_FALSE ); |
|
|
CLIENT_UNLOCK(); |
|
|
} else |
|
|
result = EGL_FALSE; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglBindTexImage(EGLDisplay dpy, EGLSurface surf, EGLint buffer) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLBoolean result; |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
//TODO: is behaviour correct if there is no current rendering context? |
|
|
EGL_SURFACE_T *surface; |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
|
|
|
surface = client_egl_get_surface(thread, process, surf); |
|
|
|
|
|
if (surface) { |
|
|
if (surface->texture_format != EGL_NO_TEXTURE) { |
|
|
if (buffer == EGL_BACK_BUFFER) { |
|
|
if (surface->type == PBUFFER && surface->texture_target == EGL_TEXTURE_2D) { |
|
|
result = (EGLBoolean) RPC_BOOLEAN_RES(RPC_CALL1_RES(eglIntBindTexImage_impl, |
|
|
thread, |
|
|
EGLINTBINDTEXIMAGE_ID, |
|
|
surface->serverbuffer)); |
|
|
if (result != EGL_TRUE) { |
|
|
// If buffer is already bound to a texture then an |
|
|
// EGL_BAD_ACCESS error is returned. |
|
|
// But we don't know whether it is or not until we call |
|
|
// the server. |
|
|
thread->error = EGL_BAD_ACCESS; |
|
|
} |
|
|
} else { |
|
|
thread->error = EGL_BAD_SURFACE; |
|
|
result = EGL_FALSE; |
|
|
} |
|
|
} else { |
|
|
thread->error = EGL_BAD_PARAMETER; |
|
|
result = EGL_FALSE; |
|
|
} |
|
|
} else { |
|
|
thread->error = EGL_BAD_MATCH; |
|
|
result = EGL_FALSE; |
|
|
} |
|
|
} |
|
|
|
|
|
result = (thread->error == EGL_SUCCESS ? EGL_TRUE : EGL_FALSE ); |
|
|
CLIENT_UNLOCK(); |
|
|
} else |
|
|
result = EGL_FALSE; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglReleaseTexImage(EGLDisplay dpy, EGLSurface surf, EGLint buffer) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLBoolean result; |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
EGL_SURFACE_T *surface; |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
|
|
|
surface = client_egl_get_surface(thread, process, surf); |
|
|
|
|
|
if (surface) { |
|
|
if (surface->texture_format != EGL_NO_TEXTURE) { |
|
|
if (buffer == EGL_BACK_BUFFER) { |
|
|
if (surface->type == PBUFFER) { |
|
|
//TODO: not a "bound" pbuffer? |
|
|
RPC_CALL1(eglIntReleaseTexImage_impl, |
|
|
thread, |
|
|
EGLINTRELEASETEXIMAGE_ID, |
|
|
surface->serverbuffer); |
|
|
} else { |
|
|
thread->error = EGL_BAD_SURFACE; |
|
|
result = EGL_FALSE; |
|
|
} |
|
|
} else { |
|
|
thread->error = EGL_BAD_PARAMETER; |
|
|
result = EGL_FALSE; |
|
|
} |
|
|
} else { |
|
|
thread->error = EGL_BAD_MATCH; |
|
|
result = EGL_FALSE; |
|
|
} |
|
|
} |
|
|
|
|
|
result = (thread->error == EGL_SUCCESS ? EGL_TRUE : EGL_FALSE ); |
|
|
CLIENT_UNLOCK(); |
|
|
} else |
|
|
result = EGL_FALSE; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglSwapInterval(EGLDisplay dpy, EGLint interval) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLBoolean result; |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
EGL_CURRENT_T *current; |
|
|
EGL_SURFACE_T *surface; |
|
|
|
|
|
/* the spec says "the window associated with the current context". it |
|
|
* doesn't explicitly say "the current context for the current rendering |
|
|
* api" (which it does in most other places), but i'm assuming that's what |
|
|
* it means */ |
|
|
|
|
|
if (thread->bound_api == EGL_OPENVG_API) |
|
|
current = &thread->openvg; |
|
|
else |
|
|
current = &thread->opengl; |
|
|
|
|
|
surface = current->draw; |
|
|
|
|
|
if (surface) { |
|
|
if (surface->type == WINDOW) { |
|
|
if (interval < EGL_CONFIG_MIN_SWAP_INTERVAL) |
|
|
interval = EGL_CONFIG_MIN_SWAP_INTERVAL; |
|
|
if (interval > EGL_CONFIG_MAX_SWAP_INTERVAL) |
|
|
interval = EGL_CONFIG_MAX_SWAP_INTERVAL; |
|
|
|
|
|
surface->swap_interval = (uint32_t) interval; |
|
|
} |
|
|
|
|
|
RPC_CALL2(eglIntSwapInterval_impl, |
|
|
thread, |
|
|
EGLINTSWAPINTERVAL_ID, |
|
|
surface->serverbuffer, |
|
|
surface->swap_interval); |
|
|
|
|
|
/* TODO: should we raise an error if it's not a window |
|
|
* surface, or silently ignore it? |
|
|
*/ |
|
|
thread->error = EGL_SUCCESS; |
|
|
result = EGL_TRUE; |
|
|
} else { |
|
|
/* |
|
|
"If there is no current context |
|
|
on the calling thread, a EGL BAD CONTEXT error is generated. If there is no surface |
|
|
bound to the current context, a EGL BAD SURFACE error is generated." |
|
|
|
|
|
TODO |
|
|
This doesn't make sense to me - the current context always has surfaces |
|
|
bound to it, so which error do we raise? |
|
|
*/ |
|
|
thread->error = EGL_BAD_SURFACE; |
|
|
result = EGL_FALSE; |
|
|
} |
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
else |
|
|
result = EGL_FALSE; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLContext EGLAPIENTRY eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_ctx, const EGLint *attrib_list) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLContext result; |
|
|
|
|
|
vcos_log_trace("eglCreateContext start"); |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
if ((int)(size_t)config < 1 || (int)(size_t)config > EGL_MAX_CONFIGS) { |
|
|
thread->error = EGL_BAD_CONFIG; |
|
|
result = EGL_NO_CONTEXT; |
|
|
} else { |
|
|
EGLint max_version = (EGLint) (thread->bound_api == EGL_OPENGL_ES_API ? 2 : 1); |
|
|
EGLint version = 1; |
|
|
|
|
|
if (!egl_context_check_attribs(attrib_list, max_version, &version)) { |
|
|
thread->error = EGL_BAD_ATTRIBUTE; |
|
|
result = EGL_NO_CONTEXT; |
|
|
} else if (!(egl_config_get_api_support((int)(intptr_t)config - 1) & |
|
|
((thread->bound_api == EGL_OPENVG_API) ? EGL_OPENVG_BIT : |
|
|
((version == 1) ? EGL_OPENGL_ES_BIT : EGL_OPENGL_ES2_BIT)))) { |
|
|
thread->error = EGL_BAD_CONFIG; |
|
|
result = EGL_NO_CONTEXT; |
|
|
} else { |
|
|
EGL_CONTEXT_T *share_context; |
|
|
|
|
|
if (share_ctx != EGL_NO_CONTEXT) { |
|
|
share_context = client_egl_get_context(thread, process, share_ctx); |
|
|
|
|
|
if (share_context) { |
|
|
if ((share_context->type == OPENVG && thread->bound_api != EGL_OPENVG_API) || |
|
|
(share_context->type != OPENVG && thread->bound_api == EGL_OPENVG_API)) { |
|
|
thread->error = EGL_BAD_MATCH; |
|
|
share_context = NULL; |
|
|
} |
|
|
} else { |
|
|
thread->error = EGL_BAD_CONTEXT; |
|
|
} |
|
|
} else { |
|
|
share_context = NULL; |
|
|
} |
|
|
|
|
|
if (share_ctx == EGL_NO_CONTEXT || share_context) { |
|
|
EGL_CONTEXT_T *context; |
|
|
EGL_CONTEXT_TYPE_T type; |
|
|
|
|
|
#ifndef NO_OPENVG |
|
|
if (thread->bound_api == EGL_OPENVG_API) |
|
|
type = OPENVG; |
|
|
else |
|
|
#endif |
|
|
if (version == 1) |
|
|
type = OPENGL_ES_11; |
|
|
else |
|
|
type = OPENGL_ES_20; |
|
|
|
|
|
context = egl_context_create( |
|
|
share_context, |
|
|
(EGLContext)(size_t)process->next_context, |
|
|
dpy, config, type); |
|
|
|
|
|
if (context) { |
|
|
if (khrn_pointer_map_insert(&process->contexts, process->next_context, context)) { |
|
|
thread->error = EGL_SUCCESS; |
|
|
result = (EGLContext)(size_t)process->next_context++; |
|
|
} else { |
|
|
thread->error = EGL_BAD_ALLOC; |
|
|
result = EGL_NO_CONTEXT; |
|
|
egl_context_term(context); |
|
|
khrn_platform_free(context); |
|
|
} |
|
|
} else { |
|
|
thread->error = EGL_BAD_ALLOC; |
|
|
result = EGL_NO_CONTEXT; |
|
|
} |
|
|
} else { |
|
|
/* thread->error set above */ |
|
|
result = EGL_NO_CONTEXT; |
|
|
} |
|
|
} |
|
|
} |
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
else |
|
|
result = EGL_NO_CONTEXT; |
|
|
|
|
|
vcos_log_trace("eglCreateContext end"); |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglDestroyContext(EGLDisplay dpy, EGLContext ctx) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLBoolean result; |
|
|
|
|
|
vcos_log_trace("eglDestroyContext ctx=%d.", (int)ctx); |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
EGL_CONTEXT_T *context; |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
|
|
|
context = client_egl_get_context(thread, process, ctx); |
|
|
|
|
|
if (context) { |
|
|
context->is_destroyed = true; |
|
|
khrn_pointer_map_delete(&process->contexts, (uint32_t)(uintptr_t)ctx); |
|
|
egl_context_maybe_free(context); |
|
|
} |
|
|
result = thread->error == EGL_SUCCESS; |
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
else |
|
|
result = EGL_FALSE; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
static void egl_current_release(CLIENT_PROCESS_STATE_T *process, EGL_CURRENT_T *current) |
|
|
{ |
|
|
if (current->context) { |
|
|
EGL_CONTEXT_T *context = current->context; |
|
|
vcos_assert(context->is_current); |
|
|
context->is_current = false; |
|
|
context->renderbuffer = EGL_NONE; |
|
|
egl_context_set_callbacks(context, NULL, NULL, NULL, NULL); |
|
|
|
|
|
current->context = 0; |
|
|
|
|
|
egl_context_maybe_free(context); |
|
|
|
|
|
vcos_assert(process->context_current_count > 0); |
|
|
process->context_current_count--; |
|
|
} |
|
|
if (current->draw) { |
|
|
EGL_SURFACE_T *draw = current->draw; |
|
|
|
|
|
vcos_assert(draw->context_binding_count > 0); |
|
|
draw->context_binding_count--; |
|
|
|
|
|
current->draw = 0; |
|
|
|
|
|
egl_surface_maybe_free(draw); |
|
|
} |
|
|
if (current->read) { |
|
|
EGL_SURFACE_T *read = current->read; |
|
|
|
|
|
vcos_assert(read->context_binding_count > 0); |
|
|
read->context_binding_count--; |
|
|
|
|
|
current->read = 0; |
|
|
|
|
|
egl_surface_maybe_free(read); |
|
|
} |
|
|
} |
|
|
|
|
|
static void set_color_data(EGL_SURFACE_ID_T surface_id, KHRN_IMAGE_WRAP_T *image) |
|
|
{ |
|
|
int line_size = (image->stride < 0) ? -image->stride : image->stride; |
|
|
int lines = KHDISPATCH_WORKSPACE_SIZE / line_size; |
|
|
int offset = 0; |
|
|
int height = image->height; |
|
|
|
|
|
if (khrn_image_is_brcm1(image->format)) |
|
|
lines &= ~63; |
|
|
|
|
|
vcos_assert(lines > 0); |
|
|
|
|
|
while (height > 0) { |
|
|
int batch = _min(lines, height); |
|
|
#ifndef RPC_DIRECT |
|
|
uint32_t len = batch * line_size; |
|
|
#endif |
|
|
|
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
int adjusted_offset = (image->stride < 0) ? (offset + (batch - 1)) : offset; |
|
|
|
|
|
RPC_CALL7_IN_BULK(eglIntSetColorData_impl, |
|
|
thread, |
|
|
EGLINTSETCOLORDATA_ID, |
|
|
surface_id, |
|
|
image->format, |
|
|
image->width, |
|
|
batch, |
|
|
image->stride, |
|
|
offset, |
|
|
(const char *)image->storage + adjusted_offset * image->stride, |
|
|
len); |
|
|
|
|
|
offset += batch; |
|
|
height -= batch; |
|
|
} |
|
|
} |
|
|
|
|
|
static void send_pixmap(EGL_SURFACE_T *surface) |
|
|
{ |
|
|
if (surface && surface->type == PIXMAP && !surface->pixmap_server_handle[0] && (surface->pixmap_server_handle[1] == (uint32_t)-1) && !surface->server_owned) { |
|
|
KHRN_IMAGE_WRAP_T image; |
|
|
|
|
|
if (!platform_get_pixmap_info(surface->pixmap, &image)) { |
|
|
(void)vcos_verify(0); /* the pixmap has become invalid... */ |
|
|
return; |
|
|
} |
|
|
|
|
|
set_color_data(surface->serverbuffer, &image); |
|
|
|
|
|
platform_send_pixmap_completed(surface->pixmap); |
|
|
|
|
|
surface->server_owned = true; |
|
|
|
|
|
khrn_platform_release_pixmap_info(surface->pixmap, &image); |
|
|
} |
|
|
} |
|
|
|
|
|
void egl_gl_render_callback(void) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
|
|
|
CLIENT_LOCK(); |
|
|
|
|
|
send_pixmap(thread->opengl.draw); |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
|
|
|
void egl_vg_render_callback(void) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
|
|
|
CLIENT_LOCK(); |
|
|
|
|
|
send_pixmap(thread->openvg.draw); |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
|
|
|
static void get_color_data(EGL_SURFACE_ID_T surface_id, KHRN_IMAGE_WRAP_T *image) |
|
|
{ |
|
|
int line_size = (image->stride < 0) ? -image->stride : image->stride; |
|
|
int lines = KHDISPATCH_WORKSPACE_SIZE / line_size; |
|
|
int offset = 0; |
|
|
int height = image->height; |
|
|
|
|
|
if (khrn_image_is_brcm1(image->format)) |
|
|
lines &= ~63; |
|
|
|
|
|
vcos_assert(lines > 0); |
|
|
|
|
|
while (height > 0) { |
|
|
int batch = _min(lines, height); |
|
|
|
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
int adjusted_offset = (image->stride < 0) ? (offset + (batch - 1)) : offset; |
|
|
|
|
|
RPC_CALL7_OUT_BULK(eglIntGetColorData_impl, |
|
|
thread, |
|
|
EGLINTGETCOLORDATA_ID, |
|
|
surface_id, |
|
|
image->format, |
|
|
image->width, |
|
|
batch, |
|
|
image->stride, |
|
|
offset, |
|
|
(char *)image->storage + adjusted_offset * image->stride); |
|
|
|
|
|
offset += batch; |
|
|
height -= batch; |
|
|
} |
|
|
} |
|
|
|
|
|
static void retrieve_pixmap(EGL_SURFACE_T *surface, bool wait) |
|
|
{ |
|
|
UNUSED(wait); |
|
|
|
|
|
/*TODO: currently we always wait */ |
|
|
if (surface && surface->type == PIXMAP && !surface->pixmap_server_handle[0] && (surface->pixmap_server_handle[1] == (uint32_t)-1) && surface->server_owned) { |
|
|
KHRN_IMAGE_WRAP_T image; |
|
|
|
|
|
if (!platform_get_pixmap_info(surface->pixmap, &image)) { |
|
|
(void)vcos_verify(0); /* the pixmap has become invalid... */ |
|
|
return; |
|
|
} |
|
|
|
|
|
get_color_data(surface->serverbuffer, &image); |
|
|
|
|
|
//Do any platform specific syncronisation or notification of modification |
|
|
platform_retrieve_pixmap_completed(surface->pixmap); |
|
|
|
|
|
surface->server_owned = false; |
|
|
khrn_platform_release_pixmap_info(surface->pixmap, &image); |
|
|
} |
|
|
} |
|
|
|
|
|
void egl_gl_flush_callback(bool wait) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
|
|
|
CLIENT_LOCK(); |
|
|
|
|
|
retrieve_pixmap(thread->opengl.draw, wait); |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
|
|
|
void egl_vg_flush_callback(bool wait) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
|
|
|
CLIENT_LOCK(); |
|
|
|
|
|
retrieve_pixmap(thread->openvg.draw, wait); |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
|
|
|
static bool context_and_surface_are_compatible(EGL_CONTEXT_T *context, EGL_SURFACE_T *surface) |
|
|
{ |
|
|
/* |
|
|
from section 2.2 of the (1.3) spec, a context and surface are compatible |
|
|
if: |
|
|
1) they support the same type of color buffer (rgb or luminance). this is |
|
|
trivially true for us as we only support rgb color buffers |
|
|
2) they have color buffers and ancillary buffers of the same depth |
|
|
3) the surface was created with respect to an EGLConfig supporting client |
|
|
api rendering of the same type as the api type of the context |
|
|
4) they were created with respect to the same EGLDisplay. this is |
|
|
trivially true for us as we only have one EGLDisplay |
|
|
*/ |
|
|
|
|
|
uint32_t api_type = 0; |
|
|
switch (context->type) { |
|
|
case OPENGL_ES_11: api_type = EGL_OPENGL_ES_BIT; break; |
|
|
case OPENGL_ES_20: api_type = EGL_OPENGL_ES2_BIT; break; |
|
|
case OPENVG: api_type = EGL_OPENVG_BIT; break; |
|
|
default: UNREACHABLE(); |
|
|
} |
|
|
|
|
|
return |
|
|
egl_config_bpps_match((int)(intptr_t)context->configname - 1, (int)(intptr_t)surface->config - 1) && /* (2) */ |
|
|
(egl_config_get_api_support((int)(intptr_t)surface->config - 1) & api_type); /* (3) */ |
|
|
} |
|
|
|
|
|
static bool egl_current_set(CLIENT_PROCESS_STATE_T *process, CLIENT_THREAD_STATE_T *thread, EGL_CURRENT_T *current, EGL_CONTEXT_T *context, EGL_SURFACE_T *draw, EGL_SURFACE_T *read) |
|
|
{ |
|
|
bool result = false; |
|
|
|
|
|
UNUSED(process); |
|
|
|
|
|
if (context->is_current && context->thread != thread) { |
|
|
// Fail - context is current to some other thread |
|
|
thread->error = EGL_BAD_ACCESS; |
|
|
} else if (draw->context_binding_count && draw->thread != thread) { |
|
|
// Fail - draw surface is bound to context which is current to another thread |
|
|
thread->error = EGL_BAD_ACCESS; |
|
|
} else if (read->context_binding_count && read->thread != thread) { |
|
|
// Fail - read surface is bound to context which is current to another thread |
|
|
thread->error = EGL_BAD_ACCESS; |
|
|
} else if (!context_and_surface_are_compatible(context, draw)) { |
|
|
// Fail - draw surface is not compatible with context |
|
|
thread->error = EGL_BAD_MATCH; |
|
|
} else if (!context_and_surface_are_compatible(context, read)) { |
|
|
// Fail - read surface is not compatible with context |
|
|
thread->error = EGL_BAD_MATCH; |
|
|
} else { |
|
|
egl_current_release(process, current); |
|
|
|
|
|
context->is_current = true; |
|
|
context->thread = thread; |
|
|
|
|
|
/* TODO: GLES supposedly doesn't support single-buffered rendering. Should we take this into account? */ |
|
|
context->renderbuffer = egl_surface_get_render_buffer(draw); |
|
|
|
|
|
// Check surfaces are not bound to a different thread, and increase their reference count |
|
|
|
|
|
draw->thread = thread; |
|
|
draw->context_binding_count++; |
|
|
|
|
|
read->thread = thread; |
|
|
read->context_binding_count++; |
|
|
|
|
|
current->context = context; |
|
|
current->draw = draw; |
|
|
current->read = read; |
|
|
|
|
|
process->context_current_count++; |
|
|
|
|
|
result = true; |
|
|
} |
|
|
if (draw->type == PIXMAP) { |
|
|
egl_context_set_callbacks(context, egl_gl_render_callback, egl_gl_flush_callback, egl_vg_render_callback, egl_vg_flush_callback); |
|
|
} else { |
|
|
egl_context_set_callbacks(context, NULL,NULL, NULL, NULL); |
|
|
} |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
static void flush_current_api(CLIENT_THREAD_STATE_T *thread) |
|
|
{ |
|
|
RPC_CALL2(eglIntFlush_impl, |
|
|
thread, |
|
|
EGLINTFLUSH_ID, |
|
|
RPC_UINT(thread->bound_api == EGL_OPENGL_ES_API), |
|
|
RPC_UINT(thread->bound_api == EGL_OPENVG_API)); |
|
|
RPC_FLUSH(thread); |
|
|
|
|
|
if (thread->bound_api == EGL_OPENGL_ES_API) |
|
|
egl_gl_flush_callback(false); |
|
|
else |
|
|
egl_vg_flush_callback(false); |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglMakeCurrent(EGLDisplay dpy, EGLSurface dr, EGLSurface rd, EGLContext ctx) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
EGLBoolean result; |
|
|
CLIENT_PROCESS_STATE_T *process = NULL; /* init to avoid warnings */ |
|
|
|
|
|
CLIENT_LOCK(); |
|
|
|
|
|
vcos_log_trace("Actual eglMakeCurrent %d %d %x", (int)ctx, (int)dr, (unsigned int) thread->error); |
|
|
|
|
|
/* |
|
|
check whether we are trying to release the current context |
|
|
Note that we can do this even if the display isn't initted. |
|
|
*/ |
|
|
|
|
|
if (dr == EGL_NO_SURFACE && rd == EGL_NO_SURFACE && ctx == EGL_NO_CONTEXT) { |
|
|
process = client_egl_get_process_state(thread, dpy, EGL_FALSE); |
|
|
|
|
|
if (process) { |
|
|
/* spec says we should flush in this case */ |
|
|
flush_current_api(thread); |
|
|
|
|
|
egl_current_release(process, |
|
|
(thread->bound_api == EGL_OPENVG_API) ? &thread->openvg : &thread->opengl); |
|
|
|
|
|
client_send_make_current(thread); |
|
|
|
|
|
client_try_unload_server(process); |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
result = EGL_TRUE; |
|
|
} else { |
|
|
result = EGL_FALSE; |
|
|
} |
|
|
} else if (dr == EGL_NO_SURFACE || rd == EGL_NO_SURFACE || ctx == EGL_NO_CONTEXT) { |
|
|
thread->error = EGL_BAD_MATCH; |
|
|
result = EGL_FALSE; |
|
|
} else { |
|
|
/* |
|
|
get display |
|
|
*/ |
|
|
|
|
|
process = client_egl_get_process_state(thread, dpy, EGL_TRUE); |
|
|
|
|
|
if (!process) |
|
|
result = EGL_FALSE; |
|
|
else { |
|
|
/* |
|
|
get context |
|
|
*/ |
|
|
|
|
|
EGL_CONTEXT_T *context = client_egl_get_context(thread, process, ctx); |
|
|
|
|
|
if (!context) { |
|
|
result = EGL_FALSE; |
|
|
} else { |
|
|
|
|
|
/* |
|
|
get surfaces |
|
|
*/ |
|
|
|
|
|
EGL_SURFACE_T *draw = client_egl_get_surface(thread, process, dr); |
|
|
EGL_SURFACE_T *read = client_egl_get_surface(thread, process, rd); |
|
|
|
|
|
if (!draw || !read) { |
|
|
result = EGL_FALSE; |
|
|
} else if (context->type == OPENVG && dr != rd) { |
|
|
thread->error = EGL_BAD_MATCH; //TODO: what error are we supposed to return here? |
|
|
result = EGL_FALSE; |
|
|
} else { |
|
|
EGL_CURRENT_T *current; |
|
|
|
|
|
if (context->type == OPENVG) |
|
|
current = &thread->openvg; |
|
|
else |
|
|
current = &thread->opengl; |
|
|
|
|
|
if (!egl_current_set(process, thread, current, context, draw, read)) |
|
|
result = EGL_FALSE; |
|
|
else { |
|
|
client_send_make_current(thread); |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
result = EGL_TRUE; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
|
|
|
vcos_log_trace("Actual eglMakeCurrent end %d %d %d %x", (int)ctx, (int)dr, result, (unsigned int)thread->error); |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext(void) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
EGLContext result; |
|
|
|
|
|
CLIENT_LOCK(); |
|
|
|
|
|
{ |
|
|
EGL_CURRENT_T *current; |
|
|
|
|
|
if (thread->bound_api == EGL_OPENVG_API) |
|
|
current = &thread->openvg; |
|
|
else |
|
|
current = &thread->opengl; |
|
|
|
|
|
if (!current->context) |
|
|
result = EGL_NO_CONTEXT; |
|
|
else |
|
|
result = current->context->name; |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
} |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLSurface EGLAPIENTRY eglGetCurrentSurface(EGLint readdraw) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
EGLSurface result; |
|
|
|
|
|
CLIENT_LOCK(); |
|
|
|
|
|
{ |
|
|
EGL_CURRENT_T *current; |
|
|
EGL_SURFACE_T *surface; |
|
|
|
|
|
if (thread->bound_api == EGL_OPENVG_API) |
|
|
current = &thread->openvg; |
|
|
else |
|
|
current = &thread->opengl; |
|
|
|
|
|
switch (readdraw) { |
|
|
case EGL_READ: |
|
|
surface = current->read; |
|
|
thread->error = EGL_SUCCESS; |
|
|
break; |
|
|
case EGL_DRAW: |
|
|
surface = current->draw; |
|
|
thread->error = EGL_SUCCESS; |
|
|
break; |
|
|
default: |
|
|
surface = 0; |
|
|
thread->error = EGL_BAD_PARAMETER; |
|
|
break; |
|
|
} |
|
|
|
|
|
if (!surface) |
|
|
result = EGL_NO_SURFACE; |
|
|
else |
|
|
result = surface->name; |
|
|
} |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLDisplay EGLAPIENTRY eglGetCurrentDisplay(void) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
EGLDisplay result; |
|
|
|
|
|
CLIENT_LOCK(); |
|
|
|
|
|
{ |
|
|
EGL_CURRENT_T *current; |
|
|
|
|
|
if (thread->bound_api == EGL_OPENVG_API) |
|
|
current = &thread->openvg; |
|
|
else |
|
|
current = &thread->opengl; |
|
|
|
|
|
if (!current->context) |
|
|
result = EGL_NO_DISPLAY; |
|
|
else |
|
|
result = current->context->display; |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
} |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLBoolean result; |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
if (!value) { |
|
|
thread->error = EGL_BAD_PARAMETER; |
|
|
result = EGL_FALSE; |
|
|
} else { |
|
|
EGL_CONTEXT_T *context; |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
|
|
|
context = client_egl_get_context(thread, process, ctx); |
|
|
|
|
|
if (context) { |
|
|
if (!egl_context_get_attrib(context, attribute, value)) |
|
|
thread->error = EGL_BAD_ATTRIBUTE; |
|
|
|
|
|
} |
|
|
result = thread->error == EGL_SUCCESS; |
|
|
} |
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
else |
|
|
result = EGL_FALSE; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglWaitGL(void) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
EGLBoolean result; |
|
|
|
|
|
//TODO: "If the surface associated with the calling thread's current context is no |
|
|
//longer valid, EGL_FALSE is returned and an EGL_BAD_CURRENT_SURFACE error is |
|
|
//generated". |
|
|
(void) RPC_INT_RES(RPC_CALL2_RES(eglIntFlushAndWait_impl, |
|
|
thread, |
|
|
EGLINTFLUSHANDWAIT_ID, |
|
|
RPC_UINT(true), |
|
|
RPC_UINT(false))); // return unimportant - read is just to cause blocking |
|
|
|
|
|
egl_gl_flush_callback(true); |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
result = EGL_TRUE; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglWaitNative(EGLint engine) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
EGLBoolean result; |
|
|
|
|
|
//TODO: "If the surface associated with the calling thread's current context is no |
|
|
//longer valid, EGL_FALSE is returned and an EGL_BAD_CURRENT_SURFACE error is |
|
|
//generated". |
|
|
|
|
|
if (engine == EGL_CORE_NATIVE_ENGINE) { |
|
|
//TODO: currently nothing we can do here |
|
|
thread->error = EGL_SUCCESS; |
|
|
result = EGL_TRUE; |
|
|
} else { |
|
|
thread->error = EGL_BAD_PARAMETER; |
|
|
result = EGL_FALSE; |
|
|
} |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffers(EGLDisplay dpy, EGLSurface surf) |
|
|
{ |
|
|
#ifdef DIRECT_RENDERING |
|
|
/* Wrapper layer shouldn't call eglSwapBuffers */ |
|
|
UNREACHABLE(); |
|
|
return EGL_FALSE; |
|
|
#else |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLBoolean result; |
|
|
|
|
|
vcos_log_trace("eglSwapBuffers start. dpy=%d. surf=%d.", (int)dpy, (int)surf); |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
EGL_SURFACE_T *surface; |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
|
|
|
surface = client_egl_get_surface(thread, process, surf); |
|
|
|
|
|
vcos_log_trace("eglSwapBuffers get surface %x",(int)surface); |
|
|
|
|
|
if (surface) { |
|
|
|
|
|
#if !(EGL_KHR_lock_surface) |
|
|
/* Surface to be displayed must be bound to current context and API */ |
|
|
/* This check is disabled if we have the EGL_KHR_lock_surface extension */ |
|
|
if (thread->bound_api == EGL_OPENGL_ES_API && surface != thread->opengl.draw && surface != thread->opengl.read |
|
|
|| thread->bound_api == EGL_OPENVG_API && surface != thread->openvg.draw) { |
|
|
thread->error = EGL_BAD_SURFACE; |
|
|
} else |
|
|
#endif |
|
|
{ |
|
|
|
|
|
if (surface->type == WINDOW) { |
|
|
uint32_t width, height, swapchain_count; |
|
|
|
|
|
/* the egl spec says eglSwapBuffers is supposed to be a no-op for |
|
|
* single-buffered surfaces, but we pass it through as the |
|
|
* semantics are potentially useful: |
|
|
* - any ops outstanding on the surface are flushed |
|
|
* - the surface is resubmitted to the display once the |
|
|
* outstanding ops complete (for displays which have their own |
|
|
* memory, this is useful) |
|
|
* - the surface is resized to fit the backing window */ |
|
|
|
|
|
// We need to check at this point if the surface has resized, and pass |
|
|
// size data down to the server. |
|
|
|
|
|
width = surface->width; |
|
|
height = surface->height; |
|
|
|
|
|
platform_get_dimensions(dpy, surface->win, |
|
|
&width, &height, &swapchain_count); |
|
|
|
|
|
if((width!=surface->width)||(height!=surface->height)) { |
|
|
uint32_t handle = platform_get_handle(dpy, surface->win); |
|
|
surface->internal_handle = handle; |
|
|
surface->width = width; |
|
|
surface->height = height; |
|
|
} |
|
|
|
|
|
vcos_log_trace("eglSwapBuffers comparison: %d %d, %d %d", |
|
|
surface->width, surface->base_width, surface->height, |
|
|
surface->base_height); |
|
|
|
|
|
/* TODO: raise EGL_BAD_ALLOC if we try to enlarge window and then run out of memory |
|
|
|
|
|
if (surface->width <= surface->base_width && surface->height <= surface->base_height || |
|
|
surface->width <= surface->base_height && surface->height <= surface->base_width) |
|
|
*/ |
|
|
// We don't call flush_current_api() here because it's only relevant |
|
|
// for pixmap surfaces (eglIntSwapBuffers takes care of flushing on |
|
|
// the server side). |
|
|
|
|
|
platform_surface_update(surface->internal_handle); |
|
|
|
|
|
vcos_log_trace("eglSwapBuffers server call"); |
|
|
|
|
|
RPC_CALL6(eglIntSwapBuffers_impl, |
|
|
thread, |
|
|
EGLINTSWAPBUFFERS_ID, |
|
|
RPC_UINT(surface->serverbuffer), |
|
|
RPC_UINT(surface->width), |
|
|
RPC_UINT(surface->height), |
|
|
RPC_UINT(surface->internal_handle), |
|
|
RPC_UINT(surface->swap_behavior == EGL_BUFFER_PRESERVED ? 1 : 0), |
|
|
RPC_UINT(khrn_platform_get_window_position(surface->win))); |
|
|
|
|
|
RPC_FLUSH(thread); |
|
|
|
|
|
#ifdef ANDROID |
|
|
CLIENT_UNLOCK(); |
|
|
platform_dequeue(dpy, surface->win); |
|
|
CLIENT_LOCK(); |
|
|
#else |
|
|
|
|
|
# ifdef KHRONOS_EGL_PLATFORM_OPENWFC |
|
|
wfc_stream_await_buffer((WFCNativeStreamType) surface->internal_handle); |
|
|
# else |
|
|
# ifndef RPC_LIBRARY |
|
|
if (surface->buffers > 1) { |
|
|
//TODO implement khan (khronos async notification) receiver for linux |
|
|
# ifndef RPC_DIRECT_MULTI |
|
|
vcos_log_trace("eglSwapBuffers waiting for semaphore"); |
|
|
khronos_platform_semaphore_acquire(&surface->avail_buffers); |
|
|
# endif |
|
|
} |
|
|
# endif // RPC_LIBRARY |
|
|
# endif // KHRONOS_EGL_PLATFORM_OPENWFC |
|
|
|
|
|
#endif /* ANDROID */ |
|
|
|
|
|
} else { |
|
|
#ifdef KHRN_COMMAND_MODE_DISPLAY |
|
|
//Check for single buffered windows surface (and VG) in which case call vgFlush to allow screen update for command mode screens |
|
|
EGL_SURFACE_T *surface = CLIENT_GET_THREAD_STATE()->openvg.draw; |
|
|
if (surface->type == WINDOW && surface->buffers==1 && thread->bound_api == EGL_OPENVG_API) { |
|
|
vgFlush(); |
|
|
} |
|
|
#endif |
|
|
} |
|
|
// else do nothing. eglSwapBuffers has no effect on pixmap or pbuffer surfaces |
|
|
} |
|
|
} |
|
|
|
|
|
result = (thread->error == EGL_SUCCESS); |
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
else |
|
|
result = EGL_FALSE; |
|
|
|
|
|
vcos_log_trace("eglSwapBuffers end"); |
|
|
|
|
|
return result; |
|
|
#endif // DIRECT_RENDERING |
|
|
} |
|
|
|
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglCopyBuffers(EGLDisplay dpy, EGLSurface surf, EGLNativePixmapType target) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLBoolean result; |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
EGL_SURFACE_T *surface; |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
|
|
|
surface = client_egl_get_surface(thread, process, surf); |
|
|
|
|
|
if ((thread->bound_api == EGL_OPENGL_ES_API && surface != thread->opengl.draw && surface != thread->opengl.read) |
|
|
|| (thread->bound_api == EGL_OPENVG_API && surface != thread->openvg.draw)) { |
|
|
/* Surface to be displayed must be bound to current context and API */ |
|
|
/* TODO remove this restriction, as we'll need to for eglSwapBuffers? */ |
|
|
thread->error = EGL_BAD_SURFACE; |
|
|
} else if (surface) { |
|
|
KHRN_IMAGE_WRAP_T image; |
|
|
|
|
|
if (!platform_get_pixmap_info(target, &image)) { |
|
|
thread->error = EGL_BAD_NATIVE_PIXMAP; |
|
|
} else { |
|
|
if (image.width != surface->width || image.height != surface->height) { |
|
|
thread->error = EGL_BAD_MATCH; |
|
|
} else { |
|
|
//Bear in mind it's possible to call eglCopyBuffers on a pixmap |
|
|
//surface which will result in the data being transferred twice, onto |
|
|
//two different native pixmaps. |
|
|
|
|
|
//TODO: flush the other API too? |
|
|
//TODO: is this necessary? |
|
|
flush_current_api(thread); |
|
|
|
|
|
get_color_data(surface->serverbuffer, &image); |
|
|
} |
|
|
khrn_platform_release_pixmap_info(target, &image); |
|
|
} |
|
|
} |
|
|
|
|
|
result = (thread->error == EGL_SUCCESS); |
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
else |
|
|
result = EGL_FALSE; |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
//#define EXPORT_DESTROY_BY_PID // define me to export a utility function which will destroy the resources associated with a given process |
|
|
#ifdef EXPORT_DESTROY_BY_PID |
|
|
EGLAPI void EGLAPIENTRY eglDestroyByPidBRCM(EGLDisplay dpy, uint32_t pid_0, uint32_t pid_1) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
EGLBoolean result; |
|
|
|
|
|
CLIENT_LOCK(); |
|
|
|
|
|
{ |
|
|
CLIENT_PROCESS_STATE_T *process = client_egl_get_process_state(thread, dpy, EGL_TRUE); |
|
|
|
|
|
if (!process) |
|
|
result = 0; |
|
|
else { |
|
|
RPC_CALL2(eglIntDestroyByPid_impl, |
|
|
thread, |
|
|
EGLINTDESTROYBYPID_ID, |
|
|
RPC_UINT(pid_0), |
|
|
RPC_UINT(pid_1)); |
|
|
|
|
|
result = 1; |
|
|
} |
|
|
} |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
#endif |
|
|
|
|
|
#ifdef DIRECT_RENDERING |
|
|
EGLAPI EGLBoolean EGLAPIENTRY eglDirectRenderingPointer(EGLDisplay dpy, EGLSurface surf, void *image) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread; |
|
|
CLIENT_PROCESS_STATE_T *process; |
|
|
EGLBoolean result = EGL_FALSE; |
|
|
|
|
|
if (CLIENT_LOCK_AND_GET_STATES(dpy, &thread, &process)) |
|
|
{ |
|
|
EGL_SURFACE_T *surface; |
|
|
|
|
|
thread->error = EGL_SUCCESS; |
|
|
|
|
|
surface = client_egl_get_surface(thread, process, surf); |
|
|
|
|
|
if (surface) |
|
|
{ |
|
|
KHRN_IMAGE_WRAP_T *image_wrap = (KHRN_IMAGE_WRAP_T *)image; |
|
|
surface->width = image_wrap->width; |
|
|
surface->height = image_wrap->height; |
|
|
RPC_CALL6(eglDirectRenderingPointer_impl, |
|
|
thread, |
|
|
EGLDIRECTRENDERINGPOINTER_ID, |
|
|
surface->serverbuffer, |
|
|
(uint32_t)image_wrap->storage, |
|
|
image_wrap->format, |
|
|
image_wrap->width, |
|
|
image_wrap->height, |
|
|
image_wrap->stride); |
|
|
} |
|
|
|
|
|
result = (thread->error == EGL_SUCCESS ? EGL_TRUE : EGL_FALSE ); |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
} |
|
|
|
|
|
return result; |
|
|
} |
|
|
#endif |
|
|
|
|
|
#if EGL_proc_state_valid |
|
|
EGLAPI void EGLAPIENTRY eglProcStateValid( EGLDisplay dpy, EGLBoolean *result ) |
|
|
{ |
|
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE(); |
|
|
|
|
|
CLIENT_LOCK(); |
|
|
|
|
|
vcos_log_trace("eglProcStateValid dpy=%d", (int)dpy ); |
|
|
|
|
|
{ |
|
|
CLIENT_PROCESS_STATE_T *process = client_egl_get_process_state(thread, dpy, EGL_TRUE); |
|
|
|
|
|
if (!process) { |
|
|
*result = EGL_FALSE; |
|
|
} |
|
|
else { |
|
|
*result = EGL_TRUE; |
|
|
} |
|
|
} |
|
|
|
|
|
CLIENT_UNLOCK(); |
|
|
|
|
|
vcos_log_trace("eglProcStateValid result=%d", *result ); |
|
|
return; |
|
|
} |
|
|
#endif |
|
|
|
|
|
|