From abdf931832c40638f268fbb84418466e9542871b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Fri, 24 Sep 2021 14:07:44 +0200 Subject: [PATCH] Add off-screen GL context --- core/os/os.cpp | 6 ++ core/os/os.h | 3 + .../lib/src/org/godotengine/godot/Godot.java | 12 ++++ .../src/org/godotengine/godot/GodotLib.java | 3 +- .../org/godotengine/godot/GodotRenderer.java | 2 +- .../src/org/godotengine/godot/GodotView.java | 55 +++++++++++++++---- platform/android/java_godot_lib_jni.cpp | 5 +- platform/android/java_godot_lib_jni.h | 2 +- platform/android/java_godot_wrapper.cpp | 26 +++++++++ platform/android/java_godot_wrapper.h | 6 ++ platform/android/os_android.cpp | 16 ++++-- platform/android/os_android.h | 6 +- platform/iphone/display_layer.mm | 7 +++ platform/iphone/os_iphone.h | 6 ++ platform/iphone/os_iphone.mm | 17 ++++++ platform/osx/os_osx.h | 4 ++ platform/osx/os_osx.mm | 15 +++++ platform/windows/context_gl_windows.cpp | 15 +++++ platform/windows/context_gl_windows.h | 5 ++ platform/windows/os_windows.cpp | 18 ++++++ platform/windows/os_windows.h | 3 + platform/x11/context_gl_x11.cpp | 18 ++++++ platform/x11/context_gl_x11.h | 4 ++ platform/x11/os_x11.cpp | 18 ++++++ platform/x11/os_x11.h | 3 + 25 files changed, 252 insertions(+), 23 deletions(-) diff --git a/core/os/os.cpp b/core/os/os.cpp index 4cad5143642..b71a1d897cc 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -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(); } diff --git a/core/os/os.h b/core/os/os.h index 06bc9aeec9f..5be9b1d9d12 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -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; diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 1bba8f7c910..e89e2cdea41 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -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) { diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index d9dc9058bbb..15b3afc811c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -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. diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java index 51c4bf435f0..2dc602c75fe 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java @@ -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); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotView.java index 9598ab02b7a..211b18e7739 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotView.java @@ -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(); } diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index f8d77894cc2..ebce1a39bf1 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -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 diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index e57134435ff..305512766bb 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -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); diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 62f1eca0624..0f3f3da5395 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -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) { diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 8bbdffdd831..8702d098d42 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -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); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 8088c2e850b..c5bb719dc82 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -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) { diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 6fd20c4b23c..d50be66bd46 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -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; diff --git a/platform/iphone/display_layer.mm b/platform/iphone/display_layer.mm index d99dcddf539..23af5c121c8 100644 --- a/platform/iphone/display_layer.mm +++ b/platform/iphone/display_layer.mm @@ -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 { diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index a7a70c33528..4f23d7c0138 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -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 *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; diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm index c115bc09976..9e53d032b91 100644 --- a/platform/iphone/os_iphone.mm +++ b/platform/iphone/os_iphone.mm @@ -417,6 +417,22 @@ void OSIPhone::get_fullscreen_mode_list(List *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 diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 51f24af2584..845788759d4 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -115,6 +115,7 @@ public: id cursor; NSOpenGLPixelFormat *pixelFormat; NSOpenGLContext *context; + NSOpenGLContext *context_offscreen; Vector 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 *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 &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); diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 27baebee8b0..66148b22396 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -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 *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; diff --git a/platform/windows/context_gl_windows.cpp b/platform/windows/context_gl_windows.cpp index 32200119da1..e390224e82d 100644 --- a/platform/windows/context_gl_windows.cpp +++ b/platform/windows/context_gl_windows.cpp @@ -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; } diff --git a/platform/windows/context_gl_windows.h b/platform/windows/context_gl_windows.h index 7253d517cb4..2dcd40ed71f 100644 --- a/platform/windows/context_gl_windows.h +++ b/platform/windows/context_gl_windows.h @@ -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(); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index a6e1bc8c6f3..29157ee6157 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -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 diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index c623523d72a..a3029b4996a 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -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(); diff --git a/platform/x11/context_gl_x11.cpp b/platform/x11/context_gl_x11.cpp index a6cca238c2c..aff4f5b8cfa 100644 --- a/platform/x11/context_gl_x11.cpp +++ b/platform/x11/context_gl_x11.cpp @@ -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); } diff --git a/platform/x11/context_gl_x11.h b/platform/x11/context_gl_x11.h index cc9e2dd81cd..90fff4180fb 100644 --- a/platform/x11/context_gl_x11.h +++ b/platform/x11/context_gl_x11.h @@ -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); diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index ccdfee05536..0b3c757bc5d 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -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; diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index 985d716cf0a..d3d5b5d9296 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -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);