Files
aya/engine/gfx/src/API/GL/ContextGLAndroid.cpp
2025-12-17 16:47:48 +00:00

287 lines
10 KiB
C++

#include "ContextGL.hpp"
#include "HeadersGL.hpp"
#include "Debug.hpp"
#include <android/native_window.h>
#include <EGL/egl.h>
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<typename T>
static void loadExtensionGL(T& ptr, const char* namecore, const char* nameext)
{
if (!ptr)
ptr = reinterpret_cast<T>(eglGetProcAddress(namecore));
if (!ptr)
ptr = reinterpret_cast<T>(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<ANativeWindow*>(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<unsigned int, unsigned int> 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