Browse Source

Add MSAA support for WebXR

David Snopek 1 year ago
parent
commit
275c496bc8

+ 7 - 1
drivers/gles3/storage/config.cpp

@@ -99,8 +99,14 @@ Config::Config() {
 	msaa_supported = extensions.has("GL_EXT_framebuffer_multisample");
 	msaa_supported = extensions.has("GL_EXT_framebuffer_multisample");
 #endif
 #endif
 #ifndef IOS_ENABLED
 #ifndef IOS_ENABLED
+#ifdef WEB_ENABLED
+	msaa_multiview_supported = extensions.has("OCULUS_multiview");
+	rt_msaa_multiview_supported = msaa_multiview_supported;
+#else
 	msaa_multiview_supported = extensions.has("GL_EXT_multiview_texture_multisample");
 	msaa_multiview_supported = extensions.has("GL_EXT_multiview_texture_multisample");
-	multiview_supported = extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview");
+#endif
+
+	multiview_supported = extensions.has("OCULUS_multiview") || extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview");
 #endif
 #endif
 
 
 #ifdef ANDROID_ENABLED
 #ifdef ANDROID_ENABLED

+ 50 - 22
drivers/gles3/storage/render_scene_buffers_gles3.cpp

@@ -51,10 +51,42 @@ RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() {
 	free_render_buffer_data();
 	free_render_buffer_data();
 }
 }
 
 
