Add off-screen GL context

This commit is contained in:
Pedro J. Estébanez 2021-09-24 14:07:44 +02:00
parent 25f01cb09d
commit abdf931832
25 changed files with 252 additions and 23 deletions

View file

@ -719,6 +719,12 @@ const char *OS::get_video_driver_name(int p_driver) const {
}
}
bool OS::is_offscreen_gl_available() const {
return false;
}
void OS::set_offscreen_gl_current(bool p_current) {}
int OS::get_audio_driver_count() const {
return AudioDriverManager::get_driver_count();
}

View file

@ -191,6 +191,9 @@ public:
virtual const char *get_video_driver_name(int p_driver) const;
virtual int get_current_video_driver() const = 0;
virtual bool is_offscreen_gl_available() const;
virtual void set_offscreen_gl_current(bool p_current);
virtual int get_audio_driver_count() const;
virtual const char *get_audio_driver_name(int p_driver) const;

View file

@ -407,6 +407,18 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
}
public boolean createOffscreenGL() {
return mView.createOffscreenGL();
}
public void destroyOffscreenGL() {
mView.destroyOffscreenGL();
}
public void setOffscreenGLCurrent(boolean p_current) {
mView.setOffscreenGLCurrent(p_current);
}
public void setKeepScreenOn(final boolean p_enabled) {
runOnUiThread(() -> {
if (p_enabled) {

View file

@ -72,10 +72,9 @@ public class GodotLib {
/**
* Invoked on the GL thread when the underlying Android surface is created or recreated.
* @param p_32_bits
* @see android.opengl.GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig)
*/
public static native void newcontext(boolean p_32_bits);
public static native void newcontext();
/**
* Forward {@link Activity#onBackPressed()} event from the main thread to the GL thread.

View file

@ -76,7 +76,7 @@ class GodotRenderer implements GLSurfaceView.Renderer {
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GodotLib.newcontext(GLUtils.use_32);
GodotLib.newcontext();
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onGLSurfaceCreated(gl, config);
}

View file

@ -49,6 +49,10 @@ import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
/**
* A simple GLSurfaceView sub-class that demonstrate how to perform
* OpenGL ES 2.0 rendering into a GL Surface. Note the following important
@ -75,6 +79,10 @@ public class GodotView extends GLSurfaceView {
private final GestureDetector detector;
private final GodotRenderer godotRenderer;
private EGLConfigChooser eglConfigChooser;
private EGLContextFactory eglContextFactory;
private EGLContext eglSecondaryContext;
public GodotView(Context context, Godot godot, XRMode xrMode, boolean p_use_gl3,
boolean p_use_32_bits, boolean p_use_debug_opengl, boolean p_translucent) {
super(context);
@ -123,10 +131,10 @@ public class GodotView extends GLSurfaceView {
switch (xrMode) {
case OVR:
// Replace the default egl config chooser.
setEGLConfigChooser(new OvrConfigChooser());
eglConfigChooser = new OvrConfigChooser();
// Replace the default context factory.
setEGLContextFactory(new OvrContextFactory());
eglContextFactory = new OvrContextFactory();
// Replace the default window surface factory.
setEGLWindowSurfaceFactory(new OvrWindowSurfaceFactory());
@ -147,7 +155,7 @@ public class GodotView extends GLSurfaceView {
/* Setup the context factory for 2.0 rendering.
* See ContextFactory class definition below
*/
setEGLContextFactory(new RegularContextFactory());
eglContextFactory = new RegularContextFactory();
/* We need to choose an EGLConfig that matches the format of
* our surface exactly. This is going to be done in our
@ -156,24 +164,49 @@ public class GodotView extends GLSurfaceView {
*/
if (GLUtils.use_32) {
setEGLConfigChooser(translucent
? new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
new RegularConfigChooser(8, 8, 8, 8, 16, stencil))
: new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
new RegularConfigChooser(5, 6, 5, 0, 16, stencil)));
eglConfigChooser = translucent
? new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
new RegularConfigChooser(8, 8, 8, 8, 16, stencil))
: new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
new RegularConfigChooser(5, 6, 5, 0, 16, stencil));
} else {
setEGLConfigChooser(translucent
? new RegularConfigChooser(8, 8, 8, 8, 16, stencil)
: new RegularConfigChooser(5, 6, 5, 0, 16, stencil));
eglConfigChooser = translucent
? new RegularConfigChooser(8, 8, 8, 8, 16, stencil)
: new RegularConfigChooser(5, 6, 5, 0, 16, stencil);
}
break;
}
setEGLConfigChooser(eglConfigChooser);
setEGLContextFactory(eglContextFactory);
/* Set the renderer responsible for frame rendering */
setRenderer(godotRenderer);
}
public boolean createOffscreenGL() {
EGL10 egl = (EGL10)EGLContext.getEGL();
EGLConfig eglConfig = eglConfigChooser.chooseConfig(egl, egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY));
eglSecondaryContext = eglContextFactory.createContext(egl, egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY), eglConfig);
if (eglSecondaryContext == EGL10.EGL_NO_CONTEXT) {
eglSecondaryContext = null;
}
return eglSecondaryContext != null;
}
public void setOffscreenGLCurrent(boolean p_current) {
EGL10 egl = (EGL10)EGLContext.getEGL();
egl.eglMakeCurrent(egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY), EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, p_current ? eglSecondaryContext : EGL10.EGL_NO_CONTEXT);
}
public void destroyOffscreenGL() {
if (eglSecondaryContext != null) {
EGL10 egl = (EGL10)EGLContext.getEGL();
eglContextFactory.destroyContext(egl, egl.eglGetCurrentDisplay(), eglSecondaryContext);
eglSecondaryContext = null;
}
}
public void onBackPressed() {
godot.onBackPressed();
}

