Browse Source

Add off-screen GL context

Pedro J. Estébanez 4 years ago
parent
commit
abdf931832

+ 6 - 0
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 {
 int OS::get_audio_driver_count() const {
 	return AudioDriverManager::get_driver_count();
 	return AudioDriverManager::get_driver_count();
 }
 }

+ 3 - 0
core/os/os.h

@@ -191,6 +191,9 @@ public:
 	virtual const char *get_video_driver_name(int p_driver) const;
 	virtual const char *get_video_driver_name(int p_driver) const;
 	virtual int get_current_video_driver() const = 0;
 	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 int get_audio_driver_count() const;
 	virtual const char *get_audio_driver_name(int p_driver) const;
 	virtual const char *get_audio_driver_name(int p_driver) const;
 
 

+ 12 - 0
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) {
 	public void setKeepScreenOn(final boolean p_enabled) {
 		runOnUiThread(() -> {
 		runOnUiThread(() -> {
 			if (p_enabled) {
 			if (p_enabled) {

+ 1 - 2
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.
 	 * 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)
 	 * @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.
 	 * Forward {@link Activity#onBackPressed()} event from the main thread to the GL thread.

+ 1 - 1
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) {
 	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
-		GodotLib.newcontext(GLUtils.use_32);
+		GodotLib.newcontext();
 		for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
 		for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
 			plugin.onGLSurfaceCreated(gl, config);
 			plugin.onGLSurfaceCreated(gl, config);
 		}
 		}

+ 44 - 11
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.KeyEvent;
 import android.view.MotionEvent;
 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
  * A simple GLSurfaceView sub-class that demonstrate how to perform
  * OpenGL ES 2.0 rendering into a GL Surface. Note the following important
  * 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 GestureDetector detector;
 	private final GodotRenderer godotRenderer;
 	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,
 	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) {
 			boolean p_use_32_bits, boolean p_use_debug_opengl, boolean p_translucent) {
 		super(context);
 		super(context);
@@ -123,10 +131,10 @@ public class GodotView extends GLSurfaceView {
 		switch (xrMode) {
 		switch (xrMode) {
 			case OVR:
 			case OVR:
 				// Replace the default egl config chooser.
 				// Replace the default egl config chooser.
-				setEGLConfigChooser(new OvrConfigChooser());
+				eglConfigChooser = new OvrConfigChooser();
 
 
 				// Replace the default context factory.
 				// Replace the default context factory.
-				setEGLContextFactory(new OvrContextFactory());
+				eglContextFactory = new OvrContextFactory();
 
 
 				// Replace the default window surface factory.
 				// Replace the default window surface factory.
 				setEGLWindowSurfaceFactory(new OvrWindowSurfaceFactory());
 				setEGLWindowSurfaceFactory(new OvrWindowSurfaceFactory());
@@ -147,7 +155,7 @@ public class GodotView extends GLSurfaceView {
 				/* Setup the context factory for 2.0 rendering.
 				/* Setup the context factory for 2.0 rendering.
 				 * See ContextFactory class definition below
 				 * See ContextFactory class definition below
 				 */
 				 */
-				setEGLContextFactory(new RegularContextFactory());
+				eglContextFactory = new RegularContextFactory();
 
 
 				/* We need to choose an EGLConfig that matches the format of
 				/* We need to choose an EGLConfig that matches the format of
 				 * our surface exactly. This is going to be done in our
 				 * our surface exactly. This is going to be done in our
@@ -156,24 +164,49 @@ public class GodotView extends GLSurfaceView {
 				 */
 				 */
 
 
 				if (GLUtils.use_32) {
 				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 {
 				} 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;
 				break;
 		}
 		}
+		setEGLConfigChooser(eglConfigChooser);
+		setEGLContextFactory(eglContextFactory);
 
 
 		/* Set the renderer responsible for frame rendering */
 		/* Set the renderer responsible for frame rendering */
 		setRenderer(godotRenderer);
 		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() {
 	public void onBackPressed() {
 		godot.onBackPressed();
 		godot.onBackPressed();
 	}
 	}

+ 3 - 2
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;
 		delete godot_io_java;
 	}
 	}
 	if (godot_java) {
 	if (godot_java) {
+		godot_java->destroy_offscreen_gl(env);
 		delete godot_java;
 		delete godot_java;
 	}
 	}
 	if (input_handler) {
 	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));
 		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 (os_android) {
 		if (step.get() == 0) {
 		if (step.get() == 0) {
 			// During startup
 			// 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 {
 		} else {
 			// GL context recreated because it was lost; restart app to let it reload everything
 			// 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
 			step.set(-1); // Ensure no further steps are attempted and no further events are sent

+ 1 - 1
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_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_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_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_step(JNIEnv *env, jclass clazz);
 JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(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);
 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);

+ 26 - 0
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...
 	// get some Godot method pointers...
 	_on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()V");
 	_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");
 	_restart = p_env->GetMethodID(godot_class, "restart", "()V");
 	_finish = p_env->GetMethodID(godot_class, "forceQuit", "()V");
 	_finish = p_env->GetMethodID(godot_class, "forceQuit", "()V");
 	_set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)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) {
 void GodotJavaWrapper::on_godot_setup_completed(JNIEnv *p_env) {
 	if (_on_godot_setup_completed) {
 	if (_on_godot_setup_completed) {
 		if (p_env == NULL) {
 		if (p_env == NULL) {

+ 6 - 0
platform/android/java_godot_wrapper.h

@@ -48,6 +48,9 @@ private:
 	jclass activity_class;
 	jclass activity_class;
 
 
 	jmethodID _on_video_init = 0;
 	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 _restart = 0;
 	jmethodID _finish = 0;
 	jmethodID _finish = 0;
 	jmethodID _set_keep_screen_on = 0;
 	jmethodID _set_keep_screen_on = 0;
@@ -77,6 +80,9 @@ public:
 	jobject get_class_loader();
 	jobject get_class_loader();
 
 
 	void gfx_init(bool gl2);
 	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_video_init(JNIEnv *p_env = NULL);
 	void on_godot_setup_completed(JNIEnv *p_env = NULL);
 	void on_godot_setup_completed(JNIEnv *p_env = NULL);
 	void on_godot_main_loop_started(JNIEnv *p_env = NULL);
 	void on_godot_main_loop_started(JNIEnv *p_env = NULL);

+ 12 - 4
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);
 	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) {
 bool OS_Android::is_joy_known(int p_device) {

+ 4 - 2
platform/android/os_android.h

@@ -45,7 +45,7 @@ class OS_Android : public OS_Unix {
 	bool use_gl2;
 	bool use_gl2;
 	bool use_apk_expansion;
 	bool use_apk_expansion;
 
 
-	bool use_16bits_fbo;
+	bool secondary_gl_available = false;
 
 
 	VisualServer *visual_server;
 	VisualServer *visual_server;
 
 
@@ -137,7 +137,9 @@ public:
 	void set_opengl_extensions(const char *p_gl_extensions);
 	void set_opengl_extensions(const char *p_gl_extensions);
 	void set_display_size(Size2 p_size);
 	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 void set_screen_orientation(ScreenOrientation p_orientation);
 	virtual ScreenOrientation get_screen_orientation() const;
 	virtual ScreenOrientation get_screen_orientation() const;

+ 7 - 0
platform/iphone/display_layer.mm

@@ -53,6 +53,7 @@ bool gles3_available = true;
 	GLint backingHeight;
 	GLint backingHeight;
 
 
 	EAGLContext *context;
 	EAGLContext *context;
+	EAGLContext *context_offscreen;
 	GLuint viewRenderbuffer, viewFramebuffer;
 	GLuint viewRenderbuffer, viewFramebuffer;
 	GLuint depthRenderbuffer;
 	GLuint depthRenderbuffer;
 }
 }
@@ -75,6 +76,9 @@ bool gles3_available = true;
 			gles3_available = false;
 			gles3_available = false;
 			fallback_gl2 = true;
 			fallback_gl2 = true;
 			NSLog(@"Failed to create OpenGL ES 3.0 context. Falling back to OpenGL ES 2.0");
 			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) {
 	if (context) {
 		context = nil;
 		context = nil;
 	}
 	}
+	if (context_offscreen) {
+		context_offscreen = nil;
+	}
 }
 }
 
 
 - (BOOL)createFramebuffer {
 - (BOOL)createFramebuffer {

+ 6 - 0
platform/iphone/os_iphone.h

@@ -61,6 +61,8 @@ private:
 
 
 	VideoMode video_mode;
 	VideoMode video_mode;
 
 
+	EAGLContext *offscreen_gl_context;
+
 	virtual int get_video_driver_count() const;
 	virtual int get_video_driver_count() const;
 	virtual const char *get_video_driver_name(int p_driver) 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;
 	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 void set_keep_screen_on(bool p_enabled);
 
 
 	virtual bool can_draw() const;
 	virtual bool can_draw() const;

+ 17 - 0
platform/iphone/os_iphone.mm

@@ -417,6 +417,22 @@ void OSIPhone::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) c
 	p_list->push_back(video_mode);
 	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 {
 bool OSIPhone::can_draw() const {
 	if (native_video_is_playing())
 	if (native_video_is_playing())
 		return false;
 		return false;
@@ -683,6 +699,7 @@ OSIPhone::OSIPhone(String p_data_dir, String p_cache_dir) {
 
 
 	main_loop = NULL;
 	main_loop = NULL;
 	visual_server = NULL;
 	visual_server = NULL;
+	offscreen_gl_context = NULL;
 
 
 	// can't call set_data_dir from here, since it requires DirAccess
 	// can't call set_data_dir from here, since it requires DirAccess
 	// which is initialized in initialize_core
 	// which is initialized in initialize_core

+ 4 - 0
platform/osx/os_osx.h

@@ -115,6 +115,7 @@ public:
 	id cursor;
 	id cursor;
 	NSOpenGLPixelFormat *pixelFormat;
 	NSOpenGLPixelFormat *pixelFormat;
 	NSOpenGLContext *context;
 	NSOpenGLContext *context;
+	NSOpenGLContext *context_offscreen;
 
 
 	Vector<Vector2> mpath;
 	Vector<Vector2> mpath;
 	bool layered_window;
 	bool layered_window;
@@ -250,6 +251,9 @@ public:
 	virtual VideoMode get_video_mode(int p_screen = 0) const;
 	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 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 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);
 	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);
 
 

+ 15 - 0
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];
 	[window_view setOpenGLContext:context];
 
 
+	context_offscreen = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
+
 	[context makeCurrentContext];
 	[context makeCurrentContext];
 
 
 	GLint dim[2];
 	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 {
 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 {
 int OS_OSX::get_screen_count() const {
 	NSArray *screenArray = [NSScreen screens];
 	NSArray *screenArray = [NSScreen screens];
 	return [screenArray count];
 	return [screenArray count];
@@ -3426,6 +3440,7 @@ OS_OSX *OS_OSX::singleton = NULL;
 
 
 OS_OSX::OS_OSX() {
 OS_OSX::OS_OSX() {
 	context = nullptr;
 	context = nullptr;
+	context_offscreen = nullptr;
 
 
 	memset(cursors, 0, sizeof(cursors));
 	memset(cursors, 0, sizeof(cursors));
 	key_event_pos = 0;
 	key_event_pos = 0;

+ 15 - 0
platform/windows/context_gl_windows.cpp

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

+ 5 - 0
platform/windows/context_gl_windows.h

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

+ 18 - 0
platform/windows/os_windows.cpp

@@ -1672,6 +1672,24 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
 	return OK;
 	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) {
 void OS_Windows::set_clipboard(const String &p_text) {
 	// Convert LF line endings to CRLF in clipboard content
 	// Convert LF line endings to CRLF in clipboard content
 	// Otherwise, line endings won't be visible when pasted in other software
 	// Otherwise, line endings won't be visible when pasted in other software

+ 3 - 0
platform/windows/os_windows.h

@@ -386,6 +386,9 @@ protected:
 	virtual void initialize_core();
 	virtual void initialize_core();
 	virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
 	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 set_main_loop(MainLoop *p_main_loop);
 	virtual void delete_main_loop();
 	virtual void delete_main_loop();
 
 

+ 18 - 0
platform/x11/context_gl_x11.cpp

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

+ 4 - 0
platform/x11/context_gl_x11.h

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

+ 18 - 0
platform/x11/os_x11.cpp

@@ -869,6 +869,24 @@ void OS_X11::finalize() {
 	args.clear();
 	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) {
 void OS_X11::set_mouse_mode(MouseMode p_mode) {
 	if (p_mode == mouse_mode) {
 	if (p_mode == mouse_mode) {
 		return;
 		return;

+ 3 - 0
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 Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
 	virtual void finalize();
 	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);
 	virtual void set_main_loop(MainLoop *p_main_loop);
 
 
 	void _window_changed(XEvent *event);
 	void _window_changed(XEvent *event);