+void RenderSceneBuffersGLES3::_rt_attach_textures(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count) {
+	if (p_view_count > 1) {
+		if (p_samples > 1) {
+#if defined(ANDROID_ENABLED) || defined(WEB_ENABLED)
+			glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, p_samples, 0, p_view_count);
+			glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, p_depth, 0, p_samples, 0, p_view_count);
+#else
+			ERR_PRINT_ONCE("Multiview MSAA isn't supported on this platform.");
+#endif
+		} else {
+#ifndef IOS_ENABLED
+			glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, 0, p_view_count);
+			glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, p_depth, 0, 0, p_view_count);
+#else
+			ERR_PRINT_ONCE("Multiview isn't supported on this platform.");
+#endif
+		}
+	} else {
+		if (p_samples > 1) {
+#ifdef ANDROID_ENABLED
+			glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0, p_samples);
+			glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0, p_samples);
+#else
+			ERR_PRINT_ONCE("MSAA via EXT_multisampled_render_to_texture isn't supported on this platform.");
+#endif
+		} else {
+			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0);
+			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0);
+		}
+	}
+}
+
 GLuint RenderSceneBuffersGLES3::_rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count) {
 GLuint RenderSceneBuffersGLES3::_rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count) {
 	FBDEF new_fbo;
 	FBDEF new_fbo;
 
 
-#ifdef ANDROID_ENABLED
+#if defined(ANDROID_ENABLED) || defined(WEB_ENABLED)
 	// There shouldn't be more then 3 entries in this...
 	// There shouldn't be more then 3 entries in this...
 	for (const FBDEF &cached_fbo : msaa3d.cached_fbos) {
 	for (const FBDEF &cached_fbo : msaa3d.cached_fbos) {
 		if (cached_fbo.color == p_color && cached_fbo.depth == p_depth) {
 		if (cached_fbo.color == p_color && cached_fbo.depth == p_depth) {
@@ -68,13 +100,7 @@ GLuint RenderSceneBuffersGLES3::_rt_get_cached_fbo(GLuint p_color, GLuint p_dept
 	glGenFramebuffers(1, &new_fbo.fbo);
 	glGenFramebuffers(1, &new_fbo.fbo);
 	glBindFramebuffer(GL_FRAMEBUFFER, new_fbo.fbo);
 	glBindFramebuffer(GL_FRAMEBUFFER, new_fbo.fbo);
 
 
-	if (p_view_count > 1) {
-		glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, p_samples, 0, p_view_count);
-		glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, p_depth, 0, p_samples, 0, p_view_count);
-	} else {
-		glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0, p_samples);
-		glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0, p_samples);
-	}
+	_rt_attach_textures(p_color, p_depth, p_samples, p_view_count);
 
 
 	GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
 	GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
 	if (status != GL_FRAMEBUFFER_COMPLETE) {
 	if (status != GL_FRAMEBUFFER_COMPLETE) {
@@ -317,8 +343,6 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p
 			// hence we'll use our FBO cache here.
 			// hence we'll use our FBO cache here.
 			msaa3d.needs_resolve = false;
 			msaa3d.needs_resolve = false;
 			msaa3d.check_fbo_cache = true;
 			msaa3d.check_fbo_cache = true;
-#endif
-#ifdef ANDROID_ENABLED
 		} else if (use_internal_buffer) {
 		} else if (use_internal_buffer) {
 			// We can combine MSAA and scaling/effects.
 			// We can combine MSAA and scaling/effects.
 			msaa3d.needs_resolve = false;
 			msaa3d.needs_resolve = false;
@@ -329,13 +353,7 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p
 			glGenFramebuffers(1, &msaa3d.fbo);
 			glGenFramebuffers(1, &msaa3d.fbo);
 			glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo);
 			glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo);
 
 
-			if (use_multiview) {
-				glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, internal3d.color, 0, msaa3d.samples, 0, view_count);
-				glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, internal3d.depth, 0, msaa3d.samples, 0, view_count);
-			} else {
-				glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, internal3d.color, 0, msaa3d.samples);
-				glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, internal3d.depth, 0, msaa3d.samples);
-			}
+			_rt_attach_textures(internal3d.color, internal3d.depth, msaa3d.samples, view_count);
 
 
 			GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
 			GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
 			if (status != GL_FRAMEBUFFER_COMPLETE) {
 			if (status != GL_FRAMEBUFFER_COMPLETE) {
@@ -515,13 +533,14 @@ void RenderSceneBuffersGLES3::free_render_buffer_data() {
 }
 }
 
 
 GLuint RenderSceneBuffersGLES3::get_render_fbo() {
 GLuint RenderSceneBuffersGLES3::get_render_fbo() {
-	if (msaa3d.check_fbo_cache) {
-		GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
+	GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
+	GLuint rt_fbo = 0;
 
 
+	if (msaa3d.check_fbo_cache) {
 		GLuint color = texture_storage->render_target_get_color(render_target);
 		GLuint color = texture_storage->render_target_get_color(render_target);
 		GLuint depth = texture_storage->render_target_get_depth(render_target);
 		GLuint depth = texture_storage->render_target_get_depth(render_target);
 
 
-		return _rt_get_cached_fbo(color, depth, msaa3d.samples, view_count);
+		rt_fbo = _rt_get_cached_fbo(color, depth, msaa3d.samples, view_count);
 	} else if (msaa3d.fbo != 0) {
 	} else if (msaa3d.fbo != 0) {
 		// We have an MSAA fbo, render to our MSAA buffer
 		// We have an MSAA fbo, render to our MSAA buffer
 		return msaa3d.fbo;
 		return msaa3d.fbo;
@@ -529,10 +548,19 @@ GLuint RenderSceneBuffersGLES3::get_render_fbo() {
 		// We have an internal buffer, render to our internal buffer!
 		// We have an internal buffer, render to our internal buffer!
 		return internal3d.fbo;
 		return internal3d.fbo;
 	} else {
 	} else {
-		GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
+		rt_fbo = texture_storage->render_target_get_fbo(render_target);
+	}
 
 
-		return texture_storage->render_target_get_fbo(render_target);
+	if (texture_storage->render_target_is_reattach_textures(render_target)) {
+		GLuint color = texture_storage->render_target_get_color(render_target);
+		GLuint depth = texture_storage->render_target_get_depth(render_target);
+
+		glBindFramebuffer(GL_FRAMEBUFFER, rt_fbo);
+		_rt_attach_textures(color, depth, msaa3d.samples, view_count);
+		glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo);
 	}
 	}
+
+	return rt_fbo;
 }
 }
 
 
 #endif // GLES3_ENABLED
 #endif // GLES3_ENABLED

+ 1 - 0
drivers/gles3/storage/render_scene_buffers_gles3.h

@@ -95,6 +95,7 @@ private:
 	void _clear_intermediate_buffers();
 	void _clear_intermediate_buffers();
 	void _clear_back_buffers();
 	void _clear_back_buffers();
 
 
+	void _rt_attach_textures(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count);
 	GLuint _rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count);
 	GLuint _rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count);
 
 
 public:
 public:

+ 14 - 0
drivers/gles3/storage/texture_storage.cpp

@@ -2320,6 +2320,20 @@ GLuint TextureStorage::render_target_get_depth(RID p_render_target) const {
 	}
 	}
 }
 }
 
 
