1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "EglOsApi.h"
17
18 #include "MacNative.h"
19
20 #include "aemu/base/containers/Lookup.h"
21 #include "aemu/base/SharedLibrary.h"
22
23 #include "host-common/logging.h"
24 #include "GLcommon/GLLibrary.h"
25
26 #include <EGL/egl.h>
27 #include <EGL/eglext.h>
28
29 #include <numeric>
30 #include <unordered_map>
31
32 #define MAX_PBUFFER_MIPMAP_LEVEL 1
33
34 using FinalizedConfigKey = std::pair<bool, int>;
35 struct FinalizedConfigHash {
operator ()FinalizedConfigHash36 std::size_t operator () (const FinalizedConfigKey& p) const {
37 return p.second + 9001 * (p.first ? 1 : 0);
38 }
39 };
40 using FinalizedConfigMap =
41 std::unordered_map<FinalizedConfigKey, void*, FinalizedConfigHash>;
42
43 static FinalizedConfigMap sFinalizedConfigs;
44
45 namespace {
46
47 class MacSurface : public EglOS::Surface {
48 public:
MacSurface(void * handle,SurfaceType type)49 MacSurface(void* handle, SurfaceType type) :
50 Surface(type), m_handle(handle) {}
51
handle() const52 void* handle() const { return m_handle; }
53
hasMipmap() const54 bool hasMipmap() const { return m_hasMipmap; }
setHasMipmap(bool value)55 void setHasMipmap(bool value) { m_hasMipmap = value; }
56
from(EglOS::Surface * s)57 static MacSurface* from(EglOS::Surface* s) {
58 return static_cast<MacSurface*>(s);
59 }
60
61 private:
62 void* m_handle = nullptr;
63 bool m_hasMipmap = false;
64 };
65
66 class MacContext : public EglOS::Context {
67 public:
MacContext(bool isCoreProfile,void * context)68 explicit MacContext(bool isCoreProfile, void* context) :
69 EglOS::Context(isCoreProfile), mContext(context) {}
70
~MacContext()71 virtual ~MacContext() {
72 nsDestroyContext(mContext);
73 }
74
context() const75 void* context() const { return mContext; }
76
lowLevelContext()77 virtual void* lowLevelContext() { return nsGetLowLevelContext(mContext); }
78
from(EglOS::Context * c)79 static void* from(EglOS::Context* c) {
80 return static_cast<MacContext*>(c)->context();
81 }
82
83 private:
84 void* mContext = nullptr;
85 };
86
87 class MacNativeSupportInfo {
88 public:
MacNativeSupportInfo()89 MacNativeSupportInfo() :
90 numNativeFormats(getNumPixelFormats()),
91 maxOpenGLProfile((MacOpenGLProfileVersions)setupCoreProfileNativeFormats()) { }
92 int numNativeFormats = 0;
93 MacOpenGLProfileVersions maxOpenGLProfile =
94 MAC_OPENGL_PROFILE_LEGACY;
get()95 static MacNativeSupportInfo* get() {
96 if (!sSupportInfo) {
97 sSupportInfo = new MacNativeSupportInfo();
98 }
99 return sSupportInfo;
100 }
101 private:
102 static MacNativeSupportInfo* sSupportInfo;
103 };
104
105 MacNativeSupportInfo* MacNativeSupportInfo::sSupportInfo = nullptr;
106
107 class MacPixelFormat : public EglOS::PixelFormat {
108 public:
MacPixelFormat(int handle,int redSize,int greenSize,int blueSize)109 MacPixelFormat(int handle, int redSize, int greenSize, int blueSize) :
110 mHandle(handle),
111 mRedSize(redSize),
112 mGreenSize(greenSize),
113 mBlueSize(blueSize) {}
114
clone()115 EglOS::PixelFormat* clone() {
116 return new MacPixelFormat(mHandle, mRedSize, mGreenSize, mBlueSize);
117 }
118
handle() const119 int handle() const { return mHandle; }
redSize() const120 int redSize() const { return mRedSize; }
greenSize() const121 int greenSize() const { return mGreenSize; }
blueSize() const122 int blueSize() const { return mBlueSize; }
123
from(const EglOS::PixelFormat * f)124 static const MacPixelFormat* from(const EglOS::PixelFormat* f) {
125 return static_cast<const MacPixelFormat*>(f);
126 }
127
128 private:
129 MacPixelFormat();
130 MacPixelFormat(const MacPixelFormat& other);
131
132 int mHandle = 0;
133 int mRedSize = 0;
134 int mGreenSize = 0;
135 int mBlueSize = 0;
136 };
137
138
pixelFormatToConfig(int index,EglOS::AddConfigCallback addConfigFunc,void * addConfigOpaque)139 void pixelFormatToConfig(int index,
140 EglOS::AddConfigCallback addConfigFunc,
141 void* addConfigOpaque) {
142 EglOS::ConfigInfo info = {};
143
144 info.surface_type = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
145
146 //default values
147 info.native_visual_id = 0;
148 info.native_visual_type = EGL_NONE;
149 info.caveat = EGL_NONE;
150 info.native_renderable = EGL_FALSE;
151
152 info.renderable_type = EGL_OPENGL_ES_BIT |
153 EGL_OPENGL_ES2_BIT |
154 EGL_OPENGL_ES3_BIT;
155
156 info.max_pbuffer_width = PBUFFER_MAX_WIDTH;
157 info.max_pbuffer_height = PBUFFER_MAX_HEIGHT;
158 info.max_pbuffer_size = PBUFFER_MAX_PIXELS;
159 info.samples_per_pixel = 0;
160 info.frame_buffer_level = 0;
161 info.trans_red_val = 0;
162 info.trans_green_val = 0;
163 info.trans_blue_val = 0;
164
165 info.transparent_type = EGL_NONE;
166
167 /* All configs can end up having an alpha channel even if none was requested.
168 * The default config chooser in GLSurfaceView will therefore not find any
169 * matching config. Thus, make sure alpha is zero (or at least signalled as
170 * zero to the calling EGL layer) for the configs where it was intended to
171 * be zero. */
172 info.alpha_size = getPixelFormatAttrib(index, MAC_ALPHA_SIZE);
173
174 info.depth_size = getPixelFormatAttrib(index, MAC_DEPTH_SIZE);
175 info.stencil_size = getPixelFormatAttrib(index, MAC_STENCIL_SIZE);
176 info.samples_per_pixel = getPixelFormatAttrib(index, MAC_SAMPLES_PER_PIXEL);
177
178 //TODO: ask guy if it is OK
179 GLint colorSize = 0;
180 colorSize = (GLint)getPixelFormatAttrib(index, MAC_COLOR_SIZE);
181 info.red_size = info.green_size = info.blue_size = (colorSize / 4);
182
183 info.frmt = new MacPixelFormat(
184 index, info.red_size, info.green_size, info.blue_size);
185
186 (*addConfigFunc)(addConfigOpaque, &info);
187 }
188
189
190 class MacDisplay : public EglOS::Display {
191
192 public:
MacDisplay(EGLNativeDisplayType dpy)193 explicit MacDisplay(EGLNativeDisplayType dpy) : mDpy(dpy) {}
194
getMaxGlesVersion()195 virtual EglOS::GlesVersion getMaxGlesVersion() {
196 switch (MacNativeSupportInfo::get()->maxOpenGLProfile) {
197 case MAC_OPENGL_PROFILE_LEGACY:
198 return EglOS::GlesVersion::ES2;
199 case MAC_OPENGL_PROFILE_3_2:
200 case MAC_OPENGL_PROFILE_4_1:
201 return EglOS::GlesVersion::ES30;
202 default:
203 return EglOS::GlesVersion::ES2;
204 }
205 }
206
queryConfigs(int renderableType,EglOS::AddConfigCallback * addConfigFunc,void * addConfigOpaque)207 virtual void queryConfigs(int renderableType,
208 EglOS::AddConfigCallback* addConfigFunc,
209 void* addConfigOpaque) {
210 for (int i = 0; i < MacNativeSupportInfo::get()->numNativeFormats; i++) {
211 pixelFormatToConfig(
212 i,
213 addConfigFunc,
214 addConfigOpaque);
215 }
216
217 // Also disable vsync.
218 int bestSwapInterval = 0;
219 nsSwapInterval(&bestSwapInterval);
220 }
221
isValidNativeWin(EglOS::Surface * win)222 virtual bool isValidNativeWin(EglOS::Surface* win) {
223
224 if (!win) { return false; }
225
226 if (win->type() != MacSurface::WINDOW) {
227 return false;
228 } else {
229 return isValidNativeWin(MacSurface::from(win)->handle());
230 }
231 }
232
isValidNativeWin(EGLNativeWindowType win)233 virtual bool isValidNativeWin(EGLNativeWindowType win) {
234 unsigned int width, height;
235 if (!win) return false;
236 return nsGetWinDims(win, &width, &height);
237 }
238
checkWindowPixelFormatMatch(EGLNativeWindowType win,const EglOS::PixelFormat * pixelFormat,unsigned int * width,unsigned int * height)239 virtual bool checkWindowPixelFormatMatch(
240 EGLNativeWindowType win,
241 const EglOS::PixelFormat* pixelFormat,
242 unsigned int* width,
243 unsigned int* height) {
244 bool ret = nsGetWinDims(win, width, height);
245
246 const MacPixelFormat* format = MacPixelFormat::from(pixelFormat);
247 int r = format->redSize();
248 int g = format->greenSize();
249 int b = format->blueSize();
250
251 bool match = nsCheckColor(win, r + g + b);
252
253 return ret && match;
254 }
255
createContext(EGLint profileMask,const EglOS::PixelFormat * pixelFormat,EglOS::Context * sharedContext)256 virtual std::shared_ptr<EglOS::Context> createContext(
257 EGLint profileMask,
258 const EglOS::PixelFormat* pixelFormat,
259 EglOS::Context* sharedContext) {
260
261 void* macSharedContext =
262 sharedContext ? MacContext::from(sharedContext) : NULL;
263
264 bool isCoreProfile =
265 profileMask & EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
266
267 auto key = std::make_pair(isCoreProfile,
268 MacPixelFormat::from(pixelFormat)->handle());
269
270 void* nsFormat = nullptr;
271 if (auto format = android::base::find(sFinalizedConfigs, key)) {
272 nsFormat = *format;
273 } else {
274 nsFormat =
275 finalizePixelFormat(isCoreProfile,
276 MacPixelFormat::from(pixelFormat)->handle());
277 sFinalizedConfigs[key] = nsFormat;
278 }
279
280 return std::make_shared<MacContext>(
281 isCoreProfile,
282 nsCreateContext(nsFormat,
283 macSharedContext));
284 }
285
createPbufferSurface(const EglOS::PixelFormat * pixelFormat,const EglOS::PbufferInfo * info)286 virtual EglOS::Surface* createPbufferSurface(
287 const EglOS::PixelFormat* pixelFormat,
288 const EglOS::PbufferInfo* info) {
289 GLenum glTexFormat = GL_RGBA, glTexTarget = GL_TEXTURE_2D;
290 switch (info->format) {
291 case EGL_TEXTURE_RGB:
292 glTexFormat = GL_RGB;
293 break;
294 case EGL_TEXTURE_RGBA:
295 glTexFormat = GL_RGBA;
296 break;
297 }
298 EGLint maxMipmap = info->hasMipmap ? MAX_PBUFFER_MIPMAP_LEVEL : 0;
299 bool isLegacyOpenGL =
300 MacNativeSupportInfo::get()->maxOpenGLProfile == MAC_OPENGL_PROFILE_LEGACY;
301 MacSurface* result = new MacSurface(
302 isLegacyOpenGL
303 ? nsCreatePBuffer(glTexTarget, glTexFormat, maxMipmap,
304 info->width, info->height)
305 : nullptr,
306 MacSurface::PBUFFER);
307
308 result->setHasMipmap(info->hasMipmap);
309 return result;
310 }
311
releasePbuffer(EglOS::Surface * pb)312 virtual bool releasePbuffer(EglOS::Surface* pb) {
313 if (pb) {
314 nsDestroyPBuffer(MacSurface::from(pb)->handle());
315 delete pb;
316 }
317 return true;
318 }
319
makeCurrent(EglOS::Surface * read,EglOS::Surface * draw,EglOS::Context * ctx)320 virtual bool makeCurrent(EglOS::Surface* read,
321 EglOS::Surface* draw,
322 EglOS::Context* ctx) {
323 // check for unbind
324 if (ctx == NULL && read == NULL && draw == NULL) {
325 nsWindowMakeCurrent(NULL, NULL);
326 return true;
327 }
328 else if (ctx == NULL || read == NULL || draw == NULL) {
329 // error !
330 return false;
331 }
332
333 //dont supporting diffrent read & draw surfaces on Mac
334 if (read != draw) {
335 return false;
336 }
337 switch (draw->type()) {
338 case MacSurface::WINDOW:
339 nsWindowMakeCurrent(MacContext::from(ctx),
340 MacSurface::from(draw)->handle());
341 break;
342 case MacSurface::PBUFFER:
343 {
344 MacSurface* macdraw = MacSurface::from(draw);
345 int mipmapLevel = macdraw->hasMipmap() ? MAX_PBUFFER_MIPMAP_LEVEL : 0;
346 nsPBufferMakeCurrent(MacContext::from(ctx),
347 macdraw->handle(), mipmapLevel);
348 break;
349 }
350 default:
351 return false;
352 }
353 return true;
354 }
355
swapBuffers(EglOS::Surface * srfc)356 virtual void swapBuffers(EglOS::Surface* srfc) {
357 nsSwapBuffers();
358 }
359
dpy() const360 EGLNativeDisplayType dpy() const { return mDpy; }
361
362 private:
363 EglOS::GlesVersion mMaxGlesVersion =
364 EglOS::GlesVersion::ES2;
365 EGLNativeDisplayType mDpy = {};
366 };
367
368 class MacGlLibrary : public GlLibrary {
369 public:
MacGlLibrary()370 MacGlLibrary() {
371 static const char kLibName[] =
372 "/System/Library/Frameworks/OpenGL.framework/OpenGL";
373 char error[256];
374 mLib = android::base::SharedLibrary::open(kLibName, error, sizeof(error));
375 if (!mLib) {
376 ERR("%s: Could not open GL library %s [%s]\n",
377 __FUNCTION__, kLibName, error);
378 }
379 }
380
~MacGlLibrary()381 ~MacGlLibrary() {
382 }
383
384 // override
findSymbol(const char * name)385 virtual GlFunctionPointer findSymbol(const char* name) {
386 if (!mLib) {
387 return NULL;
388 }
389 return reinterpret_cast<GlFunctionPointer>(mLib->findSymbol(name));
390 }
391
392 private:
393 android::base::SharedLibrary* mLib = nullptr;
394 };
395
396 class MacEngine : public EglOS::Engine {
397 public:
getDefaultDisplay()398 virtual EglOS::Display* getDefaultDisplay() {
399 return new MacDisplay(0);
400 }
401
getGlLibrary()402 virtual GlLibrary* getGlLibrary() {
403 return &mGlLib;
404 }
405
eglGetProcAddress(const char *)406 virtual void* eglGetProcAddress(const char*) {
407 return 0;
408 }
409
createWindowSurface(EglOS::PixelFormat * cfg,EGLNativeWindowType wnd)410 virtual EglOS::Surface* createWindowSurface(EglOS::PixelFormat* cfg,
411 EGLNativeWindowType wnd) {
412 return new MacSurface(wnd, MacSurface::WINDOW);
413 }
414
415 private:
416 MacGlLibrary mGlLib;
417 };
418
419 static MacEngine* sHostEngine = nullptr;
420
421 } // namespace
422
423
424 // static
getHostInstance()425 EglOS::Engine* EglOS::Engine::getHostInstance() {
426 if (!sHostEngine) {
427 sHostEngine = new MacEngine();
428 }
429 return sHostEngine;
430 }
431
432