Procházet zdrojové kódy

Add multiview to the opengl3 driver

David Snopek před 2 roky
rodič
revize
398ee08375

+ 21 - 4
drivers/gles3/rasterizer_gles3.cpp

@@ -274,15 +274,32 @@ RasterizerGLES3::~RasterizerGLES3() {
 void RasterizerGLES3::prepare_for_blitting_render_targets() {
 }
 
-void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect) {
+void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer) {
 	GLES3::RenderTarget *rt = GLES3::TextureStorage::get_singleton()->get_render_target(p_render_target);
+
 	ERR_FAIL_COND(!rt);
 
-	glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo);
+	GLuint read_fbo = 0;
+	if (rt->view_count > 1) {
+		glGenFramebuffers(1, &read_fbo);
+		glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo);
+		glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, p_layer);
+	} else {
+		glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo);
+	}
+
 	glReadBuffer(GL_COLOR_ATTACHMENT0);
 	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
 	// Flip content upside down to correct for coordinates.
-	glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, 0, p_screen_rect.size.y, p_screen_rect.size.x, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+	Vector2i screen_rect_end = p_screen_rect.get_end();
+	glBlitFramebuffer(0, 0, rt->size.x, rt->size.y,
+			p_screen_rect.position.x, screen_rect_end.y, screen_rect_end.x, p_screen_rect.position.y,
+			GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+	if (read_fbo != 0) {
+		glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+		glDeleteFramebuffers(1, &read_fbo);
+	}
 }
 
 // is this p_screen useless in a multi window environment?
@@ -293,7 +310,7 @@ void RasterizerGLES3::blit_render_targets_to_screen(DisplayServer::WindowID p_sc
 		RID rid_rt = blit.render_target;
 
 		Rect2 dst_rect = blit.dst_rect;
-		_blit_render_target_to_screen(rid_rt, p_screen, dst_rect);
+		_blit_render_target_to_screen(rid_rt, p_screen, dst_rect, blit.multi_view.use_layer ? blit.multi_view.layer : 0);
 	}
 }
 

+ 1 - 1
drivers/gles3/rasterizer_gles3.h

@@ -68,7 +68,7 @@ protected:
 	RasterizerCanvasGLES3 *canvas = nullptr;
 	RasterizerSceneGLES3 *scene = nullptr;
 
-	void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect);
+	void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer);
 
 public:
 	RendererUtilities *get_utilities() { return utilities; }

+ 36 - 10
drivers/gles3/rasterizer_scene_gles3.cpp

@@ -1273,6 +1273,19 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da
 	GLES3::MaterialStorage::store_transform(p_render_data->cam_transform, scene_state.ubo.inv_view_matrix);
 	GLES3::MaterialStorage::store_transform(p_render_data->inv_cam_transform, scene_state.ubo.view_matrix);
 
+	if (p_render_data->view_count > 1) {
+		for (uint32_t v = 0; v < p_render_data->view_count; v++) {
+			projection = correction * p_render_data->view_projection[v];
+			GLES3::MaterialStorage::store_camera(projection, scene_state.multiview_ubo.projection_matrix_view[v]);
+			GLES3::MaterialStorage::store_camera(projection.inverse(), scene_state.multiview_ubo.inv_projection_matrix_view[v]);
+
+			scene_state.multiview_ubo.eye_offset[v][0] = p_render_data->view_eye_offset[v].x;
+			scene_state.multiview_ubo.eye_offset[v][1] = p_render_data->view_eye_offset[v].y;
+			scene_state.multiview_ubo.eye_offset[v][2] = p_render_data->view_eye_offset[v].z;
+			scene_state.multiview_ubo.eye_offset[v][3] = 0.0;
+		}
+	}
+
 	scene_state.ubo.directional_light_count = p_render_data->directional_light_count;
 
 	scene_state.ubo.z_far = p_render_data->z_far;
@@ -1374,6 +1387,15 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da
 	glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_DATA_UNIFORM_LOCATION, scene_state.ubo_buffer);
 	glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::UBO), &scene_state.ubo, GL_STREAM_DRAW);
 	glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+	if (p_render_data->view_count > 1) {
+		if (scene_state.multiview_buffer == 0) {
+			glGenBuffers(1, &scene_state.multiview_buffer);
+		}
+		glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer);
+		glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::MultiviewUBO), &scene_state.multiview_ubo, GL_STREAM_DRAW);
+		glBindBuffer(GL_UNIFORM_BUFFER, 0);
+	}
 }
 
 // Puts lights into Uniform Buffers. Needs to be called before _fill_list as this caches the index of each light in the Uniform Buffer
@@ -1916,8 +1938,14 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
 	GLES3::SceneShaderData *prev_shader = nullptr;
 	GeometryInstanceGLES3 *prev_inst = nullptr;
 	SceneShaderGLES3::ShaderVariant prev_variant = SceneShaderGLES3::ShaderVariant::MODE_COLOR;
+	SceneShaderGLES3::ShaderVariant shader_variant = SceneShaderGLES3::MODE_COLOR; // Assigned to silence wrong -Wmaybe-initialized
 
-	SceneShaderGLES3::ShaderVariant shader_variant = SceneShaderGLES3::MODE_COLOR; // Assigned to silence wrong -Wmaybe-initialized.
+	// @todo Get this from p_params->spec_constant_base_flags instead of hardcoding it.
+	uint32_t base_spec_constants = 0;
+
+	if (p_render_data->view_count > 1) {
+		base_spec_constants |= 1 << SPEC_CONSTANT_USE_MULTIVIEW;
+	}
 
 	switch (p_pass_mode) {
 		case PASS_MODE_COLOR:
@@ -1957,8 +1985,6 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
 			continue;
 		}
 