+void TextureStorage::render_target_set_reattach_textures(RID p_render_target, bool p_reattach_textures) const {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_NULL(rt);
+
+	rt->reattach_textures = p_reattach_textures;
+}
+
+bool TextureStorage::render_target_is_reattach_textures(RID p_render_target) const {
+	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+	ERR_FAIL_NULL_V(rt, false);
+
+	return rt->reattach_textures;
+}
+
 void TextureStorage::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) {
 void TextureStorage::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) {
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
 	ERR_FAIL_NULL(rt);
 	ERR_FAIL_NULL(rt);

+ 3 - 0
drivers/gles3/storage/texture_storage.h

@@ -365,6 +365,7 @@ struct RenderTarget {
 
 
 	bool used_in_frame = false;
 	bool used_in_frame = false;
 	RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED;
 	RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED;
+	bool reattach_textures = false;
 
 
 	struct RTOverridden {
 	struct RTOverridden {
 		bool is_overridden = false;
 		bool is_overridden = false;
@@ -639,6 +640,8 @@ public:
 	GLuint render_target_get_fbo(RID p_render_target) const;
 	GLuint render_target_get_fbo(RID p_render_target) const;
 	GLuint render_target_get_color(RID p_render_target) const;
 	GLuint render_target_get_color(RID p_render_target) const;
 	GLuint render_target_get_depth(RID p_render_target) const;
 	GLuint render_target_get_depth(RID p_render_target) const;
+	void render_target_set_reattach_textures(RID p_render_target, bool p_reattach_textures) const;
+	bool render_target_is_reattach_textures(RID p_render_target) const;
 
 
 	virtual void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override;
 	virtual void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override;
 	virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const override;
 	virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const override;

+ 12 - 26
modules/webxr/webxr_interface_js.cpp

@@ -309,7 +309,7 @@ void WebXRInterfaceJS::uninitialize() {
 
 
 		godot_webxr_uninitialize();
 		godot_webxr_uninitialize();
 
 
-		GLES3::TextureStorage *texture_storage = dynamic_cast<GLES3::TextureStorage *>(RSG::texture_storage);
+		GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
 		if (texture_storage != nullptr) {
 		if (texture_storage != nullptr) {
 			for (KeyValue<unsigned int, RID> &E : texture_cache) {
 			for (KeyValue<unsigned int, RID> &E : texture_cache) {
 				// Forcibly mark as not part of a render target so we can free it.
 				// Forcibly mark as not part of a render target so we can free it.
@@ -438,16 +438,11 @@ Projection WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, double p_a
 }
 }
 
 
 bool WebXRInterfaceJS::pre_draw_viewport(RID p_render_target) {
 bool WebXRInterfaceJS::pre_draw_viewport(RID p_render_target) {
-	GLES3::TextureStorage *texture_storage = dynamic_cast<GLES3::TextureStorage *>(RSG::texture_storage);
+	GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
 	if (texture_storage == nullptr) {
 	if (texture_storage == nullptr) {
 		return false;
 		return false;
 	}
 	}
 
 
-	GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target);
-	if (rt == nullptr) {
-		return false;
-	}
-
 	// Cache the resources so we don't have to get them from JS twice.
 	// Cache the resources so we don't have to get them from JS twice.
 	color_texture = _get_color_texture();
 	color_texture = _get_color_texture();
 	depth_texture = _get_depth_texture();
 	depth_texture = _get_depth_texture();
@@ -460,23 +455,9 @@ bool WebXRInterfaceJS::pre_draw_viewport(RID p_render_target) {
 	//
 	//
 	// See: https://immersive-web.github.io/layers/#xropaquetextures
 	// See: https://immersive-web.github.io/layers/#xropaquetextures
 	//
 	//
-	// This is why we're doing this sort of silly check: if the color and depth
-	// textures are the same this frame as last frame, we need to attach them
-	// again, despite the fact that the GLuint for them hasn't changed.
-	if (rt->overridden.is_overridden && rt->overridden.color == color_texture && rt->overridden.depth == depth_texture) {
-		GLES3::Config *config = GLES3::Config::get_singleton();
-		bool use_multiview = rt->view_count > 1 && config->multiview_supported;
-
-		glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo);
-		if (use_multiview) {
-			glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, rt->view_count);
-			glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count);
-		} else {
-			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
-			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0);
-		}
-		glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo);
-	}
+	// So, even if the color and depth textures have the same GLuint as the last
+	// frame, we need to re-attach them again.
+	texture_storage->render_target_set_reattach_textures(p_render_target, true);
 
 
 	return true;
 	return true;
 }
 }
@@ -484,7 +465,12 @@ bool WebXRInterfaceJS::pre_draw_viewport(RID p_render_target) {
 Vector<BlitToScreen> WebXRInterfaceJS::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
 Vector<BlitToScreen> WebXRInterfaceJS::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
 	Vector<BlitToScreen> blit_to_screen;
 	Vector<BlitToScreen> blit_to_screen;
 
 
-	// We don't need to do anything here.
+	GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
+	if (texture_storage == nullptr) {
+		return blit_to_screen;
+	}
+
+	texture_storage->render_target_set_reattach_textures(p_render_target, false);
 
 
 	return blit_to_screen;
 	return blit_to_screen;
 };
 };
@@ -513,7 +499,7 @@ RID WebXRInterfaceJS::_get_texture(unsigned int p_texture_id) {
 		return cache->get();
 		return cache->get();
 	}
 	}
 
 
