blob: d0d2cf948fc163e4c6258b462dfff53f50f63d86 [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 "EglOsApi.h"
#include "MacNative.h"
#include "aemu/base/containers/Lookup.h"
#include "aemu/base/SharedLibrary.h"
#include "host-common/logging.h"
#include "GLcommon/GLLibrary.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <numeric>
#include <unordered_map>
#define MAX_PBUFFER_MIPMAP_LEVEL 1
using FinalizedConfigKey = std::pair<bool, int>;
struct FinalizedConfigHash {
std::size_t operator () (const FinalizedConfigKey& p) const {
return p.second + 9001 * (p.first ? 1 : 0);
}
};
using FinalizedConfigMap =
std::unordered_map<FinalizedConfigKey, void*, FinalizedConfigHash>;
static FinalizedConfigMap sFinalizedConfigs;
namespace {
class MacSurface : public EglOS::Surface {
public:
MacSurface(void* handle, SurfaceType type) :
Surface(type), m_handle(handle) {}
void* handle() const { return m_handle; }
bool hasMipmap() const { return m_hasMipmap; }
void setHasMipmap(bool value) { m_hasMipmap = value; }
static MacSurface* from(EglOS::Surface* s) {
return static_cast<MacSurface*>(s);
}
private:
void* m_handle = nullptr;
bool m_hasMipmap = false;
};
class MacContext : public EglOS::Context {
public:
explicit MacContext(bool isCoreProfile, void* context) :
EglOS::Context(isCoreProfile), mContext(context) {}
virtual ~MacContext() {
nsDestroyContext(mContext);
}
void* context() const { return mContext; }
virtual void* lowLevelContext() { return nsGetLowLevelContext(mContext); }
static void* from(EglOS::Context* c) {
return static_cast<MacContext*>(c)->context();
}
private:
void* mContext = nullptr;
};
class MacNativeSupportInfo {
public:
MacNativeSupportInfo() :
numNativeFormats(getNumPixelFormats()),
maxOpenGLProfile((MacOpenGLProfileVersions)setupCoreProfileNativeFormats()) { }
int numNativeFormats = 0;
MacOpenGLProfileVersions maxOpenGLProfile =
MAC_OPENGL_PROFILE_LEGACY;
static MacNativeSupportInfo* get() {
if (!sSupportInfo) {
sSupportInfo = new MacNativeSupportInfo();
}
return sSupportInfo;
}
private:
static MacNativeSupportInfo* sSupportInfo;
};
MacNativeSupportInfo* MacNativeSupportInfo::sSupportInfo = nullptr;
class MacPixelFormat : public EglOS::PixelFormat {
public:
MacPixelFormat(int handle, int redSize, int greenSize, int blueSize) :
mHandle(handle),
mRedSize(redSize),
mGreenSize(greenSize),
mBlueSize(blueSize) {}
EglOS::PixelFormat* clone() {
return new MacPixelFormat(mHandle, mRedSize, mGreenSize, mBlueSize);
}
int handle() const { return mHandle; }
int redSize() const { return mRedSize; }
int greenSize() const { return mGreenSize; }
int blueSize() const { return mBlueSize; }
static const MacPixelFormat* from(const EglOS::PixelFormat* f) {
return static_cast<const MacPixelFormat*>(f);
}
private:
MacPixelFormat();
MacPixelFormat(const MacPixelFormat& other);
int mHandle = 0;
int mRedSize = 0;
int mGreenSize = 0;
int mBlueSize = 0;
};
void pixelFormatToConfig(int index,
EglOS::AddConfigCallback addConfigFunc,
void* addConfigOpaque) {
EglOS::ConfigInfo info = {};
info.surface_type = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
//default values
info.native_visual_id = 0;
info.native_visual_type = EGL_NONE;
info.caveat = EGL_NONE;
info.native_renderable = EGL_FALSE;
info.renderable_type = EGL_OPENGL_ES_BIT |
EGL_OPENGL_ES2_BIT |
EGL_OPENGL_ES3_BIT;
info.max_pbuffer_width = PBUFFER_MAX_WIDTH;
info.max_pbuffer_height = PBUFFER_MAX_HEIGHT;
info.max_pbuffer_size = PBUFFER_MAX_PIXELS;
info.samples_per_pixel = 0;
info.frame_buffer_level = 0;
info.trans_red_val = 0;
info.trans_green_val = 0;
info.trans_blue_val = 0;
info.transparent_type = EGL_NONE;
/* All configs can end up having an alpha channel even if none was requested.
* The default config chooser in GLSurfaceView will therefore not find any
* matching config. Thus, make sure alpha is zero (or at least signalled as
* zero to the calling EGL layer) for the configs where it was intended to
* be zero. */
info.alpha_size = getPixelFormatAttrib(index, MAC_ALPHA_SIZE);
info.depth_size = getPixelFormatAttrib(index, MAC_DEPTH_SIZE);
info.stencil_size = getPixelFormatAttrib(index, MAC_STENCIL_SIZE);
info.samples_per_pixel = getPixelFormatAttrib(index, MAC_SAMPLES_PER_PIXEL);
//TODO: ask guy if it is OK
GLint colorSize = 0;
colorSize = (GLint)getPixelFormatAttrib(index, MAC_COLOR_SIZE);
info.red_size = info.green_size = info.blue_size = (colorSize / 4);
info.frmt = new MacPixelFormat(
index, info.red_size, info.green_size, info.blue_size);
(*addConfigFunc)(addConfigOpaque, &info);
}
class MacDisplay : public EglOS::Display {
public:
explicit MacDisplay(EGLNativeDisplayType dpy) : mDpy(dpy) {}
virtual EglOS::GlesVersion getMaxGlesVersion() {
switch (MacNativeSupportInfo::get()->maxOpenGLProfile) {
case MAC_OPENGL_PROFILE_LEGACY:
return EglOS::GlesVersion::ES2;
case MAC_OPENGL_PROFILE_3_2:
case MAC_OPENGL_PROFILE_4_1:
return EglOS::GlesVersion::ES30;
default:
return EglOS::GlesVersion::ES2;
}
}
virtual void queryConfigs(int renderableType,
EglOS::AddConfigCallback* addConfigFunc,
void* addConfigOpaque) {
for (int i = 0; i < MacNativeSupportInfo::get()->numNativeFormats; i++) {
pixelFormatToConfig(
i,
addConfigFunc,
addConfigOpaque);
}
// Also disable vsync.
int bestSwapInterval = 0;
nsSwapInterval(&bestSwapInterval);
}
virtual bool isValidNativeWin(EglOS::Surface* win) {
if (!win) { return false; }
if (win->type() != MacSurface::WINDOW) {
return false;
} else {
return isValidNativeWin(MacSurface::from(win)->handle());
}
}
virtual bool isValidNativeWin(EGLNativeWindowType win) {
unsigned int width, height;
if (!win) return false;
return nsGetWinDims(win, &width, &height);
}
virtual bool checkWindowPixelFormatMatch(
EGLNativeWindowType win,
const EglOS::PixelFormat* pixelFormat,
unsigned int* width,
unsigned int* height) {
bool ret = nsGetWinDims(win, width, height);
const MacPixelFormat* format = MacPixelFormat::from(pixelFormat);
int r = format->redSize();
int g = format->greenSize();
int b = format->blueSize();
bool match = nsCheckColor(win, r + g + b);
return ret && match;
}
virtual std::shared_ptr<EglOS::Context> createContext(
EGLint profileMask,
const EglOS::PixelFormat* pixelFormat,
EglOS::Context* sharedContext) {
void* macSharedContext =
sharedContext ? MacContext::from(sharedContext) : NULL;
bool isCoreProfile =
profileMask & EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
auto key = std::make_pair(isCoreProfile,
MacPixelFormat::from(pixelFormat)->handle());
void* nsFormat = nullptr;
if (auto format = android::base::find(sFinalizedConfigs, key)) {
nsFormat = *format;
} else {
nsFormat =
finalizePixelFormat(isCoreProfile,
MacPixelFormat::from(pixelFormat)->handle());
sFinalizedConfigs[key] = nsFormat;
}
return std::make_shared<MacContext>(
isCoreProfile,
nsCreateContext(nsFormat,
macSharedContext));
}
virtual EglOS::Surface* createPbufferSurface(
const EglOS::PixelFormat* pixelFormat,
const EglOS::PbufferInfo* info) {
GLenum glTexFormat = GL_RGBA, glTexTarget = GL_TEXTURE_2D;
switch (info->format) {
case EGL_TEXTURE_RGB:
glTexFormat = GL_RGB;
break;
case EGL_TEXTURE_RGBA:
glTexFormat = GL_RGBA;
break;
}
EGLint maxMipmap = info->hasMipmap ? MAX_PBUFFER_MIPMAP_LEVEL : 0;
bool isLegacyOpenGL =
MacNativeSupportInfo::get()->maxOpenGLProfile == MAC_OPENGL_PROFILE_LEGACY;
MacSurface* result = new MacSurface(
isLegacyOpenGL
? nsCreatePBuffer(glTexTarget, glTexFormat, maxMipmap,
info->width, info->height)
: nullptr,
MacSurface::PBUFFER);
result->setHasMipmap(info->hasMipmap);
return result;
}
virtual bool releasePbuffer(EglOS::Surface* pb) {
if (pb) {
nsDestroyPBuffer(MacSurface::from(pb)->handle());
delete pb;
}
return true;
}
virtual bool makeCurrent(EglOS::Surface* read,
EglOS::Surface* draw,
EglOS::Context* ctx) {
// check for unbind
if (ctx == NULL && read == NULL && draw == NULL) {
nsWindowMakeCurrent(NULL, NULL);
return true;
}
else if (ctx == NULL || read == NULL || draw == NULL) {
// error !
return false;
}
//dont supporting diffrent read & draw surfaces on Mac
if (read != draw) {
return false;
}
switch (draw->type()) {
case MacSurface::WINDOW:
nsWindowMakeCurrent(MacContext::from(ctx),
MacSurface::from(draw)->handle());
break;
case MacSurface::PBUFFER:
{
MacSurface* macdraw = MacSurface::from(draw);
int mipmapLevel = macdraw->hasMipmap() ? MAX_PBUFFER_MIPMAP_LEVEL : 0;
nsPBufferMakeCurrent(MacContext::from(ctx),
macdraw->handle(), mipmapLevel);
break;
}
default:
return false;
}
return true;
}
virtual void swapBuffers(EglOS::Surface* srfc) {
nsSwapBuffers();
}
EGLNativeDisplayType dpy() const { return mDpy; }
private:
EglOS::GlesVersion mMaxGlesVersion =
EglOS::GlesVersion::ES2;
EGLNativeDisplayType mDpy = {};
};
class MacGlLibrary : public GlLibrary {
public:
MacGlLibrary() {
static const char kLibName[] =
"/System/Library/Frameworks/OpenGL.framework/OpenGL";
char error[256];
mLib = android::base::SharedLibrary::open(kLibName, error, sizeof(error));
if (!mLib) {
ERR("%s: Could not open GL library %s [%s]\n",
__FUNCTION__, kLibName, error);
}
}
~MacGlLibrary() {
}
// override
virtual GlFunctionPointer findSymbol(const char* name) {
if (!mLib) {
return NULL;
}
return reinterpret_cast<GlFunctionPointer>(mLib->findSymbol(name));
}
private:
android::base::SharedLibrary* mLib = nullptr;
};
class MacEngine : public EglOS::Engine {
public:
virtual EglOS::Display* getDefaultDisplay() {
return new MacDisplay(0);
}
virtual GlLibrary* getGlLibrary() {
return &mGlLib;
}
virtual void* eglGetProcAddress(const char*) {
return 0;
}
virtual EglOS::Surface* createWindowSurface(EglOS::PixelFormat* cfg,
EGLNativeWindowType wnd) {
return new MacSurface(wnd, MacSurface::WINDOW);
}
private:
MacGlLibrary mGlLib;
};
static MacEngine* sHostEngine = nullptr;
} // namespace
// static
EglOS::Engine* EglOS::Engine::getHostInstance() {
if (!sHostEngine) {
sHostEngine = new MacEngine();
}
return sHostEngine;
}