-		//uint32_t base_spec_constants = p_params->spec_constant_base_flags;
-
 		GLES3::SceneShaderData *shader;
 		GLES3::SceneMaterialData *material_data;
 		void *mesh_surface;
@@ -2128,7 +2154,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
 		}
 
 		if (prev_shader != shader || prev_variant != instance_variant) {
-			material_storage->shaders.scene_shader.version_bind_shader(shader->version, instance_variant);
+			material_storage->shaders.scene_shader.version_bind_shader(shader->version, instance_variant, base_spec_constants);
 			float opaque_prepass_threshold = 0.0;
 			if constexpr (p_pass_mode == PASS_MODE_DEPTH) {
 				opaque_prepass_threshold = 0.99;
@@ -2136,7 +2162,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
 				opaque_prepass_threshold = 0.1;
 			}
 
-			material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OPAQUE_PREPASS_THRESHOLD, opaque_prepass_threshold, shader->version, instance_variant);
+			material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OPAQUE_PREPASS_THRESHOLD, opaque_prepass_threshold, shader->version, instance_variant, base_spec_constants);
 
 			prev_shader = shader;
 			prev_variant = instance_variant;
@@ -2144,21 +2170,21 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
 
 		if (prev_inst != inst || prev_shader != shader || prev_variant != instance_variant) {
 			// Rebind the light indices.
-			material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OMNI_LIGHT_COUNT, inst->omni_light_count, shader->version, instance_variant);
-			material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_count, shader->version, instance_variant);
+			material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OMNI_LIGHT_COUNT, inst->omni_light_count, shader->version, instance_variant, base_spec_constants);
+			material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_count, shader->version, instance_variant, base_spec_constants);
 
 			if (inst->omni_light_count) {
-				glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::OMNI_LIGHT_INDICES, shader->version, instance_variant), inst->omni_light_count, inst->omni_light_gl_cache.ptr());
+				glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::OMNI_LIGHT_INDICES, shader->version, instance_variant, base_spec_constants), inst->omni_light_count, inst->omni_light_gl_cache.ptr());
 			}
 
 			if (inst->spot_light_count) {
-				glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, instance_variant), inst->spot_light_count, inst->spot_light_gl_cache.ptr());
+				glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, instance_variant, base_spec_constants), inst->spot_light_count, inst->spot_light_gl_cache.ptr());
 			}
 
 			prev_inst = inst;
 		}
 
-		material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant);
+		material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant, base_spec_constants);
 		if (inst->instance_count > 0) {
 			// Using MultiMesh.
 			// Bind instance buffers.

+ 12 - 0
drivers/gles3/rasterizer_scene_gles3.h

@@ -74,6 +74,7 @@ enum SceneUniformLocation {
 	SCENE_OMNILIGHT_UNIFORM_LOCATION,
 	SCENE_SPOTLIGHT_UNIFORM_LOCATION,
 	SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION,
+	SCENE_MULTIVIEW_UNIFORM_LOCATION,
 };
 
 enum SkyUniformLocation {
@@ -90,6 +91,8 @@ enum {
 	SPEC_CONSTANT_DISABLE_OMNI_LIGHTS = 2,
 	SPEC_CONSTANT_DISABLE_SPOT_LIGHTS = 3,
 	SPEC_CONSTANT_DISABLE_FOG = 4,
+	SPEC_CONSTANT_USE_RADIANCE_MAP = 5,
+	SPEC_CONSTANT_USE_MULTIVIEW = 6,
 };
 
 struct RenderDataGLES3 {
@@ -343,6 +346,13 @@ private:
 		};
 		static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes");
 
+		struct MultiviewUBO {
+			float projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
+			float inv_projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
+			float eye_offset[RendererSceneRender::MAX_RENDER_VIEWS][4];
+		};
+		static_assert(sizeof(MultiviewUBO) % 16 == 0, "Multiview UBO size must be a multiple of 16 bytes");
+
 		struct TonemapUBO {
 			float exposure = 1.0;
 			float white = 1.0;
@@ -353,6 +363,8 @@ private:
 
 		UBO ubo;
 		GLuint ubo_buffer = 0;
+		MultiviewUBO multiview_ubo;
+		GLuint multiview_buffer = 0;
 		GLuint tonemap_buffer = 0;
 
 		bool used_depth_prepass = false;

+ 26 - 7
drivers/gles3/shader_gles3.cpp

@@ -142,7 +142,7 @@ RID ShaderGLES3::version_create() {
 	return version_owner.make_rid(version);
 }
 
-void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization) {
+void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization) {
 #ifdef GLES_OVER_GL
 	builder.append("#version 330\n");
 	builder.append("#define USE_GLES_OVER_GL\n");
@@ -171,6 +171,24 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant
 	}
 	builder.append("\n"); //make sure defines begin at newline
 
+	// Insert multiview extension loading, because it needs to appear before
+	// any non-preprocessor code (like the "precision highp..." lines below).
+	builder.append("#ifdef USE_MULTIVIEW\n");
+	builder.append("#if defined(GL_OVR_multiview2)\n");
+	builder.append("#extension GL_OVR_multiview2 : require\n");
+	builder.append("#elif defined(GL_OVR_multiview)\n");
+	builder.append("#extension GL_OVR_multiview : require\n");
+	builder.append("#endif\n");
+	if (p_stage_type == StageType::STAGE_TYPE_VERTEX) {
+		builder.append("layout(num_views=2) in;\n");
+	}
+	builder.append("#define ViewIndex gl_ViewID_OVR\n");
+	builder.append("#define MAX_VIEWS 2\n");
+	builder.append("#else\n");
+	builder.append("#define ViewIndex 0\n");
+	builder.append("#define MAX_VIEWS 1\n");
+	builder.append("#endif\n");
+
 	// Default to highp precision unless specified otherwise.
 	builder.append("precision highp float;\n");
 	builder.append("precision highp int;\n");
@@ -180,8 +198,9 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant
 	builder.append("precision highp sampler2DArray;\n");
 #endif
 
-	for (uint32_t i = 0; i < p_template.chunks.size(); i++) {
-		const StageTemplate::Chunk &chunk = p_template.chunks[i];
+	const StageTemplate &stage_template = stage_templates[p_stage_type];
+	for (uint32_t i = 0; i < stage_template.chunks.size(); i++) {
+		const StageTemplate::Chunk &chunk = stage_template.chunks[i];
 		switch (chunk.type) {
 			case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: {
 				builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment)
@@ -224,7 +243,7 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_
 	//vertex stage
 	{
 		StringBuilder builder;
-		_build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_VERTEX], p_specialization);
+		_build_variant_code(builder, p_variant, p_version, STAGE_TYPE_VERTEX, p_specialization);
 
 		spec.vert_id = glCreateShader(GL_VERTEX_SHADER);
 		String builder_string = builder.as_string();
@@ -272,7 +291,7 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_
 	//fragment stage
 	{
 		StringBuilder builder;
-		_build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_FRAGMENT], p_specialization);
+		_build_variant_code(builder, p_variant, p_version, STAGE_TYPE_FRAGMENT, p_specialization);
 
 		spec.frag_id = glCreateShader(GL_FRAGMENT_SHADER);
 		String builder_string = builder.as_string();
@@ -413,7 +432,7 @@ RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_ver
 
 		{
 			StringBuilder builder;
-			_build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_VERTEX], specialization_default_mask);
+			_build_variant_code(builder, i, version, STAGE_TYPE_VERTEX, specialization_default_mask);
 
 			RS::ShaderNativeSourceCode::Version::Stage stage;
 			stage.name = "vertex";
@@ -425,7 +444,7 @@ RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_ver
 		//fragment stage
 		{
 			StringBuilder builder;
-			_build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_FRAGMENT], specialization_default_mask);
+			_build_variant_code(builder, i, version, STAGE_TYPE_FRAGMENT, specialization_default_mask);
 
 			RS::ShaderNativeSourceCode::Version::Stage stage;
 			stage.name = "fragment";

+ 1 - 1
drivers/gles3/shader_gles3.h

@@ -153,7 +153,7 @@ private:
 
 	StageTemplate stage_templates[STAGE_TYPE_MAX];
 
-	void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization);
+	void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization);
 
 	void _add_stage(const char *p_code, StageType p_stage_type);
 

