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

918 lines
29 KiB

/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define VCOS_LOG_CATEGORY (&egl_client_log_cat)
#include "interface/khronos/common/khrn_int_common.h"
#include "interface/khronos/common/khrn_client_rpc.h"
#include "interface/khronos/common/khrn_client_platform.h"
#include "interface/khronos/egl/egl_client_surface.h"
#include "interface/khronos/egl/egl_client_config.h"
#include "interface/khronos/common/khrn_client.h"
#ifdef KHRONOS_EGL_PLATFORM_OPENWFC
#include "interface/khronos/wf/wfc_int.h"
#include "interface/khronos/wf/wfc_client_stream.h"
#endif
#ifdef RPC_DIRECT
#include "interface/khronos/egl/egl_int_impl.h"
#endif
#include <stdlib.h>
/*
surface_pool
cache for a small number of pre-allocated surface objects
Validity:
surfaces[i] is valid if allocated & (1<<i)
*/
#define EGL_SURFACE_POOL_SIZE 2
static struct
{
EGL_SURFACE_T surfaces[EGL_SURFACE_POOL_SIZE];
uint32_t allocated;
} surface_pool;
extern VCOS_LOG_CAT_T egl_client_log_cat;
/*
EGL_SURFACE_T* egl_surface_pool_alloc(void)
Implementation notes:
We have a small static pool of structures (surface_pool) which we try and allocate out of
in order to reduce memory fragmentation. When we have run out of space in the pool we
resort to khrn_platform_malloc.
Preconditions:
Whoever calls this must initialise (or free) the returned structure in order to satisfy the invariant
on surface_pool.
Postconditions:
Return value is NULL or an uninitialised EGL_SURFACE_T structure, valid until egl_surface_pool_free
is called.
*/
static EGL_SURFACE_T* egl_surface_pool_alloc(void)
{
int i = 0;
while(surface_pool.allocated & (1 << i))
i++;
if (i < EGL_SURFACE_POOL_SIZE)
{
surface_pool.allocated |= 1 << i;
return &surface_pool.surfaces[i];
}
else
{
return (EGL_SURFACE_T*)khrn_platform_malloc(sizeof(EGL_SURFACE_T), "EGL_SURFACE_T");
}
}
static void egl_surface_pool_free(EGL_SURFACE_T* surface)
{
unsigned int i = 0;
/* todo: this doesn't belong here */
//semaphore now gets destroyed on async callback from VC
if (surface->avail_buffers_valid)
khronos_platform_semaphore_destroy(&surface->avail_buffers);
surface->avail_buffers_valid = false;
i = (unsigned int) (surface - surface_pool.surfaces);
if (i < EGL_SURFACE_POOL_SIZE)
{
surface_pool.allocated &= ~(1 << i);
}
else
{
khrn_platform_free((void*)surface);
}
}
/*
EGLBoolean egl_surface_check_attribs_window(const EGLint *attrib_list, EGLBoolean *linear, EGLBoolean *premult, EGLBoolean *single)
TODO: are we actually supposed to validate our parameters and generate an
error if they're wrong? I can't find an explicit mention in the spec about it.
(except for EGL_WIDTH and EGL_HEIGHT in pbuffer)
Preconditions:
type in {WINDOW, PBUFFER, PIXMAP}
attrib_list is NULL or a pointer to an EGL_NONE-terminated list of attribute/value pairs
linear, premult are NULL or valid pointers
If type == WINDOW then single is NULL or a valid pointer
If type == PBUFFER then width, height, largest_pbuffer, texture_format, texture_target, mipmap_texture are NULL or valid pointers
Postconditions:
-
*/
bool egl_surface_check_attribs(
EGL_SURFACE_TYPE_T type,
const EGLint *attrib_list,
bool *linear,
bool *premult,
bool *single,
int *width,
int *height,
bool *largest_pbuffer,
EGLenum *texture_format,
EGLenum *texture_target,
bool *mipmap_texture
)
{
if (!attrib_list)
return true;
while (*attrib_list != EGL_NONE) {
int name = *attrib_list++;
int value = *attrib_list++;
switch (name) {
case EGL_VG_COLORSPACE:
if (value != EGL_VG_COLORSPACE_sRGB && value != EGL_VG_COLORSPACE_LINEAR)
return false;
if (value == EGL_VG_COLORSPACE_LINEAR && linear != NULL)
*linear = true;
break;
case EGL_VG_ALPHA_FORMAT:
if (value != EGL_VG_ALPHA_FORMAT_NONPRE && value != EGL_VG_ALPHA_FORMAT_PRE)
return false;
if (value == EGL_VG_ALPHA_FORMAT_PRE && premult != NULL)
*premult = true;
break;
/* For WINDOW types only */
case EGL_RENDER_BUFFER:
if (type != WINDOW || (value != EGL_SINGLE_BUFFER && value != EGL_BACK_BUFFER))
return false;
if (value == EGL_SINGLE_BUFFER && single != NULL)
*single = true;
break;
/* For PBUFFER types only */
case EGL_WIDTH:
if (type != PBUFFER || value < 0)
return false;
if (width != NULL)
*width = value;
break;
case EGL_HEIGHT:
if (type != PBUFFER || value < 0)
return false;
if (height != NULL)
*height = value;
break;
case EGL_LARGEST_PBUFFER:
if (type != PBUFFER || (value != EGL_FALSE && value != EGL_TRUE))
return false;
if (largest_pbuffer != NULL)
*largest_pbuffer = value;
break;
case EGL_TEXTURE_FORMAT:
if (type != PBUFFER || (value != EGL_NO_TEXTURE && value != EGL_TEXTURE_RGB && value != EGL_TEXTURE_RGBA))
return false;
if (texture_format != NULL)
*texture_format = value;
break;
case EGL_TEXTURE_TARGET:
if (type != PBUFFER || (value != EGL_NO_TEXTURE && value != EGL_TEXTURE_2D))
return false;
if (texture_target != NULL)
*texture_target = value;
break;
case EGL_MIPMAP_TEXTURE:
if (type != PBUFFER || (value != EGL_FALSE && value != EGL_TRUE))
return false;
if (mipmap_texture != NULL)
*mipmap_texture = value;
break;
default:
return false;
}
}
return true;
}
/*
EGL_SURFACE_T *egl_surface_create(
EGLSurface name,
EGL_SURFACE_TYPE_T type,
EGL_SURFACE_COLORSPACE_T colorspace,
EGL_SURFACE_ALPHAFORMAT_T alphaformat,
uint32_t buffers,
uint32_t width,
uint32_t height,
EGLConfig config,
EGLNativeWindowType win,
uint32_t serverwin,
bool largest_pbuffer,
bool mipmap_texture,
EGLenum texture_format,
EGLenum texture_target,
EGLNativePixmapType pixmap,
const uint32_t *pixmap_server_handle)
Implementation notes:
TODO: respect largest_pbuffer?
Preconditions:
type in {WINDOW,PBUFFER,PIXMAP}
colorspace in {SRGB,LINEAR}
alphaformat in {NONPRE,PRE}
1 <= buffers <= EGL_MAX_BUFFERS
0 < width, 0 < height
If !largest_pbuffer then width <= EGL_CONFIG_MAX_WIDTH, height <= EGL_CONFIG_MAX_HEIGHT
config is a valid EGL config
If type == WINDOW then
win is a valid client-side handle to window W
serverwin is a valid server-side handle to window W
else
win == 0
serverwin == PLATFORM_WIN_NONE
If type == PBBUFFER then
texture_format in {EGL_NO_TEXTURE, EGL_TEXTURE_RGB, EGL_TEXTURE_RGBA}
texture_target in {EGL_NO_TEXTURE, EGL_TEXTURE_2D}
else
largest_pbuffer == mipmap_texture == false
texture_format == texture_target == EGL_NO_TEXTURE
If type == PIXMAP then
pixmap is a valid client-side handle to pixmap P
If pixmap is a server-side pixmap then
pixmap_server_handle is a pointer to two elements, representing a valid server-side handle to pixmap P
else
pixmap_server_handle == 0
else
pixmap == pixmap_server_handle == 0
Postconditions:
Return value is NULL or an EGL_SURFACE_T structure, valid until egl_surface_free is called
Invariants preserved:
All invaraints on EGL_SURFACE_T
*/
EGL_SURFACE_T *egl_surface_create(
EGLSurface name,
EGL_SURFACE_TYPE_T type,
EGL_SURFACE_COLORSPACE_T colorspace,
EGL_SURFACE_ALPHAFORMAT_T alphaformat,
uint32_t buffers,
uint32_t width,
uint32_t height,
EGLConfig config,
EGLNativeWindowType win,
uint32_t serverwin,
bool largest_pbuffer,
bool texture_compatibility,
bool mipmap_texture,
EGLenum texture_format,
EGLenum texture_target,
EGLNativePixmapType pixmap,
const uint32_t *pixmap_server_handle)
{
KHRN_IMAGE_FORMAT_T color;
KHRN_IMAGE_FORMAT_T depth;
KHRN_IMAGE_FORMAT_T mask;
KHRN_IMAGE_FORMAT_T multi;
uint32_t configid;
uint32_t sem_name;
EGLint config_depth_bits;
EGLint config_stencil_bits;
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
EGL_SURFACE_T *surface = egl_surface_pool_alloc();
//vcos_assert(width > 0 && height > 0);
vcos_assert((width <= EGL_CONFIG_MAX_WIDTH && height <= EGL_CONFIG_MAX_HEIGHT) || largest_pbuffer);
if (!surface) {
return 0;
}
/* TODO: respect largest_pbuffer? */
surface->name = name;
surface->type = type;
surface->colorspace = colorspace;
surface->alphaformat = alphaformat;
surface->config = config;
surface->win = win;
surface->width = width;
surface->height = height;
surface->swap_interval = 1;
surface->base_width = width;
surface->base_height = height;
surface->internal_handle = serverwin;
surface->largest_pbuffer = largest_pbuffer;
surface->mipmap_texture = mipmap_texture;
surface->mipmap_level = 0;
surface->texture_format = texture_format;
surface->texture_target = texture_target;
surface->pixmap = pixmap;
surface->pixmap_server_handle[0] = 0;
surface->pixmap_server_handle[1] = (uint32_t)-1;
surface->server_owned = false;
surface->swap_behavior = buffers > 1 ? EGL_BUFFER_DESTROYED : EGL_BUFFER_PRESERVED;
surface->multisample_resolve = EGL_MULTISAMPLE_RESOLVE_DEFAULT;
surface->context_binding_count = 0;
surface->is_destroyed = false;
#if EGL_KHR_lock_surface
surface->is_locked = false;
surface->mapped_buffer = 0;
#endif
configid = egl_config_to_id(config);
color = egl_config_get_color_format(configid);
if (texture_compatibility) { color = khrn_image_to_tf_format(color); }
if (alphaformat == PRE) { color = (KHRN_IMAGE_FORMAT_T)(color | IMAGE_FORMAT_PRE); }
if (colorspace == LINEAR) { color = (KHRN_IMAGE_FORMAT_T)(color | IMAGE_FORMAT_LIN); }
depth = egl_config_get_depth_format(configid);
mask = egl_config_get_mask_format(configid);
multi = egl_config_get_multisample_format(configid);
/* Find depth and stencil bits from chosen config (these may NOT be the same as the underlying format!) */
egl_config_get_attrib(configid, EGL_DEPTH_SIZE, &config_depth_bits);
egl_config_get_attrib(configid, EGL_STENCIL_SIZE, &config_stencil_bits);
vcos_assert(color != IMAGE_FORMAT_INVALID);
#ifdef KHRONOS_EGL_PLATFORM_OPENWFC
// Create stream for this window
if(type != PBUFFER)
{
WFCNativeStreamType stream = (WFCNativeStreamType) surface->internal_handle;
vcos_assert(stream != WFC_INVALID_HANDLE);
uint32_t failure = wfc_stream_create(stream, WFC_STREAM_FLAGS_EGL);
vcos_log_trace("Creating stream with handle %X", stream);
vcos_assert(failure == 0);
} // if
#endif
surface->buffers = buffers;
if (pixmap_server_handle) {
vcos_assert(type == PIXMAP);
surface->pixmap_server_handle[0] = pixmap_server_handle[0];
surface->pixmap_server_handle[1] = pixmap_server_handle[1];
surface->serverbuffer = RPC_UINT_RES(RPC_CALL7_RES(eglIntCreateWrappedSurface_impl,
thread,
EGLINTCREATEWRAPPEDSURFACE_ID,
RPC_UINT(pixmap_server_handle[0]),
RPC_UINT(pixmap_server_handle[1]),
RPC_UINT(depth),
RPC_UINT(mask),
RPC_UINT(multi),
RPC_UINT(config_depth_bits),
RPC_UINT(config_stencil_bits)));
surface->avail_buffers_valid = false;
} else {
#ifdef __SYMBIAN32__
uint32_t nbuff = 0;
surface->avail_buffers_valid = 0;
if (surface->buffers > 1) {
uint64_t pid = rpc_get_client_id(thread);
int sem[3] = { (int)pid, (int)(pid >> 32), (int)name };
if (khronos_platform_semaphore_create(&surface->avail_buffers, sem, surface->buffers) == KHR_SUCCESS)
surface->avail_buffers_valid = 1;
nbuff = (int)surface->avail_buffers;
}
if (surface->buffers == 1 || surface->avail_buffers_valid) {
uint32_t results[3];
RPC_CALL15_OUT_CTRL(eglIntCreateSurface_impl,
thread,
EGLINTCREATESURFACE_ID,
RPC_UINT(serverwin),
RPC_UINT(buffers),
RPC_UINT(width),
RPC_UINT(height),
RPC_UINT(color),
RPC_UINT(depth),
RPC_UINT(mask),
RPC_UINT(multi),
RPC_UINT(largest_pbuffer),
RPC_UINT(mipmap_texture),
RPC_UINT(config_depth_bits),
RPC_UINT(config_stencil_bits),
RPC_UINT((int)nbuff),
RPC_UINT(type),
results);
#else
surface->avail_buffers_valid = false;
sem_name = KHRN_NO_SEMAPHORE;
#ifndef KHRONOS_EGL_PLATFORM_OPENWFC
if (surface->buffers > 1) {
uint64_t pid = rpc_get_client_id(thread);
int sem[3];
sem[0] = (int)pid; sem[1] = (int)(pid >> 32); sem[2] = (int)name;
sem_name = (int)name;
if (khronos_platform_semaphore_create(&surface->avail_buffers, sem, surface->buffers) == KHR_SUCCESS)
surface->avail_buffers_valid = true;
}
if (sem_name == KHRN_NO_SEMAPHORE || surface->avail_buffers_valid) {
#else
sem_name = (uint32_t)surface->internal_handle;
vcos_log_trace("Surface create has semaphore %X", sem_name);
#endif
uint32_t results[3];
RPC_CALL15_OUT_CTRL(eglIntCreateSurface_impl,
thread,
EGLINTCREATESURFACE_ID,
RPC_UINT(serverwin),
RPC_UINT(buffers),
RPC_UINT(width),
RPC_UINT(height),
RPC_UINT(color),
RPC_UINT(depth),
RPC_UINT(mask),
RPC_UINT(multi),
RPC_UINT(largest_pbuffer),
RPC_UINT(mipmap_texture),
RPC_UINT(config_depth_bits),
RPC_UINT(config_stencil_bits),
RPC_UINT(sem_name),
RPC_UINT(type),
results);
#endif
surface->width = results[0];
surface->height = results[1];
surface->serverbuffer = results[2];
#ifndef KHRONOS_EGL_PLATFORM_OPENWFC
} else {
surface->serverbuffer = 0;
}
#endif
}
if (surface->serverbuffer)
return surface;
else {
/* Server failed to create a surface due to out-of-memory or
we failed to create the named semaphore object. */
egl_surface_pool_free(surface);
return 0;
}
}
#ifndef NO_OPENVG
/* Either returns a valid EGL_SURFACE_T, or returns null and sets error appropriately */
EGL_SURFACE_T *egl_surface_from_vg_image(
VGImage vg_handle,
EGLSurface name,
EGLConfig config,
EGLBoolean largest_pbuffer,
EGLBoolean mipmap_texture,
EGLenum texture_format,
EGLenum texture_target,
EGLint *error)
{
KHRN_IMAGE_FORMAT_T color;
KHRN_IMAGE_FORMAT_T depth;
KHRN_IMAGE_FORMAT_T mask;
KHRN_IMAGE_FORMAT_T multi;
EGLint config_depth_bits;
EGLint config_stencil_bits;
uint32_t results[5];
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
EGL_SURFACE_T *surface = egl_surface_pool_alloc();
if (!surface) {
*error = EGL_BAD_ALLOC;
return 0;
}
/* TODO: respect largest_pbuffer? */
surface->name = name;
surface->type = PBUFFER;
surface->config = config;
surface->win = 0;
surface->swap_interval = 1;
surface->largest_pbuffer = largest_pbuffer;
surface->mipmap_texture = mipmap_texture;
surface->mipmap_level = 0;
surface->texture_format = texture_format;
surface->texture_target = texture_target;
surface->pixmap = 0;
surface->pixmap_server_handle[0] = 0;
surface->pixmap_server_handle[1] = (uint32_t)-1;
surface->server_owned = false;
surface->context_binding_count = 0;
surface->is_destroyed = false;
#if EGL_KHR_lock_surface
surface->is_locked = false;
surface->mapped_buffer = 0;
#endif
color = egl_config_get_color_format((int)(size_t)config - 1);
depth = egl_config_get_depth_format((int)(size_t)config - 1);
mask = egl_config_get_mask_format((int)(size_t)config - 1);
multi = egl_config_get_multisample_format((int)(size_t)config - 1);
/* Find depth and stencil bits from chosen config (these may NOT be the same as the underlying format!) */
egl_config_get_attrib((int)(size_t)config - 1, EGL_DEPTH_SIZE, &config_depth_bits);
egl_config_get_attrib((int)(size_t)config - 1, EGL_STENCIL_SIZE, &config_stencil_bits);
vcos_assert(color != IMAGE_FORMAT_INVALID);
surface->buffers = 1;
RPC_CALL9_OUT_CTRL(eglIntCreatePbufferFromVGImage_impl,
thread,
EGLINTCREATEPBUFFERFROMVGIMAGE_ID,
RPC_UINT(vg_handle),
RPC_UINT(color),
RPC_UINT(depth),
RPC_UINT(mask),
RPC_UINT(multi),
RPC_UINT(mipmap_texture),
RPC_UINT(config_depth_bits),
RPC_UINT(config_stencil_bits),
results);
surface->avail_buffers_valid = false;
if (results[0]) {
KHRN_IMAGE_FORMAT_T format = (KHRN_IMAGE_FORMAT_T)results[4];
surface->serverbuffer = results[0];
surface->width = results[2];
surface->height = results[3];
/* TODO: picking apart image formats like this seems messy */
surface->colorspace = (format & IMAGE_FORMAT_LIN) ? LINEAR : SRGB;
surface->alphaformat = (format & IMAGE_FORMAT_PRE) ? PRE : NONPRE;
*error = EGL_SUCCESS;
return surface;
} else {
*error = results[1];
egl_surface_pool_free(surface);
return 0;
}
}
#endif /* NO_OPENVG */
/*
void egl_surface_free(EGL_SURFACE_T *surface)
Preconditions:
surface is a valid EGL_SURFACE_T returned from egl_surface_create or egl_surface_from_vg_image
Postconditions:
surface is freed and any associated server-side resources are dereferenced.
*/
void egl_surface_free(EGL_SURFACE_T *surface)
{
CLIENT_THREAD_STATE_T *thread;
vcos_log_trace("egl_surface_free");
thread = CLIENT_GET_THREAD_STATE();
if( surface->type == WINDOW ) {
vcos_log_trace("egl_surface_free: calling platform_destroy_winhandle...");
platform_destroy_winhandle( surface->win, surface->internal_handle );
}
/* return value ignored -- read performed to ensure blocking. we want this to
* block so clients can safely destroy the surface's window as soon as the
* egl call that destroys the surface returns (usually eglDestroySurface, but
* could be eg eglMakeCurrent) */
vcos_log_trace("egl_surface_free: calling eglIntDestroySurface_impl via RPC...; "
"thread = 0x%X; serverbuffer = 0x%X",
(uint32_t) thread, (uint32_t) surface->serverbuffer);
(void)RPC_INT_RES(RPC_CALL1_RES(eglIntDestroySurface_impl,
thread,
EGLINTDESTROYSURFACE_ID,
RPC_UINT(surface->serverbuffer)));
#ifdef KHRONOS_EGL_PLATFORM_OPENWFC
if(surface->internal_handle != PLATFORM_WIN_NONE) // TODO what about pbuffers?
{
vcos_log_trace("egl_surface_free: calling wfc_stream_destroy...");
wfc_stream_destroy((WFCNativeStreamType) surface->internal_handle);
}
#endif
vcos_log_trace("egl_surface_free: calling egl_surface_pool_free...");
egl_surface_pool_free(surface);
vcos_log_trace("egl_surface_free: end");
}
EGLint egl_surface_get_render_buffer(EGL_SURFACE_T *surface)
{
switch (surface->type) {
case WINDOW:
if (surface->buffers == 1)
return EGL_SINGLE_BUFFER;
else
return EGL_BACK_BUFFER;
case PBUFFER:
return EGL_BACK_BUFFER;
case PIXMAP:
return EGL_SINGLE_BUFFER;
default:
UNREACHABLE();
return EGL_NONE;
}
}
EGLBoolean egl_surface_get_attrib(EGL_SURFACE_T *surface, EGLint attrib, EGLint *value)
{
switch (attrib) {
case EGL_VG_ALPHA_FORMAT:
if (surface->alphaformat == NONPRE)
*value = EGL_VG_ALPHA_FORMAT_NONPRE;
else
*value = EGL_VG_ALPHA_FORMAT_PRE;
return EGL_TRUE;
case EGL_VG_COLORSPACE:
if (surface->colorspace == SRGB)
*value = EGL_VG_COLORSPACE_sRGB;
else
*value = EGL_VG_COLORSPACE_LINEAR;
return EGL_TRUE;
case EGL_CONFIG_ID:
*value = (EGLint)(size_t)surface->config;
return EGL_TRUE;
case EGL_HEIGHT:
*value = surface->height;
return EGL_TRUE;
case EGL_HORIZONTAL_RESOLUTION:
case EGL_VERTICAL_RESOLUTION:
*value = EGL_UNKNOWN;
return EGL_TRUE;
case EGL_LARGEST_PBUFFER:
// For a window or pixmap surface, the contents of value are not modified.
if (surface->type == PBUFFER)
*value = surface->largest_pbuffer;
return EGL_TRUE;
case EGL_MIPMAP_TEXTURE:
// Querying EGL_MIPMAP_TEXTURE for a non-pbuffer surface is not
// an error, but value is not modified.
if (surface->type == PBUFFER)
*value = surface->mipmap_texture;
return EGL_TRUE;
case EGL_MIPMAP_LEVEL:
// Querying EGL_MIPMAP_LEVEL for a non-pbuffer surface is not
// an error, but value is not modified.
if (surface->type == PBUFFER)
*value = surface->mipmap_level;
return EGL_TRUE;
case EGL_PIXEL_ASPECT_RATIO:
*value = EGL_DISPLAY_SCALING;
return EGL_TRUE;
case EGL_RENDER_BUFFER:
*value = egl_surface_get_render_buffer(surface);
return EGL_TRUE;
case EGL_SWAP_BEHAVIOR:
*value = surface->swap_behavior;
return EGL_TRUE;
case EGL_MULTISAMPLE_RESOLVE:
*value = surface->multisample_resolve;
return EGL_TRUE;
case EGL_TEXTURE_FORMAT:
// Querying EGL_TEXTURE_FORMAT for a non-pbuffer surface is not
// an error, but value is not modified.
if (surface->type == PBUFFER)
*value = surface->texture_format;
return EGL_TRUE;
case EGL_TEXTURE_TARGET:
// Querying EGL_TEXTURE_TARGET for a non-pbuffer surface is not
// an error, but value is not modified.
if (surface->type == PBUFFER)
*value = surface->texture_target;
return EGL_TRUE;
case EGL_WIDTH:
*value = surface->width;
return EGL_TRUE;
default:
return EGL_FALSE;
}
}
EGLint egl_surface_set_attrib(EGL_SURFACE_T *surface, EGLint attrib, EGLint value)
{
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
switch (attrib) {
case EGL_MIPMAP_LEVEL:
if (surface->type == PBUFFER) {
RPC_CALL2(eglIntSelectMipmap_impl,
thread,
EGLINTSELECTMIPMAP_ID,
RPC_UINT(surface->serverbuffer),
RPC_INT(value));
surface->mipmap_level = value;
}
return EGL_SUCCESS;
case EGL_SWAP_BEHAVIOR:
switch (value) {
case EGL_BUFFER_PRESERVED:
case EGL_BUFFER_DESTROYED:
surface->swap_behavior = value;
return EGL_SUCCESS;
default:
return EGL_BAD_PARAMETER;
}
case EGL_MULTISAMPLE_RESOLVE:
switch (value) {
case EGL_MULTISAMPLE_RESOLVE_DEFAULT:
case EGL_MULTISAMPLE_RESOLVE_BOX:
surface->multisample_resolve = value;
return EGL_SUCCESS;
default:
return EGL_BAD_PARAMETER;
}
default:
return EGL_BAD_ATTRIBUTE;
}
}
#if EGL_KHR_lock_surface
EGLint egl_surface_get_mapped_buffer_attrib(EGL_SURFACE_T *surface, EGLint attrib, EGLint *value)
{
KHRN_IMAGE_FORMAT_T format;
bool is565;
vcos_assert(surface);
if (attrib == EGL_BITMAP_POINTER_KHR || attrib == EGL_BITMAP_PITCH_KHR) {
// Querying either of these causes the buffer to be mapped (if it isn't already)
// They also require that the surface is locked
if (!surface->is_locked) {
return EGL_BAD_ACCESS; // TODO is this the right error?
}
if (!surface->mapped_buffer) {
uint32_t size;
void *buffer;
format = egl_config_get_mapped_format((int)((intptr_t)surface->config - 1)); // type juggling to avoid pointer truncation warnings
size = khrn_image_get_size(format, surface->width, surface->height);
buffer = khrn_platform_malloc(size, "EGL_SURFACE_T.mapped_buffer");
if (!buffer) {
return EGL_BAD_ALLOC;
}
surface->mapped_buffer = buffer;
}
}
if (!egl_config_is_lockable((int)((intptr_t)surface->config-1))) { // type juggling to avoid pointer truncation warnings
// Calling any of these on unlockable surfaces is allowed but returns undefined results
*value = 0;
return EGL_SUCCESS;
}
format = egl_config_get_mapped_format((int)((intptr_t)surface->config-1)); // type juggling to avoid pointer truncation warnings
vcos_assert(format == RGB_565_RSO || format == ARGB_8888_RSO);
is565 = (format == RGB_565_RSO); // else 888
switch (attrib) {
case EGL_BITMAP_POINTER_KHR:
*value = (EGLint)(intptr_t)surface->mapped_buffer; // type juggling to avoid pointer truncation warnings
return EGL_SUCCESS;
case EGL_BITMAP_PITCH_KHR:
*value = khrn_image_get_stride(format, surface->width);
return EGL_SUCCESS;
case EGL_BITMAP_ORIGIN_KHR:
*value = EGL_LOWER_LEFT_KHR; // TODO: is this correct?
return EGL_SUCCESS;
case EGL_BITMAP_PIXEL_RED_OFFSET_KHR:
*value = is565 ? 11 : 0; // TODO: I've probably got these wrong too
return EGL_SUCCESS;
case EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR:
*value = is565 ? 5 : 8;
return EGL_SUCCESS;
case EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR:
*value = is565 ? 0 : 16;
return EGL_SUCCESS;
case EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR:
*value = is565 ? 0 : 24;
return EGL_SUCCESS;
case EGL_BITMAP_PIXEL_LUMINANCE_OFFSET_KHR:
*value = 0;
return EGL_SUCCESS;
default:
UNREACHABLE();
return EGL_BAD_PARAMETER;
}
}
#endif
/*
void egl_surface_maybe_free(EGL_SURFACE_T *surface)
Frees a surface together with its server-side resources if:
- it has been destroyed
- it is no longer current
Implementation notes:
-
Preconditions:
surface is a valid pointer
Postconditions:
Either:
- surface->is_destroyed is false (we don't change this), or
- surface->context_binding_count > 0, or
- surface has been deleted.
Invariants preserved:
-
Invariants used:
-
*/
void egl_surface_maybe_free(EGL_SURFACE_T *surface)
{
vcos_assert(surface);
if (!surface->is_destroyed)
return;
if (surface->context_binding_count)
return;
egl_surface_free(surface);
}