#include "ContextGL.hpp" #include "HeadersGL.hpp" #include "Debug.hpp" #include #include LOGGROUP(Graphics) // GL extensions static GLvoid(GL_APIENTRY* glBindVertexArrayPtr)(GLuint array); static GLvoid(GL_APIENTRY* glDeleteVertexArraysPtr)(GLsizei n, const GLuint* arrays); static GLvoid(GL_APIENTRY* glGenVertexArraysPtr)(GLsizei n, const GLuint* arrays); static GLvoid*(GL_APIENTRY* glMapBufferPtr)(GLenum target, GLenum access); static GLboolean(GL_APIENTRY* glUnmapBufferPtr)(GLenum target); static GLvoid*(GL_APIENTRY* glMapBufferRangePtr)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); static GLvoid(GL_APIENTRY* glTexImage3DPtr)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels); static GLvoid(GL_APIENTRY* glTexSubImage3DPtr)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels); static GLvoid(GL_APIENTRY* glTexStorage2DPtr)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); static GLvoid(GL_APIENTRY* glTexStorage3DPtr)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); static GLsync(GL_APIENTRY* glFenceSyncPtr)(GLenum condition, GLbitfield flags); static void(GL_APIENTRY* glDeleteSyncPtr)(GLsync sync); static GLenum(GL_APIENTRY* glClientWaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout); static void(GL_APIENTRY* glWaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout); static GLvoid(GL_APIENTRY* glBlitFramebufferPtr)( GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); static GLvoid(GL_APIENTRY* glRenderbufferStorageMultisamplePtr)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); static GLvoid(GL_APIENTRY* glInvalidateFramebufferPtr)(GLenum target, GLsizei numAttachments, const GLenum* attachments); template static void loadExtensionGL(T& ptr, const char* namecore, const char* nameext) { if (!ptr) ptr = reinterpret_cast(eglGetProcAddress(namecore)); if (!ptr) ptr = reinterpret_cast(eglGetProcAddress(nameext)); } #define LOAD_EXTENSION_GL(name, suffix) loadExtensionGL(name##Ptr, #name, #name #suffix) // GL stubs GLvoid glBindVertexArray(GLuint array) { glBindVertexArrayPtr(array); } GLvoid glDeleteVertexArrays(GLsizei n, const GLuint* arrays) { glDeleteVertexArraysPtr(n, arrays); } GLvoid glGenVertexArrays(GLsizei n, GLuint* arrays) { glGenVertexArraysPtr(n, arrays); } GLvoid* glMapBuffer(GLenum target, GLenum access) { return glMapBufferPtr(target, access); } GLboolean glUnmapBuffer(GLenum target) { return glUnmapBufferPtr(target); } GLvoid* glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) { return glMapBufferRangePtr(target, offset, length, access); } GLvoid glTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) { glTexStorage2DPtr(target, levels, internalformat, width, height); } GLvoid glTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels) { glTexImage3DPtr(target, level, internalFormat, width, height, depth, border, format, type, pixels); } GLvoid glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels) { glTexSubImage3DPtr(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); } GLvoid glTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { glTexStorage3DPtr(target, levels, internalformat, width, height, depth); } GLsync glFenceSync(GLenum condition, GLbitfield flags) { return glFenceSyncPtr(condition, flags); } void glDeleteSync(GLsync sync) { glDeleteSyncPtr(sync); } GLenum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) { return glClientWaitSyncPtr(sync, flags, timeout); } void glWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) { glWaitSyncPtr(sync, flags, timeout); } GLvoid glBlitFramebuffer( GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { glBlitFramebufferPtr(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } GLvoid glRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { glRenderbufferStorageMultisamplePtr(target, samples, internalformat, width, height); } GLvoid glInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments) { glInvalidateFramebufferPtr(target, numAttachments, attachments); } namespace Aya { namespace Graphics { class ContextGLAndroid : public ContextGL { public: ContextGLAndroid(void* windowHandle) { aNativeWindow = static_cast(windowHandle); ANativeWindow_acquire(aNativeWindow); FASTLOG2(FLog::Graphics, "Window size: %dx%d", ANativeWindow_getWidth(aNativeWindow), ANativeWindow_getHeight(aNativeWindow)); display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (display == EGL_NO_DISPLAY) throw runtime_error("Error creating context: eglGetDisplay %x", eglGetError()); if (!eglInitialize(display, 0, 0)) throw runtime_error("Error creating context: eglInitialize %x", eglGetError()); EGLConfig config; if (!tryChooseConfig(display, &config, 8, 8, 8, 24, 0) && !tryChooseConfig(display, &config, 8, 8, 8, 16, 0) && !tryChooseConfig(display, &config, 5, 6, 5, 16, 0) && !tryChooseConfig(display, &config, 8, 8, 8, 24, 1) && !tryChooseConfig(display, &config, 8, 8, 8, 16, 1) && !tryChooseConfig(display, &config, 5, 6, 5, 16, 1)) throw runtime_error("Error creating context: could not find suitable config (%x)", eglGetError()); surface = eglCreateWindowSurface(display, config, aNativeWindow, 0); if (!surface) { throw runtime_error("Error creating context: eglCreateWindowSurface %x", eglGetError()); } static const EGLint contextAttrs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; context = eglCreateContext(display, config, NULL, contextAttrs); if (!context) throw runtime_error("Error creating context: eglCreateContext %x", eglGetError()); if (!eglMakeCurrent(display, surface, surface, context)) throw runtime_error("Error creating context: eglMakeCurrent %x", eglGetError()); eglQuerySurface(display, surface, EGL_WIDTH, &surfaceWidth); eglQuerySurface(display, surface, EGL_HEIGHT, &surfaceHeight); FASTLOG4(FLog::Graphics, "Initialized EGL context %p (surface %p) with renderbuffer %dx%d", context, surface, surfaceWidth, surfaceHeight); EGLint minSwapInterval = -1; eglGetConfigAttrib(display, config, EGL_MIN_SWAP_INTERVAL, &minSwapInterval); FASTLOG1(FLog::Graphics, "EGL_MIN_SWAP_INTERVAL: %d", minSwapInterval); if (eglSwapInterval(display, 0) == EGL_FALSE) { // This should be impossible as the interval is silently clamped to EGL_MIN_SWAP_INTERVAL. FASTLOG(FLog::Graphics, "*** eglSwapInterval EGL_FALSE"); } LOAD_EXTENSION_GL(glBindVertexArray, OES); LOAD_EXTENSION_GL(glDeleteVertexArrays, OES); LOAD_EXTENSION_GL(glGenVertexArrays, OES); LOAD_EXTENSION_GL(glMapBuffer, OES); LOAD_EXTENSION_GL(glUnmapBuffer, OES); LOAD_EXTENSION_GL(glMapBufferRange, EXT); LOAD_EXTENSION_GL(glTexImage3D, OES); LOAD_EXTENSION_GL(glTexSubImage3D, OES); LOAD_EXTENSION_GL(glTexStorage2D, EXT); LOAD_EXTENSION_GL(glTexStorage3D, EXT); LOAD_EXTENSION_GL(glFenceSync, EXT); LOAD_EXTENSION_GL(glDeleteSync, EXT); LOAD_EXTENSION_GL(glClientWaitSync, EXT); LOAD_EXTENSION_GL(glWaitSync, EXT); LOAD_EXTENSION_GL(glBlitFramebuffer, EXT); LOAD_EXTENSION_GL(glRenderbufferStorageMultisample, EXT); LOAD_EXTENSION_GL(glInvalidateFramebuffer, EXT); } ~ContextGLAndroid() { eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(display, context); eglDestroySurface(display, surface); eglTerminate(display); ANativeWindow_release(aNativeWindow); } virtual void setCurrent() { bool result = eglMakeCurrent(display, surface, surface, context); AYAASSERT(result); } virtual void swapBuffers() { bool result = eglSwapBuffers(display, surface); AYAASSERT(result); } virtual unsigned int getMainFramebufferId() { return 0; } virtual bool isMainFramebufferRetina() { return false; } virtual void resizeWindow(int w, int h) { return; } virtual std::pair updateMainFramebuffer(unsigned int width, unsigned int height) { return std::make_pair(surfaceWidth, surfaceHeight); } private: ANativeWindow* aNativeWindow; EGLDisplay display; EGLSurface surface; EGLContext context; EGLint surfaceWidth; EGLint surfaceHeight; static bool tryChooseConfig(EGLDisplay display, EGLConfig* config, int redBits, int greenBits, int blueBits, int depthBits, int swapInterval) { const EGLint attribs[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_MIN_SWAP_INTERVAL, swapInterval, EGL_RED_SIZE, redBits, EGL_GREEN_SIZE, greenBits, EGL_BLUE_SIZE, blueBits, EGL_DEPTH_SIZE, depthBits, EGL_NONE}; FASTLOG4(FLog::Graphics, "Trying to choose EGL config r%d g%d b%d d%d", redBits, greenBits, blueBits, depthBits); EGLint numConfigs = 0; return eglChooseConfig(display, attribs, config, 1, &numConfigs) && numConfigs > 0; } }; ContextGL* ContextGL::create(void* windowHandle, void* display, int w, int h) { return new ContextGLAndroid(windowHandle); } } // namespace Graphics } // namespace Aya