+ 37 - 4
drivers/gles3/shaders/scene.glsl

@@ -16,6 +16,7 @@ DISABLE_LIGHT_OMNI = false
 DISABLE_LIGHT_SPOT = false
 DISABLE_FOG = false
 USE_RADIANCE_MAP = true
+USE_MULTIVIEW = false
 
 
 #[vertex]
@@ -153,6 +154,15 @@ layout(std140) uniform SceneData { // ubo:2
 }
 scene_data;
 
+#ifdef USE_MULTIVIEW
+layout(std140) uniform MultiviewData { // ubo:8
+	highp mat4 projection_matrix_view[MAX_VIEWS];
+	highp mat4 inv_projection_matrix_view[MAX_VIEWS];
+	highp vec4 eye_offset[MAX_VIEWS];
+}
+multiview_data;
+#endif
+
 uniform highp mat4 world_transform;
 
 #ifdef USE_LIGHTMAP
@@ -250,8 +260,14 @@ void main() {
 #if defined(OVERRIDE_POSITION)
 	highp vec4 position;
 #endif
-	highp mat4 projection_matrix = scene_data.projection_matrix;
-	highp mat4 inv_projection_matrix = scene_data.inv_projection_matrix;
+
+#ifdef USE_MULTIVIEW
+	mat4 projection_matrix = multiview_data.projection_matrix_view[ViewIndex];
+	mat4 inv_projection_matrix = multiview_data.inv_projection_matrix_view[ViewIndex];
+#else
+	mat4 projection_matrix = scene_data.projection_matrix;
+	mat4 inv_projection_matrix = scene_data.inv_projection_matrix;
+#endif //USE_MULTIVIEW
 
 #ifdef USE_INSTANCING
 	vec4 instance_custom = vec4(unpackHalf2x16(instance_color_custom_data.z), unpackHalf2x16(instance_color_custom_data.w));
@@ -339,7 +355,6 @@ void main() {
 /* clang-format off */
 #[fragment]
 
-
 // Default to SPECULAR_SCHLICK_GGX.
 #if !defined(SPECULAR_DISABLED) && !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_TOON)
 #define SPECULAR_SCHLICK_GGX
@@ -463,6 +478,15 @@ layout(std140) uniform SceneData { // ubo:2
 }
 scene_data;
 
+#ifdef USE_MULTIVIEW
+layout(std140) uniform MultiviewData { // ubo:8
+	highp mat4 projection_matrix_view[MAX_VIEWS];
+	highp mat4 inv_projection_matrix_view[MAX_VIEWS];
+	highp vec4 eye_offset[MAX_VIEWS];
+}
+multiview_data;
+#endif
+
 /* clang-format off */
 
 #GLOBALS
@@ -530,8 +554,13 @@ uniform highp samplerCubeShadow positional_shadow; // texunit:-4
 
 #endif // !defined(DISABLE_LIGHT_OMNI) && !defined(DISABLE_LIGHT_SPOT)
 
-uniform highp sampler2D screen_texture; // texunit:-5
+#ifdef USE_MULTIVIEW
+uniform highp sampler2DArray depth_buffer; // texunit:-6
+uniform highp sampler2DArray screen_texture; // texunit:-5
+#else
 uniform highp sampler2D depth_buffer; // texunit:-6
+uniform highp sampler2D screen_texture; // texunit:-5
+#endif
 
 uniform highp mat4 world_transform;
 uniform mediump float opaque_prepass_threshold;
@@ -884,7 +913,11 @@ vec4 fog_process(vec3 vertex) {
 void main() {
 	//lay out everything, whatever is unused is optimized away anyway
 	vec3 vertex = vertex_interp;
+#ifdef USE_MULTIVIEW
+	vec3 view = -normalize(vertex_interp - multiview_data.eye_offset[ViewIndex].xyz);
+#else
 	vec3 view = -normalize(vertex_interp);
+#endif
 	vec3 albedo = vec3(1.0);
 	vec3 backlight = vec3(0.0);
 	vec4 transmittance_color = vec4(0.0, 0.0, 0.0, 1.0);

+ 20 - 0
drivers/gles3/shaders/tonemap.glsl

@@ -44,7 +44,11 @@ in vec2 uv_interp;
 
 layout(location = 0) out vec4 frag_color;
 
+#ifdef USE_MULTIVIEW
+uniform highp sampler2DArray source; //texunit:0
+#else
 uniform highp sampler2D source; //texunit:0
+#endif
 
 #if defined(USE_GLOW_LEVEL1) || defined(USE_GLOW_LEVEL2) || defined(USE_GLOW_LEVEL3) || defined(USE_GLOW_LEVEL4) || defined(USE_GLOW_LEVEL5) || defined(USE_GLOW_LEVEL6) || defined(USE_GLOW_LEVEL7)
 #define USING_GLOW // only use glow when at least one glow level is selected
@@ -191,10 +195,17 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) {
 	const float FXAA_REDUCE_MUL = (1.0 / 8.0);
 	const float FXAA_SPAN_MAX = 8.0;
 
+#ifdef USE_MULTIVIEW
+	vec3 rgbNW = textureLod(source, vec3(uv_interp + vec2(-1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz;
+	vec3 rgbNE = textureLod(source, vec3(uv_interp + vec2(1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz;
+	vec3 rgbSW = textureLod(source, vec3(uv_interp + vec2(-1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz;
+	vec3 rgbSE = textureLod(source, vec3(uv_interp + vec2(1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz;
+#else
 	vec3 rgbNW = textureLod(source, uv_interp + vec2(-1.0, -1.0) * pixel_size, 0.0).xyz;
 	vec3 rgbNE = textureLod(source, uv_interp + vec2(1.0, -1.0) * pixel_size, 0.0).xyz;
 	vec3 rgbSW = textureLod(source, uv_interp + vec2(-1.0, 1.0) * pixel_size, 0.0).xyz;
 	vec3 rgbSE = textureLod(source, uv_interp + vec2(1.0, 1.0) * pixel_size, 0.0).xyz;
+#endif
 	vec3 rgbM = color;
 	vec3 luma = vec3(0.299, 0.587, 0.114);
 	float lumaNW = dot(rgbNW, luma);
@@ -219,8 +230,13 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) {
 						  dir * rcpDirMin)) *
 			pixel_size;
 
+#ifdef USE_MULTIVIEW
+	vec3 rgbA = 0.5 * (textureLod(source, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz);
+	vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz);
+#else
 	vec3 rgbA = 0.5 * (textureLod(source, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz);
 	vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source, uv_interp + dir * 0.5, 0.0).xyz);
+#endif
 
 	float lumaB = dot(rgbB, luma);
 	if ((lumaB < lumaMin) || (lumaB > lumaMax)) {
@@ -231,7 +247,11 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) {
 }
 
 void main() {
+#ifdef USE_MULTIVIEW
+	vec4 color = textureLod(source, vec3(uv_interp, ViewIndex), 0.0);
+#else
 	vec4 color = textureLod(source, uv_interp, 0.0);
+#endif
 
 #ifdef USE_FXAA
 	color.rgb = apply_fxaa(color.rgb, uv_interp, pixel_size);

+ 19 - 0
drivers/gles3/storage/config.cpp

@@ -34,6 +34,15 @@
 #include "core/config/project_settings.h"
 #include "core/templates/vector.h"
 
+#ifdef ANDROID_ENABLED
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <GLES3/gl3platform.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#endif
+
 using namespace GLES3;
 
 #define _GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
@@ -98,6 +107,16 @@ Config::Config() {
 		anisotropic_level = MIN(float(1 << int(ProjectSettings::get_singleton()->get("rendering/textures/default_filters/anisotropic_filtering_level"))), anisotropic_level);
 	}
 
+	multiview_supported = extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview");
+#ifdef ANDROID_ENABLED
+	if (multiview_supported) {
+		eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR");
+		if (eglFramebufferTextureMultiviewOVR == nullptr) {
+			multiview_supported = false;
+		}
+	}
+#endif
+
 	force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading");
 	use_nearest_mip_filter = GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter");
 

+ 9 - 0
drivers/gles3/storage/config.h

@@ -44,6 +44,10 @@
 #include OPENGL_INCLUDE_H
 #endif
 
+#ifdef ANDROID_ENABLED
+typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei);
+#endif
+
 namespace GLES3 {
 
 class Config {
@@ -82,6 +86,11 @@ public:
 	bool support_anisotropic_filter = false;
 	float anisotropic_level = 0.0f;
 
+	bool multiview_supported = false;
+#ifdef ANDROID_ENABLED
+	PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr;
+#endif
+
 	static Config *get_singleton() { return singleton; };
 
 	Config();

+ 36 - 12
drivers/gles3/storage/render_scene_buffers_gles3.cpp

@@ -33,12 +33,17 @@
 #include "render_scene_buffers_gles3.h"
 #include "texture_storage.h"
 
+#ifdef ANDROID_ENABLED
+#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR
+#endif
+
 RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() {
 	free_render_buffer_data();
 }
 
 void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) {
 	GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
+	GLES3::Config *config = GLES3::Config::get_singleton();
 
 	//internal_size.x = p_internal_size.x; // ignore for now
 	//internal_size.y = p_internal_size.y;
@@ -50,7 +55,7 @@ void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_inte
 	//msaa = p_msaa;
 	//screen_space_aa = p_screen_space_aa;
 	//use_debanding = p_use_debanding;
-	//view_count = p_view_count;
+	view_count = p_view_count;
 
 	free_render_buffer_data();
 
@@ -62,24 +67,43 @@ void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_inte
 	glGenFramebuffers(1, &framebuffer);
 	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
 
-	glBindTexture(GL_TEXTURE_2D, rt->color);
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
+	if (view_count > 1 && config->multiview_supported) {
+		glBindTexture(GL_TEXTURE_2D_ARRAY, rt->color);
+		glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, view_count);
+	} else {
+		glBindTexture(GL_TEXTURE_2D, rt->color);
+		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
+	}
 
 	glGenTextures(1, &depth_texture);
-	glBindTexture(GL_TEXTURE_2D, depth_texture);
-
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
-
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+	if (view_count > 1 && config->multiview_supported) {
+		glBindTexture(GL_TEXTURE_2D_ARRAY, depth_texture);
+		glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
+
+		glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+	} else {
+		glBindTexture(GL_TEXTURE_2D, depth_texture);
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
+
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+	}
 
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0);
+	if (view_count > 1 && config->multiview_supported) {
+		glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_texture, 0, 0, view_count);
+	} else {
+		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0);
+	}
 
 	GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
 
 	glBindTexture(GL_TEXTURE_2D, 0);
+	glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
 	glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo);
 
 	if (status != GL_FRAMEBUFFER_COMPLETE) {

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

@@ -56,7 +56,7 @@ public:
 	RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED;
 	//RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED;
 	//bool use_debanding = false;
-	//uint32_t view_count = 1;
+	uint32_t view_count = 1;
 
 	bool is_transparent = false;
 

+ 45 - 16
drivers/gles3/storage/texture_storage.cpp

@@ -34,6 +34,10 @@
 #include "config.h"
 #include "drivers/gles3/effects/copy_effects.h"
 
+#ifdef ANDROID_ENABLED
+#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR
+#endif
+
 using namespace GLES3;
 
 TextureStorage *TextureStorage::singleton = nullptr;
@@ -720,8 +724,7 @@ void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) {
 }
 
 void TextureStorage::texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer) {
-	// only 1 layer so far
-	texture_set_data(p_texture, p_image);
+	texture_set_data(p_texture, p_image, p_layer);
 #ifdef TOOLS_ENABLED
 	Texture *tex = texture_owner.get_or_null(p_texture);
 
@@ -1012,7 +1015,7 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image,
 		img->resize_to_po2(false);
 	}
 
-	GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : GL_TEXTURE_2D;
+	GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : texture->target;
 
 	Vector<uint8_t> read = img->get_data();
 
@@ -1069,7 +1072,11 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image,
 			glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]);
 		} else {
 			glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-			glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]);
+			if (texture->target == GL_TEXTURE_2D_ARRAY) {
+				glTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 0, format, type, &read[ofs]);
+			} else {
+				glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]);
+			}
 		}
 
 		tsize += size;
@@ -1425,6 +1432,8 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
 		return;
 	}
 
