blob: 58d19a1a1b17f108ae3e7c3c70480e482fd19a34 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "EglDisplay.h"
#include "EglConfig.h"
#include "EglOsApi.h"
#include <GLcommon/GLutils.h>
#include <algorithm>
EglDisplay::EglDisplay(EGLNativeDisplayType dpy,
EglOS::Display* idpy) :
m_dpy(dpy),
m_idpy(idpy)
{
m_manager[GLES_1_1] = new ObjectNameManager(&m_globalNameSpace);
m_manager[GLES_2_0] = new ObjectNameManager(&m_globalNameSpace);
};
EglDisplay::~EglDisplay() {
emugl::Mutex::AutoLock mutex(m_lock);
//
// Destroy the global context if one was created.
// (should be true for windows platform only)
//
if (m_globalSharedContext != NULL) {
m_idpy->destroyContext(m_globalSharedContext);
}
m_configs.clear();
delete m_manager[GLES_1_1];
delete m_manager[GLES_2_0];
delete m_idpy;
}
void EglDisplay::initialize(int renderableType) {
emugl::Mutex::AutoLock mutex(m_lock);
m_initialized = true;
initConfigurations(renderableType);
m_configInitialized = true;
}
bool EglDisplay::isInitialize() { return m_initialized;}
void EglDisplay::terminate(){
emugl::Mutex::AutoLock mutex(m_lock);
m_contexts.clear();
m_surfaces.clear();
m_initialized = false;
}
namespace CompareEglConfigs {
// Old compare function used to initialize to something decently sorted.
struct StaticCompare {
bool operator()(const std::unique_ptr<EglConfig>& first,
const std::unique_ptr<EglConfig>& second) const {
return *first < *second;
}
};
// In actual usage, we need to dynamically re-sort configs
// that are returned to the user.
struct DynamicCompare;
// This is because the sorting order of configs is affected
// based on dynamic properties.
//
// See https://www.khronos.org/registry/egl/sdk/docs/man/html/eglChooseConfig.xhtml
// and the section on config sorting.
//
// If the user requests an EGL config with a particular EGL_RED_SIZE,
// for example, we must sort configs based on that criteria, while if that
// was not specified, we would just skip right on to sorting by buffer size.
// Below is an implementation of EGL config sorting according
// to spec, that takes the dynamic properties into account.
static int ColorBufferTypeVal(EGLenum type) {
switch (type) {
case EGL_RGB_BUFFER: return 0;
case EGL_LUMINANCE_BUFFER: return 1;
case EGL_YUV_BUFFER_EXT: return 2;
}
return 3;
}
static bool nonTrivialAttribVal(EGLint val) {
return val != 0 && val != EGL_DONT_CARE;
}
struct DynamicCompare {
public:
DynamicCompare(const EglConfig& wantedAttribs) {
EGLint wantedRVal = wantedAttribs.getConfAttrib(EGL_RED_SIZE);
EGLint wantedGVal = wantedAttribs.getConfAttrib(EGL_GREEN_SIZE);
EGLint wantedBVal = wantedAttribs.getConfAttrib(EGL_BLUE_SIZE);
EGLint wantedLVal = wantedAttribs.getConfAttrib(EGL_LUMINANCE_SIZE);
EGLint wantedAVal = wantedAttribs.getConfAttrib(EGL_ALPHA_SIZE);
wantedR = wantedAttribs.isWantedAttrib(EGL_RED_SIZE) && nonTrivialAttribVal(wantedRVal);
wantedG = wantedAttribs.isWantedAttrib(EGL_GREEN_SIZE) && nonTrivialAttribVal(wantedGVal);
wantedB = wantedAttribs.isWantedAttrib(EGL_BLUE_SIZE) && nonTrivialAttribVal(wantedBVal);
wantedL = wantedAttribs.isWantedAttrib(EGL_LUMINANCE_SIZE) && nonTrivialAttribVal(wantedLVal);
wantedA = wantedAttribs.isWantedAttrib(EGL_ALPHA_SIZE) && nonTrivialAttribVal(wantedAVal);
}
bool operator()(EglConfig* a, EglConfig* b) const {
EGLint aConformant = a->getConfAttrib(EGL_CONFORMANT);
EGLint bConformant = b->getConfAttrib(EGL_CONFORMANT);
if (aConformant != bConformant) {
return aConformant != 0;
}
EGLint aCaveat = a->getConfAttrib(EGL_CONFIG_CAVEAT);
EGLint bCaveat = b->getConfAttrib(EGL_CONFIG_CAVEAT);
if (aCaveat != bCaveat) {
return aCaveat < bCaveat;
}
EGLint aCbType = a->getConfAttrib(EGL_COLOR_BUFFER_TYPE);
EGLint bCbType = b->getConfAttrib(EGL_COLOR_BUFFER_TYPE);
if (aCbType != bCbType) {
return ColorBufferTypeVal(aCbType) <
ColorBufferTypeVal(bCbType);
}
EGLint aCbSize = 0;
EGLint bCbSize = 0;
if (wantedR) {
aCbSize += a->getConfAttrib(EGL_RED_SIZE);
bCbSize += b->getConfAttrib(EGL_RED_SIZE);
}
if (wantedG) {
aCbSize += a->getConfAttrib(EGL_GREEN_SIZE);
bCbSize += b->getConfAttrib(EGL_GREEN_SIZE);
}
if (wantedB) {
aCbSize += a->getConfAttrib(EGL_BLUE_SIZE);
bCbSize += b->getConfAttrib(EGL_BLUE_SIZE);
}
if (wantedL) {
aCbSize += a->getConfAttrib(EGL_LUMINANCE_SIZE);
bCbSize += b->getConfAttrib(EGL_LUMINANCE_SIZE);
}
if (wantedA) {
aCbSize += a->getConfAttrib(EGL_ALPHA_SIZE);
bCbSize += b->getConfAttrib(EGL_ALPHA_SIZE);
}
if (aCbSize != bCbSize) {
return aCbSize > bCbSize;
}
EGLint aBufferSize = a->getConfAttrib(EGL_BUFFER_SIZE);
EGLint bBufferSize = b->getConfAttrib(EGL_BUFFER_SIZE);
if (aBufferSize != bBufferSize) {
return aBufferSize < bBufferSize;
}
EGLint aSampleBuffersNum = a->getConfAttrib(EGL_SAMPLE_BUFFERS);
EGLint bSampleBuffersNum = b->getConfAttrib(EGL_SAMPLE_BUFFERS);
if (aSampleBuffersNum != bSampleBuffersNum) {
return aSampleBuffersNum < bSampleBuffersNum;
}
EGLint aSPP = a->getConfAttrib(EGL_SAMPLES);
EGLint bSPP = b->getConfAttrib(EGL_SAMPLES);
if (aSPP != bSPP) {
return aSPP < bSPP;
}
EGLint aDepthSize = a->getConfAttrib(EGL_DEPTH_SIZE);
EGLint bDepthSize = b->getConfAttrib(EGL_DEPTH_SIZE);
if (aDepthSize != bDepthSize) {
return aDepthSize < bDepthSize;
}
EGLint aStencilSize = a->getConfAttrib(EGL_STENCIL_SIZE);
EGLint bStencilSize = b->getConfAttrib(EGL_STENCIL_SIZE);
if (aStencilSize != bStencilSize) {
return aStencilSize < bStencilSize;
}
return a->getConfAttrib(EGL_CONFIG_ID) < b->getConfAttrib(EGL_CONFIG_ID);
}
bool wantedR;
bool wantedG;
bool wantedB;
bool wantedL;
bool wantedA;
};
}
void EglDisplay::addSimplePixelFormat(int red_size,
int green_size,
int blue_size,
int alpha_size) {
std::sort(m_configs.begin(), m_configs.end(), CompareEglConfigs::StaticCompare());
EGLConfig match;
EglConfig dummy(red_size,
green_size,
blue_size,
alpha_size, // RGB_565
EGL_DONT_CARE,
EGL_DONT_CARE,
16, // Depth
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
EGL_DONT_CARE,
NULL);
if(!doChooseConfigs(dummy, &match, 1))
{
return;
}
const EglConfig* config = (EglConfig*)match;
int bSize;
config->getConfAttrib(EGL_BUFFER_SIZE,&bSize);
if(bSize == 16)
{
return;
}
int max_config_id = 0;
for(ConfigsList::iterator it = m_configs.begin(); it != m_configs.end() ;++it) {
EGLint id;
(*it)->getConfAttrib(EGL_CONFIG_ID, &id);
if(id > max_config_id)
max_config_id = id;
}
EglConfig* newConfig = new EglConfig(*config,max_config_id+1,
red_size, green_size, blue_size,
alpha_size);
m_configs.emplace_back(newConfig);
}
void EglDisplay::addMissingConfigs() {
addSimplePixelFormat(5, 6, 5, 0); // RGB_565
addSimplePixelFormat(8, 8, 8, 0); // RGB_888
// (Host GPUs that are newer may not list RGB_888
// out of the box.)
}
void EglDisplay::initConfigurations(int renderableType) {
if (m_configInitialized) {
return;
}
m_idpy->queryConfigs(renderableType, addConfig, this);
addMissingConfigs();
std::sort(m_configs.begin(), m_configs.end(), CompareEglConfigs::StaticCompare());
#if EMUGL_DEBUG
for (ConfigsList::const_iterator it = m_configs.begin();
it != m_configs.end();
++it) {
EglConfig* config = it->get();
EGLint red, green, blue, alpha, depth, stencil, renderable, surface;
config->getConfAttrib(EGL_RED_SIZE, &red);
config->getConfAttrib(EGL_GREEN_SIZE, &green);
config->getConfAttrib(EGL_BLUE_SIZE, &blue);
config->getConfAttrib(EGL_ALPHA_SIZE, &alpha);
config->getConfAttrib(EGL_DEPTH_SIZE, &depth);
config->getConfAttrib(EGL_STENCIL_SIZE, &stencil);
config->getConfAttrib(EGL_RENDERABLE_TYPE, &renderable);
config->getConfAttrib(EGL_SURFACE_TYPE, &surface);
}
#endif // EMUGL_DEBUG
}
EglConfig* EglDisplay::getConfig(EGLConfig conf) const {
emugl::Mutex::AutoLock mutex(m_lock);
for(ConfigsList::const_iterator it = m_configs.begin();
it != m_configs.end();
++it) {
if(static_cast<EGLConfig>(it->get()) == conf) {
return it->get();
}
}
return NULL;
}
SurfacePtr EglDisplay::getSurface(EGLSurface surface) const {
emugl::Mutex::AutoLock mutex(m_lock);
/* surface is "key" in map<unsigned int, SurfacePtr>. */
unsigned int hndl = SafeUIntFromPointer(surface);
SurfacesHndlMap::const_iterator it = m_surfaces.find(hndl);
return it != m_surfaces.end() ?
(*it).second :
SurfacePtr();
}
ContextPtr EglDisplay::getContext(EGLContext ctx) const {
emugl::Mutex::AutoLock mutex(m_lock);
/* ctx is "key" in map<unsigned int, ContextPtr>. */
unsigned int hndl = SafeUIntFromPointer(ctx);
ContextsHndlMap::const_iterator it = m_contexts.find(hndl);
return it != m_contexts.end() ?
(*it).second :
ContextPtr();
}
bool EglDisplay::removeSurface(EGLSurface s) {
emugl::Mutex::AutoLock mutex(m_lock);
/* s is "key" in map<unsigned int, SurfacePtr>. */
unsigned int hndl = SafeUIntFromPointer(s);
SurfacesHndlMap::iterator it = m_surfaces.find(hndl);
if(it != m_surfaces.end()) {
m_surfaces.erase(it);
return true;
}
return false;
}
bool EglDisplay::removeContext(EGLContext ctx) {
emugl::Mutex::AutoLock mutex(m_lock);
/* ctx is "key" in map<unsigned int, ContextPtr>. */
unsigned int hndl = SafeUIntFromPointer(ctx);
ContextsHndlMap::iterator it = m_contexts.find(hndl);
if(it != m_contexts.end()) {
m_contexts.erase(it);
return true;
}
return false;
}
bool EglDisplay::removeContext(ContextPtr ctx) {
emugl::Mutex::AutoLock mutex(m_lock);
ContextsHndlMap::iterator it;
for(it = m_contexts.begin(); it != m_contexts.end();++it) {
if((*it).second.get() == ctx.get()){
break;
}
}
if(it != m_contexts.end()) {
m_contexts.erase(it);
return true;
}
return false;
}
EglConfig* EglDisplay::getConfig(EGLint id) const {
emugl::Mutex::AutoLock mutex(m_lock);
for(ConfigsList::const_iterator it = m_configs.begin();
it != m_configs.end();
++it) {
if((*it)->id() == id) {
return it->get();
}
}
return NULL;
}
int EglDisplay::getConfigs(EGLConfig* configs,int config_size) const {
emugl::Mutex::AutoLock mutex(m_lock);
int i = 0;
for(ConfigsList::const_iterator it = m_configs.begin();
it != m_configs.end() && i < config_size;
i++, ++it) {
configs[i] = static_cast<EGLConfig>(it->get());
}
return i;
}
int EglDisplay::chooseConfigs(const EglConfig& dummy,
EGLConfig* configs,
int config_size) const {
emugl::Mutex::AutoLock mutex(m_lock);
return doChooseConfigs(dummy, configs, config_size);
}
int EglDisplay::doChooseConfigs(const EglConfig& dummy,
EGLConfig* configs,
int config_size) const {
int added = 0;
std::vector<EglConfig*> validConfigs;
CHOOSE_CONFIG_DLOG("returning configs. ids: {");
for(ConfigsList::const_iterator it = m_configs.begin();
it != m_configs.end() && (added < config_size || !configs);
++it) {
if( (*it)->chosen(dummy)){
if(configs) {
CHOOSE_CONFIG_DLOG("valid config: id=0x%x", it->get()->id());
validConfigs.push_back(it->get());
}
added++;
}
}
CHOOSE_CONFIG_DLOG("sorting valid configs...");
std::sort(validConfigs.begin(),
validConfigs.end(),
CompareEglConfigs::DynamicCompare(dummy));
for (int i = 0; i < added; i++) {
configs[i] = static_cast<EGLConfig>(validConfigs[i]);
}
CHOOSE_CONFIG_DLOG("returning configs. ids end }");
return added;
}
EGLSurface EglDisplay::addSurface(SurfacePtr s ) {
emugl::Mutex::AutoLock mutex(m_lock);
unsigned int hndl = s.get()->getHndl();
EGLSurface ret =reinterpret_cast<EGLSurface> (hndl);
if(m_surfaces.find(hndl) != m_surfaces.end()) {
return ret;
}
m_surfaces[hndl] = s;
return ret;
}
EGLContext EglDisplay::addContext(ContextPtr ctx ) {
emugl::Mutex::AutoLock mutex(m_lock);
unsigned int hndl = ctx.get()->getHndl();
EGLContext ret = reinterpret_cast<EGLContext> (hndl);
if(m_contexts.find(hndl) != m_contexts.end()) {
return ret;
}
m_contexts[hndl] = ctx;
return ret;
}
EGLImageKHR EglDisplay::addImageKHR(ImagePtr img) {
emugl::Mutex::AutoLock mutex(m_lock);
do { ++m_nextEglImageId; } while(m_nextEglImageId == 0);
img->imageId = m_nextEglImageId;
m_eglImages[m_nextEglImageId] = img;
return reinterpret_cast<EGLImageKHR>(m_nextEglImageId);
}
ImagePtr EglDisplay::getImage(EGLImageKHR img) const {
emugl::Mutex::AutoLock mutex(m_lock);
/* img is "key" in map<unsigned int, ImagePtr>. */
unsigned int hndl = SafeUIntFromPointer(img);
ImagesHndlMap::const_iterator i( m_eglImages.find(hndl) );
return (i != m_eglImages.end()) ? (*i).second :ImagePtr();
}
bool EglDisplay:: destroyImageKHR(EGLImageKHR img) {
emugl::Mutex::AutoLock mutex(m_lock);
/* img is "key" in map<unsigned int, ImagePtr>. */
unsigned int hndl = SafeUIntFromPointer(img);
ImagesHndlMap::iterator i( m_eglImages.find(hndl) );
if (i != m_eglImages.end())
{
m_eglImages.erase(i);
return true;
}
return false;
}
EglOS::Context* EglDisplay::getGlobalSharedContext() const {
emugl::Mutex::AutoLock mutex(m_lock);
#ifndef _WIN32
// find an existing OpenGL context to share with, if exist
EglOS::Context* ret =
(EglOS::Context*)m_manager[GLES_1_1]->getGlobalContext();
if (!ret)
ret = (EglOS::Context*)m_manager[GLES_2_0]->getGlobalContext();
return ret;
#else
if (!m_globalSharedContext) {
//
// On windows we create a dummy context to serve as the
// "global context" which all contexts share with.
// This is because on windows it is not possible to share
// with a context which is already current. This dummy context
// will never be current to any thread so it is safe to share with.
// Create that context using the first config
if (m_configs.empty()) {
// Should not happen! config list should be initialized at this point
return NULL;
}
EglConfig *cfg = m_configs.front().get();
m_globalSharedContext = m_idpy->createContext(
cfg->nativeFormat(), NULL);
}
return m_globalSharedContext;
#endif
}
// static
void EglDisplay::addConfig(void* opaque, const EglOS::ConfigInfo* info) {
EglDisplay* display = static_cast<EglDisplay*>(opaque);
EglConfig* config = new EglConfig(
info->red_size,
info->green_size,
info->blue_size,
info->alpha_size,
info->caveat,
info->config_id,
info->depth_size,
info->frame_buffer_level,
info->max_pbuffer_width,
info->max_pbuffer_height,
info->max_pbuffer_size,
info->native_renderable,
info->renderable_type,
info->native_visual_id,
info->native_visual_type,
info->samples_per_pixel,
info->stencil_size,
info->surface_type,
info->transparent_type,
info->trans_red_val,
info->trans_green_val,
info->trans_blue_val,
info->recordable_android,
info->frmt);
display->m_configs.emplace_back(config);
}