View file

@ -152,6 +152,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env
delete godot_io_java;
}
if (godot_java) {
godot_java->destroy_offscreen_gl(env);
delete godot_java;
}
if (input_handler) {
@ -212,11 +213,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, j
os_android->set_display_size(Size2(width, height));
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jboolean p_32_bits) {
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz) {
if (os_android) {
if (step.get() == 0) {
// During startup
os_android->set_context_is_16_bits(!p_32_bits);
os_android->set_offscreen_gl_available(godot_java->create_offscreen_gl(env));
} else {
// GL context recreated because it was lost; restart app to let it reload everything
step.set(-1); // Ensure no further steps are attempted and no further events are sent

View file

@ -41,7 +41,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jint width, jint height);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jboolean p_32_bits);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz);
void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask = 0, jfloat vertical_factor = 0, jfloat horizontal_factor = 0);

View file

@ -59,6 +59,9 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
// get some Godot method pointers...
_on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()V");
_create_offscreen_gl = p_env->GetMethodID(godot_class, "createOffscreenGL", "()Z");
_destroy_offscreen_gl = p_env->GetMethodID(godot_class, "destroyOffscreenGL", "()V");
_set_offscreen_gl_current = p_env->GetMethodID(godot_class, "setOffscreenGLCurrent", "(Z)V");
_restart = p_env->GetMethodID(godot_class, "restart", "()V");
_finish = p_env->GetMethodID(godot_class, "forceQuit", "()V");
_set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V");
@ -131,6 +134,29 @@ void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
}
}
bool GodotJavaWrapper::create_offscreen_gl(JNIEnv *p_env) {
if (_create_offscreen_gl) {
return p_env->CallBooleanMethod(godot_instance, _create_offscreen_gl);
} else {
return false;
}
}
void GodotJavaWrapper::destroy_offscreen_gl(JNIEnv *p_env) {
if (_destroy_offscreen_gl) {
p_env->CallBooleanMethod(godot_instance, _destroy_offscreen_gl);
}
}
void GodotJavaWrapper::set_offscreen_gl_current(JNIEnv *p_env, bool p_current) {
if (_set_offscreen_gl_current) {
if (p_env == NULL)
p_env = get_jni_env();
ERR_FAIL_COND(p_env == nullptr);
p_env->CallVoidMethod(godot_instance, _set_offscreen_gl_current, p_current);
}
}
void GodotJavaWrapper::on_godot_setup_completed(JNIEnv *p_env) {
if (_on_godot_setup_completed) {
if (p_env == NULL) {

View file

@ -48,6 +48,9 @@ private:
jclass activity_class;
jmethodID _on_video_init = 0;
jmethodID _create_offscreen_gl = 0;
jmethodID _destroy_offscreen_gl = 0;
jmethodID _set_offscreen_gl_current = 0;
jmethodID _restart = 0;
jmethodID _finish = 0;
jmethodID _set_keep_screen_on = 0;
@ -77,6 +80,9 @@ public:
jobject get_class_loader();
void gfx_init(bool gl2);
bool create_offscreen_gl(JNIEnv *p_env);
void destroy_offscreen_gl(JNIEnv *p_env);
void set_offscreen_gl_current(JNIEnv *p_env, bool p_current);
void on_video_init(JNIEnv *p_env = NULL);
void on_godot_setup_completed(JNIEnv *p_env = NULL);
void on_godot_main_loop_started(JNIEnv *p_env = NULL);

View file

@ -484,10 +484,18 @@ String OS_Android::get_system_dir(SystemDir p_dir, bool p_shared_storage) const
return godot_io_java->get_system_dir(p_dir, p_shared_storage);
}
void OS_Android::set_context_is_16_bits(bool p_is_16) {
//use_16bits_fbo = p_is_16;
//if (rasterizer)
// rasterizer->set_force_16_bits_fbo(p_is_16);
void OS_Android::set_offscreen_gl_available(bool p_available) {
secondary_gl_available = p_available;
}
bool OS_Android::is_offscreen_gl_available() const {
return secondary_gl_available;
}
void OS_Android::set_offscreen_gl_current(bool p_current) {
if (secondary_gl_available) {
godot_java->set_offscreen_gl_current(nullptr, p_current);
}
}
bool OS_Android::is_joy_known(int p_device) {

View file

@ -45,7 +45,7 @@ class OS_Android : public OS_Unix {
bool use_gl2;
bool use_apk_expansion;
bool use_16bits_fbo;
bool secondary_gl_available = false;
VisualServer *visual_server;
@ -137,7 +137,9 @@ public:
void set_opengl_extensions(const char *p_gl_extensions);
void set_display_size(Size2 p_size);
void set_context_is_16_bits(bool p_is_16);
void set_offscreen_gl_available(bool p_available);
virtual bool is_offscreen_gl_available() const;
virtual void set_offscreen_gl_current(bool p_current);
virtual void set_screen_orientation(ScreenOrientation p_orientation);
virtual ScreenOrientation get_screen_orientation() const;

View file

@ -53,6 +53,7 @@ bool gles3_available = true;
GLint backingHeight;
EAGLContext *context;
EAGLContext *context_offscreen;
GLuint viewRenderbuffer, viewFramebuffer;
GLuint depthRenderbuffer;
}
@ -75,6 +76,9 @@ bool gles3_available = true;
gles3_available = false;
fallback_gl2 = true;
NSLog(@"Failed to create OpenGL ES 3.0 context. Falling back to OpenGL ES 2.0");
} else {
context_offscreen = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
OSIPhone::get_singleton()->set_offscreen_gl_context(context_offscreen);
}
}
@ -130,6 +134,9 @@ bool gles3_available = true;
if (context) {
context = nil;
}
if (context_offscreen) {
context_offscreen = nil;
}
}
- (BOOL)createFramebuffer {

View file

@ -61,6 +61,8 @@ private:
VideoMode video_mode;
EAGLContext *offscreen_gl_context;
virtual int get_video_driver_count() const;
virtual const char *get_video_driver_name(int p_driver) const;
@ -162,6 +164,10 @@ public:
virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const;
void set_offscreen_gl_context(EAGLContext *p_context);
virtual bool is_offscreen_gl_available() const;
virtual void set_offscreen_gl_current(bool p_current);
virtual void set_keep_screen_on(bool p_enabled);
virtual bool can_draw() const;

View file

@ -417,6 +417,22 @@ void OSIPhone::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) c
p_list->push_back(video_mode);
}
void OSIPhone::set_offscreen_gl_context(EAGLContext *p_context) {
offscreen_gl_context = p_context;
}
bool OSIPhone::is_offscreen_gl_available() const {
return offscreen_gl_context;
}
void OSIPhone::set_offscreen_gl_current(bool p_current) {
if (p_current) {
[EAGLContext setCurrentContext:offscreen_gl_context];
} else {
[EAGLContext setCurrentContext:nil];
}
}
bool OSIPhone::can_draw() const {
if (native_video_is_playing())
return false;
@ -683,6 +699,7 @@ OSIPhone::OSIPhone(String p_data_dir, String p_cache_dir) {
main_loop = NULL;
visual_server = NULL;
offscreen_gl_context = NULL;
// can't call set_data_dir from here, since it requires DirAccess
// which is initialized in initialize_core

View file

@ -115,6 +115,7 @@ public:
id cursor;
NSOpenGLPixelFormat *pixelFormat;
NSOpenGLContext *context;
NSOpenGLContext *context_offscreen;
Vector<Vector2> mpath;
bool layered_window;
@ -250,6 +251,9 @@ public:
virtual VideoMode get_video_mode(int p_screen = 0) const;
virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const;
virtual bool is_offscreen_gl_available() const;
virtual void set_offscreen_gl_current(bool p_current);
virtual String get_executable_path() const;
virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr);

View file

@ -1666,6 +1666,8 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
[window_view setOpenGLContext:context];
context_offscreen = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
[context makeCurrentContext];
GLint dim[2];
@ -2427,6 +2429,18 @@ OS::VideoMode OS_OSX::get_video_mode(int p_screen) const {
void OS_OSX::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
}
bool OS_OSX::is_offscreen_gl_available() const {
return context_offscreen != nil;
}
void OS_OSX::set_offscreen_gl_current(bool p_current) {
if (p_current) {
[context makeCurrentContext];
} else {
[NSOpenGLContext clearCurrentContext];
}
}
int OS_OSX::get_screen_count() const {
NSArray *screenArray = [NSScreen screens];
return [screenArray count];
@ -3426,6 +3440,7 @@ OS_OSX *OS_OSX::singleton = NULL;
OS_OSX::OS_OSX() {
context = nullptr;
context_offscreen = nullptr;
memset(cursors, 0, sizeof(cursors));
key_event_pos = 0;

View file

@ -58,6 +58,18 @@ void ContextGL_Windows::make_current() {
wglMakeCurrent(hDC, hRC);
}
bool ContextGL_Windows::is_offscreen_available() const {
return hRC_offscreen != NULL;
}
void ContextGL_Windows::make_offscreen_current() {
ERR_FAIL_COND(!wglMakeCurrent(hDC, hRC_offscreen));
}
void ContextGL_Windows::release_offscreen_current() {
ERR_FAIL_COND(!wglMakeCurrent(hDC, NULL));
}
HDC ContextGL_Windows::get_hdc() {
return hDC;
}
@ -205,6 +217,8 @@ Error ContextGL_Windows::initialize() {
{
return ERR_CANT_CREATE; // Return FALSE
}
hRC_offscreen = wglCreateContextAttribsARB(hDC, 0, attribs);
}
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
@ -217,6 +231,7 @@ Error ContextGL_Windows::initialize() {
ContextGL_Windows::ContextGL_Windows(HWND hwnd, bool p_opengl_3_context) {
opengl_3_context = p_opengl_3_context;
hWnd = hwnd;
hRC_offscreen = NULL;
use_vsync = false;
vsync_via_compositor = false;
}

View file

@ -47,6 +47,7 @@ typedef int(APIENTRY *PFNWGLGETSWAPINTERVALEXTPROC)(void);
class ContextGL_Windows {
HDC hDC;
HGLRC hRC;
HGLRC hRC_offscreen;
unsigned int pixel_format;
HWND hWnd;
bool opengl_3_context;
@ -63,6 +64,10 @@ public:
void make_current();
bool is_offscreen_available() const;
void make_offscreen_current();
void release_offscreen_current();
HDC get_hdc();
HGLRC get_hglrc();

View file

@ -1672,6 +1672,24 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
return OK;
}
bool OS_Windows::is_offscreen_gl_available() const {
#if defined(OPENGL_ENABLED)
return gl_context->is_offscreen_available();
#else
return false;
#endif
}
void OS_Windows::set_offscreen_gl_current(bool p_current) {
#if defined(OPENGL_ENABLED)
if (p_current) {
return gl_context->make_offscreen_current();
} else {
return gl_context->release_offscreen_current();
}
#endif
}
void OS_Windows::set_clipboard(const String &p_text) {
// Convert LF line endings to CRLF in clipboard content
// Otherwise, line endings won't be visible when pasted in other software

View file

@ -386,6 +386,9 @@ protected:
virtual void initialize_core();
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
virtual bool is_offscreen_gl_available() const;
virtual void set_offscreen_gl_current(bool p_current);
virtual void set_main_loop(MainLoop *p_main_loop);
virtual void delete_main_loop();

View file

@ -47,6 +47,7 @@ typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLX
struct ContextGL_X11_Private {
::GLXContext glx_context;
::GLXContext glx_context_offscreen;
};
void ContextGL_X11::release_current() {
@ -57,6 +58,18 @@ void ContextGL_X11::make_current() {
glXMakeCurrent(x11_display, x11_window, p->glx_context);
}
bool ContextGL_X11::is_offscreen_available() const {
return p->glx_context_offscreen;
}
void ContextGL_X11::make_offscreen_current() {
glXMakeCurrent(x11_display, x11_window, p->glx_context_offscreen);
}
void ContextGL_X11::release_offscreen_current() {
glXMakeCurrent(x11_display, None, NULL);
}
void ContextGL_X11::swap_buffers() {
glXSwapBuffers(x11_display, x11_window);
}
@ -181,6 +194,7 @@ Error ContextGL_X11::initialize() {
p->glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, nullptr, true, context_attribs);
ERR_FAIL_COND_V(ctxErrorOccurred || !p->glx_context, ERR_UNCONFIGURED);
p->glx_context_offscreen = glXCreateContextAttribsARB(x11_display, fbconfig, nullptr, true, context_attribs);
} break;
}
@ -275,12 +289,16 @@ ContextGL_X11::ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, c
glx_minor = glx_major = 0;
p = memnew(ContextGL_X11_Private);
p->glx_context = nullptr;
p->glx_context_offscreen = nullptr;
use_vsync = false;
}
ContextGL_X11::~ContextGL_X11() {
release_current();
glXDestroyContext(x11_display, p->glx_context);
if (p->glx_context_offscreen) {
glXDestroyContext(x11_display, p->glx_context_offscreen);
}
memdelete(p);
}

View file

@ -69,6 +69,10 @@ public:
int get_window_height();
void *get_glx_context();
bool is_offscreen_available() const;
void make_offscreen_current();
void release_offscreen_current();
Error initialize();
void set_use_vsync(bool p_use);

View file

@ -869,6 +869,24 @@ void OS_X11::finalize() {
args.clear();
}
bool OS_X11::is_offscreen_gl_available() const {
#if defined(OPENGL_ENABLED)
return context_gl->is_offscreen_available();
#else
return false;
#endif
}
void OS_X11::set_offscreen_gl_current(bool p_current) {
#if defined(OPENGL_ENABLED)
if (p_current) {
return context_gl->make_offscreen_current();
} else {
return context_gl->release_offscreen_current();
}
#endif
}
void OS_X11::set_mouse_mode(MouseMode p_mode) {
if (p_mode == mouse_mode) {
return;

View file

@ -239,6 +239,9 @@ protected:
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
virtual void finalize();
virtual bool is_offscreen_gl_available() const;
virtual void set_offscreen_gl_current(bool p_current);
virtual void set_main_loop(MainLoop *p_main_loop);
void _window_changed(XEvent *event);