+	Config *config = Config::get_singleton();
+
 	rt->color_internal_format = rt->is_transparent ? GL_RGBA8 : GL_RGB10_A2;
 	rt->color_format = GL_RGBA;
 	rt->color_type = rt->is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV;
@@ -1446,17 +1455,29 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
 
 		// color
 		glGenTextures(1, &rt->color);
-		glBindTexture(GL_TEXTURE_2D, rt->color);
-
-		glTexImage2D(GL_TEXTURE_2D, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr);
-
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+		if (rt->view_count > 1 && config->multiview_supported) {
+			glBindTexture(GL_TEXTURE_2D_ARRAY, rt->color);
+			glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, rt->color_internal_format, rt->size.x, rt->size.y, rt->view_count, 0, rt->color_format, rt->color_type, nullptr);
+
+			glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+			glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+			glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+			glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+		} else {
+			glBindTexture(GL_TEXTURE_2D, rt->color);
+			glTexImage2D(GL_TEXTURE_2D, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr);
 
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+		}
 
-		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
+		if (rt->view_count > 1 && config->multiview_supported) {
+			glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, rt->view_count);
+		} else {
+			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
+		}
 
 		GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
 
@@ -1475,8 +1496,15 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
 
 		texture->format = rt->image_format;
 		texture->real_format = rt->image_format;