-	GLES3::TextureStorage *texture_storage = dynamic_cast<GLES3::TextureStorage *>(RSG::texture_storage);
+	GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
 	if (texture_storage == nullptr) {
 	if (texture_storage == nullptr) {
 		return RID();
 		return RID();
 	}
 	}

+ 2 - 0
platform/web/godot_webgl2.h

@@ -44,9 +44,11 @@ extern "C" {
 #endif
 #endif
 
 
 void godot_webgl2_glFramebufferTextureMultiviewOVR(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews);
 void godot_webgl2_glFramebufferTextureMultiviewOVR(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews);
+void godot_webgl2_glFramebufferTextureMultisampleMultiviewOVR(GLenum target, GLenum attachment, GLuint texture, GLint level, GLsizei samples, GLint baseViewIndex, GLsizei numViews);
 void godot_webgl2_glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data);
 void godot_webgl2_glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data);
 
 
 #define glFramebufferTextureMultiviewOVR godot_webgl2_glFramebufferTextureMultiviewOVR
 #define glFramebufferTextureMultiviewOVR godot_webgl2_glFramebufferTextureMultiviewOVR
+#define glFramebufferTextureMultisampleMultiviewOVR godot_webgl2_glFramebufferTextureMultisampleMultiviewOVR
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 16 - 0
platform/web/js/libs/library_godot_webgl2.externs.js

@@ -34,3 +34,19 @@ OVR_multiview2.prototype.FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR;
  * @return {void}
  * @return {void}
  */
  */
 OVR_multiview2.prototype.framebufferTextureMultiviewOVR = function(target, attachment, texture, level, baseViewIndex, numViews) {};
 OVR_multiview2.prototype.framebufferTextureMultiviewOVR = function(target, attachment, texture, level, baseViewIndex, numViews) {};
+
+/**
+ * @constructor OCULUS_multiview
+ */
+function OCULUS_multiview() {}
+
+/**
+ * @param {number} target
+ * @param {number} attachment
+ * @param {WebGLTexture} texture
+ * @param {number} level
+ * @param {number} baseViewIndex
+ * @param {number} numViews
+ * @return {void}
+ */
+OCULUS_multiview.prototype.framebufferTextureMultisampleMultiviewOVR = function(target, attachment, texture, level, samples, baseViewIndex, numViews) {};

+ 17 - 0
platform/web/js/libs/library_godot_webgl2.js

@@ -61,6 +61,23 @@ const GodotWebGL2 = {
 		const /** OVR_multiview2 */ ext = context.multiviewExt;
 		const /** OVR_multiview2 */ ext = context.multiviewExt;
 		ext.framebufferTextureMultiviewOVR(target, attachment, GL.textures[texture], level, base_view_index, num_views);
 		ext.framebufferTextureMultiviewOVR(target, attachment, GL.textures[texture], level, base_view_index, num_views);
 	},
 	},
+
+	godot_webgl2_glFramebufferTextureMultisampleMultiviewOVR__deps: ['emscripten_webgl_get_current_context'],
+	godot_webgl2_glFramebufferTextureMultisampleMultiviewOVR__proxy: 'sync',
+	godot_webgl2_glFramebufferTextureMultisampleMultiviewOVR__sig: 'viiiiiii',
+	godot_webgl2_glFramebufferTextureMultisampleMultiviewOVR: function (target, attachment, texture, level, samples, base_view_index, num_views) {
+		const context = GL.currentContext;
+		if (typeof context.oculusMultiviewExt === 'undefined') {
+			const /** OCULUS_multiview */ ext = context.GLctx.getExtension('OCULUS_multiview');
+			if (!ext) {
+				GodotRuntime.error('Trying to call glFramebufferTextureMultisampleMultiviewOVR() without the OCULUS_multiview extension');
+				return;
+			}
+			context.oculusMultiviewExt = ext;
+		}
+		const /** OCULUS_multiview */ ext = context.oculusMultiviewExt;
+		ext.framebufferTextureMultisampleMultiviewOVR(target, attachment, GL.textures[texture], level, samples, base_view_index, num_views);
+	},
 };
 };
 
 
 autoAddDeps(GodotWebGL2, '$GodotWebGL2');
 autoAddDeps(GodotWebGL2, '$GodotWebGL2');