mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-02-12 10:15:54 +00:00
2512 lines
85 KiB
C
2512 lines
85 KiB
C
/*
|
||
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 <20><>OpenGL ES<45><53> or <20><>OpenVG<56><47>.
|
||
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<65>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
|
||
|