-		texture->type = Texture::TYPE_2D;
-		texture->target = GL_TEXTURE_2D;
+		if (rt->view_count > 1 && config->multiview_supported) {
+			texture->type = Texture::TYPE_LAYERED;
+			texture->target = GL_TEXTURE_2D_ARRAY;
+			texture->layers = rt->view_count;
+		} else {
+			texture->type = Texture::TYPE_2D;
+			texture->target = GL_TEXTURE_2D;
+			texture->layers = 1;
+		}
 		texture->gl_format_cache = rt->color_format;
 		texture->gl_type_cache = GL_UNSIGNED_BYTE;
 		texture->gl_internal_format_cache = rt->color_internal_format;
@@ -1619,13 +1647,14 @@ void TextureStorage::render_target_set_size(RID p_render_target, int p_width, in
 	RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
 	ERR_FAIL_COND(!rt);
 
-	if (p_width == rt->size.x && p_height == rt->size.y) {
+	if (p_width == rt->size.x && p_height == rt->size.y && p_view_count == rt->view_count) {
 		return;
 	}
 
 	_clear_render_target(rt);
 
 	rt->size = Size2i(p_width, p_height);
+	rt->view_count = p_view_count;
 
 	_update_render_target(rt);
 }

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

@@ -324,6 +324,7 @@ private:
 struct RenderTarget {
 	Point2i position = Point2i(0, 0);
 	Size2i size = Size2i(0, 0);
+	uint32_t view_count = 1;
 	int mipmap_count = 1;
 	RID self;
 	GLuint fbo = 0;

+ 1 - 1
modules/webxr/godot_webxr.h

@@ -65,7 +65,7 @@ extern int godot_webxr_get_view_count();
 extern int *godot_webxr_get_render_target_size();
 extern float *godot_webxr_get_transform_for_eye(int p_eye);
 extern float *godot_webxr_get_projection_for_eye(int p_eye);
-extern void godot_webxr_commit_for_eye(int p_eye, unsigned int p_destination_fbo);
+extern void godot_webxr_commit(unsigned int p_texture);
 
 extern void godot_webxr_sample_controller_data();
 extern int godot_webxr_get_controller_count();

+ 26 - 15
modules/webxr/native/library_godot_webxr.js

@@ -337,20 +337,18 @@ const GodotWebXR = {
 		return buf;
 	},
 
-	godot_webxr_commit_for_eye__proxy: 'sync',
-	godot_webxr_commit_for_eye__sig: 'vii',
-	godot_webxr_commit_for_eye: function (p_eye, p_destination_fbo) {
+	godot_webxr_commit__proxy: 'sync',
+	godot_webxr_commit__sig: 'vi',
+	godot_webxr_commit: function (p_texture) {
 		if (!GodotWebXR.session || !GodotWebXR.pose) {
 			return;
 		}
 
-		const view_index = (p_eye === 2 /* ARVRInterface::EYE_RIGHT */) ? 1 : 0;
 		const glLayer = GodotWebXR.session.renderState.baseLayer;
-		const view = GodotWebXR.pose.views[view_index];
-		const viewport = glLayer.getViewport(view);
+		const views = GodotWebXR.pose.views;
 		const gl = GodotWebXR.gl;
 
-		const framebuffer = GL.framebuffers[p_destination_fbo];
+		const texture = GL.textures[p_texture];
 
 		const orig_framebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
 		const orig_read_framebuffer = gl.getParameter(gl.READ_FRAMEBUFFER_BINDING);
@@ -359,14 +357,27 @@ const GodotWebXR = {
 
 		// Copy from Godot render target into framebuffer from WebXR.
 		gl.bindFramebuffer(gl.FRAMEBUFFER, null);
-		gl.bindFramebuffer(gl.READ_FRAMEBUFFER, framebuffer);
-		gl.readBuffer(gl.COLOR_ATTACHMENT0);
-		gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, glLayer.framebuffer);
-
-		// Flip Y upside down on destination.
-		gl.blitFramebuffer(0, 0, viewport.width, viewport.height,
-			viewport.x, viewport.height, viewport.width, viewport.y,
-			gl.COLOR_BUFFER_BIT, gl.NEAREST);
+		for (let i = 0; i < views.length; i++) {
+			const viewport = glLayer.getViewport(views[i]);
+
+			const read_fbo = gl.createFramebuffer();
+			gl.bindFramebuffer(gl.READ_FRAMEBUFFER, read_fbo);
+			if (views.length > 1) {
+				gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture, 0, i);
+			} else {
+				gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
+			}
+			gl.readBuffer(gl.COLOR_ATTACHMENT0);
+			gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, glLayer.framebuffer);
+
+			// Flip Y upside down on destination.
+			gl.blitFramebuffer(0, 0, viewport.width, viewport.height,
+				viewport.x, viewport.y + viewport.height, viewport.x + viewport.width, viewport.y,
+				gl.COLOR_BUFFER_BIT, gl.NEAREST);
+
+			gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+			gl.deleteFramebuffer(read_fbo);
+		}
 
 		// Restore state.
 		gl.bindFramebuffer(gl.FRAMEBUFFER, orig_framebuffer);

+ 1 - 2
modules/webxr/webxr_interface_js.cpp

@@ -415,8 +415,7 @@ Vector<BlitToScreen> WebXRInterfaceJS::post_draw_viewport(RID p_render_target, c
 
 	GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target);
 
-	// @todo Support multiple eyes!
-	godot_webxr_commit_for_eye(1, rt->fbo);
+	godot_webxr_commit(rt->color);
 
 	return blit_to_screen;
 };

+ 1 - 0
platform/web/SCsub

@@ -35,6 +35,7 @@ sys_env.AddJSLibraries(
         "js/libs/library_godot_os.js",
         "js/libs/library_godot_runtime.js",
         "js/libs/library_godot_input.js",
+        "js/libs/library_godot_webgl2.js",
     ]
 )
 

+ 3 - 0
platform/web/display_server_web.cpp

@@ -773,6 +773,9 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode
 		if (emscripten_webgl_make_context_current(webgl_ctx) != EMSCRIPTEN_RESULT_SUCCESS) {
 			webgl2_init_failed = true;
 		} else {
+			if (!emscripten_webgl_enable_extension(webgl_ctx, "OVR_multiview2")) {
+				// @todo Should we log this?
+			}
 			RasterizerGLES3::make_current();
 		}
 	}

+ 17 - 0
platform/web/godot_webgl2.h

@@ -34,4 +34,21 @@
 #include "GLES3/gl3.h"
 #include "webgl/webgl2.h"
 
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632
+#define GL_MAX_VIEWS_OVR 0x9631
+#define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void godot_webgl2_glFramebufferTextureMultiviewOVR(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews);
+
+#define glFramebufferTextureMultiviewOVR godot_webgl2_glFramebufferTextureMultiviewOVR
+
+#ifdef __cplusplus
+}
+#endif
+
 #endif // GODOT_WEBGL2_H

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

@@ -0,0 +1,52 @@
+/*************************************************************************/
+/*  library_godot_webgl2.js                                               */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+const GodotWebGL2 = {
+	$GodotWebGL2__deps: ['$GL', '$GodotRuntime'],
+	$GodotWebGL2: {},
+
+	godot_webgl2_glFramebufferTextureMultiviewOVR__deps: ['emscripten_webgl_get_current_context'],
+	godot_webgl2_glFramebufferTextureMultiviewOVR__proxy: 'sync',
+	godot_webgl2_glFramebufferTextureMultiviewOVR__sig: 'viiiiii',
+	godot_webgl2_glFramebufferTextureMultiviewOVR: function (target, attachment, texture, level, base_view_index, num_views) {
+		const context = GL.currentContext;
+		if (typeof context.multiviewExt === 'undefined') {
+			const ext = context.GLctx.getExtension('OVR_multiview2');
+			if (!ext) {
+				console.error('Trying to call glFramebufferTextureMultiviewOVR() without the OVR_multiview2 extension');
+				return;
+			}
+			context.multiviewExt = ext;
+		}
+		context.multiviewExt.framebufferTextureMultiviewOVR(target, attachment, GL.textures[texture], level, base_view_index, num_views);
+	},
+};
+
+autoAddDeps(GodotWebGL2, '$GodotWebGL2');
+mergeInto(LibraryManager.library, GodotWebGL2);

+ 24 - 3
thirdparty/glad/KHR/khrplatform.h

@@ -153,6 +153,20 @@ typedef int64_t                 khronos_int64_t;
 typedef uint64_t                khronos_uint64_t;
 #define KHRONOS_SUPPORT_INT64   1
 #define KHRONOS_SUPPORT_FLOAT   1
+/*
+ * To support platform where unsigned long cannot be used interchangeably with
+ * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
+ * Ideally, we could just use (u)intptr_t everywhere, but this could result in
+ * ABI breakage if khronos_uintptr_t is changed from unsigned long to
+ * unsigned long long or similar (this results in different C++ name mangling).
+ * To avoid changes for existing platforms, we restrict usage of intptr_t to
+ * platforms where the size of a pointer is larger than the size of long.
+ */
+#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
+#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
+#define KHRONOS_USE_INTPTR_T
+#endif
+#endif
 
 #elif defined(__VMS ) || defined(__sgi)
 
@@ -235,14 +249,21 @@ typedef unsigned short int     khronos_uint16_t;
  * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
  * to be the only LLP64 architecture in current use.
  */
-#ifdef _WIN64
+#ifdef KHRONOS_USE_INTPTR_T
+typedef intptr_t               khronos_intptr_t;
+typedef uintptr_t              khronos_uintptr_t;
+#elif defined(_WIN64)
 typedef signed   long long int khronos_intptr_t;
 typedef unsigned long long int khronos_uintptr_t;
-typedef signed   long long int khronos_ssize_t;
-typedef unsigned long long int khronos_usize_t;
 #else
 typedef signed   long  int     khronos_intptr_t;
 typedef unsigned long  int     khronos_uintptr_t;
+#endif
+
+#if defined(_WIN64)
+typedef signed   long long int khronos_ssize_t;
+typedef unsigned long long int khronos_usize_t;
+#else
 typedef signed   long  int     khronos_ssize_t;
 typedef unsigned long  int     khronos_usize_t;
 #endif

+ 16 - 4
thirdparty/glad/glad.c

@@ -1,6 +1,6 @@
 /*
 
-    OpenGL loader generated by glad 0.1.34 on Tue Nov 17 16:41:02 2020.
+    OpenGL loader generated by glad 0.1.36 on Sun Sep  4 15:50:32 2022.
 
     Language/Generator: C/C++
     Specification: gl
@@ -11,16 +11,18 @@
         GL_ARB_framebuffer_object,
         GL_EXT_framebuffer_blit,
         GL_EXT_framebuffer_multisample,
-        GL_EXT_framebuffer_object
+        GL_EXT_framebuffer_object,
+        GL_OVR_multiview,
+        GL_OVR_multiview2
     Loader: True
     Local files: False
     Omit khrplatform: False
     Reproducible: False
 
     Commandline:
-        --profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object"
+        --profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_OVR_multiview,GL_OVR_multiview2"
     Online:
-        https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object
+        https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object&extensions=GL_OVR_multiview&extensions=GL_OVR_multiview2
 */
 
 #include <stdio.h>
@@ -1000,6 +1002,8 @@ int GLAD_GL_ARB_framebuffer_object = 0;
 int GLAD_GL_EXT_framebuffer_blit = 0;
 int GLAD_GL_EXT_framebuffer_multisample = 0;
 int GLAD_GL_EXT_framebuffer_object = 0;
+int GLAD_GL_OVR_multiview = 0;
+int GLAD_GL_OVR_multiview2 = 0;
 PFNGLDEBUGMESSAGECONTROLARBPROC glad_glDebugMessageControlARB = NULL;
 PFNGLDEBUGMESSAGEINSERTARBPROC glad_glDebugMessageInsertARB = NULL;
 PFNGLDEBUGMESSAGECALLBACKARBPROC glad_glDebugMessageCallbackARB = NULL;
@@ -1023,6 +1027,7 @@ PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glad_glFramebufferTexture3DEXT = NULL;
 PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glad_glFramebufferRenderbufferEXT = NULL;
 PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glad_glGetFramebufferAttachmentParameterivEXT = NULL;
 PFNGLGENERATEMIPMAPEXTPROC glad_glGenerateMipmapEXT = NULL;
+PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glad_glFramebufferTextureMultiviewOVR = NULL;
 static void load_GL_VERSION_1_0(GLADloadproc load) {
 	if(!GLAD_GL_VERSION_1_0) return;
 	glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace");
@@ -1844,6 +1849,10 @@ static void load_GL_EXT_framebuffer_object(GLADloadproc load) {
 	glad_glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)load("glGetFramebufferAttachmentParameterivEXT");
 	glad_glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC)load("glGenerateMipmapEXT");
 }
+static void load_GL_OVR_multiview(GLADloadproc load) {
+	if(!GLAD_GL_OVR_multiview) return;
+	glad_glFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)load("glFramebufferTextureMultiviewOVR");
+}
 static int find_extensionsGL(void) {
 	if (!get_exts()) return 0;
 	GLAD_GL_ARB_debug_output = has_ext("GL_ARB_debug_output");
@@ -1851,6 +1860,8 @@ static int find_extensionsGL(void) {
 	GLAD_GL_EXT_framebuffer_blit = has_ext("GL_EXT_framebuffer_blit");
 	GLAD_GL_EXT_framebuffer_multisample = has_ext("GL_EXT_framebuffer_multisample");
 	GLAD_GL_EXT_framebuffer_object = has_ext("GL_EXT_framebuffer_object");
+	GLAD_GL_OVR_multiview = has_ext("GL_OVR_multiview");
+	GLAD_GL_OVR_multiview2 = has_ext("GL_OVR_multiview2");
 	free_exts();
 	return 1;
 }
@@ -1934,6 +1945,7 @@ int gladLoadGLLoader(GLADloadproc load) {
 	load_GL_EXT_framebuffer_blit(load);
 	load_GL_EXT_framebuffer_multisample(load);
 	load_GL_EXT_framebuffer_object(load);
+	load_GL_OVR_multiview(load);
 	return GLVersion.major != 0 || GLVersion.minor != 0;
 }
 

+ 21 - 4
thirdparty/glad/glad/glad.h

@@ -1,6 +1,6 @@
 /*
 
-    OpenGL loader generated by glad 0.1.34 on Tue Nov 17 16:41:02 2020.
+    OpenGL loader generated by glad 0.1.36 on Sun Sep  4 15:50:32 2022.
 
     Language/Generator: C/C++
     Specification: gl
@@ -11,16 +11,18 @@
         GL_ARB_framebuffer_object,
         GL_EXT_framebuffer_blit,
         GL_EXT_framebuffer_multisample,
-        GL_EXT_framebuffer_object
+        GL_EXT_framebuffer_object,
+        GL_OVR_multiview,
+        GL_OVR_multiview2
     Loader: True
     Local files: False
     Omit khrplatform: False
     Reproducible: False
 
     Commandline:
-        --profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object"
+        --profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_OVR_multiview,GL_OVR_multiview2"
     Online:
-        https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object
+        https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object&extensions=GL_OVR_multiview&extensions=GL_OVR_multiview2
 */
 
 
@@ -3687,6 +3689,10 @@ GLAPI PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv;
 #define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53
 #define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54
 #define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632
+#define GL_MAX_VIEWS_OVR 0x9631
+#define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633
 #ifndef GL_ARB_debug_output
 #define GL_ARB_debug_output 1
 GLAPI int GLAD_GL_ARB_debug_output;
@@ -3776,6 +3782,17 @@ typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC)(GLenum target);
 GLAPI PFNGLGENERATEMIPMAPEXTPROC glad_glGenerateMipmapEXT;
 #define glGenerateMipmapEXT glad_glGenerateMipmapEXT
 #endif
+#ifndef GL_OVR_multiview
+#define GL_OVR_multiview 1
+GLAPI int GLAD_GL_OVR_multiview;
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews);
+GLAPI PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glad_glFramebufferTextureMultiviewOVR;
+#define glFramebufferTextureMultiviewOVR glad_glFramebufferTextureMultiviewOVR
+#endif
+#ifndef GL_OVR_multiview2
+#define GL_OVR_multiview2 1
+GLAPI int GLAD_GL_OVR_multiview2;
+#endif
 
 #ifdef __cplusplus
 }